From 7af6915581f0733d3dd82c283d1cc0f67a939e12 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Thu, 21 Apr 2022 12:11:46 +0200 Subject: [PATCH 1/7] [APM] Progressive fetching (experimental) (#127598) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .eslintrc.js | 5 +- src/core/types/elasticsearch/search.ts | 5 + .../app/service_inventory/index.tsx | 20 ++- .../components/app/trace_overview/index.tsx | 5 +- .../public/hooks/use_progressive_fetcher.tsx | 155 ++++++++++++++++++ .../get_global_apm_server_route_repository.ts | 4 +- .../apm/server/routes/default_api_types.ts | 5 +- .../__snapshots__/queries.test.ts.snap | 114 +++++++------ .../get_service_transaction_stats.ts | 41 +++-- ...ervices_from_error_and_metric_documents.ts | 33 ++-- .../get_services/get_services_items.ts | 3 + .../routes/services/get_services/index.ts | 3 + ...service_transaction_detailed_statistics.ts | 53 +++--- .../get_services_detailed_statistics/index.ts | 3 + .../server/routes/services/queries.test.ts | 1 + .../apm/server/routes/services/route.ts | 34 +++- .../traces/get_top_traces_primary_stats.ts | 87 +++++----- .../plugins/apm/server/routes/traces/route.ts | 12 +- x-pack/plugins/observability/common/index.ts | 6 + .../common/progressive_loading.ts | 31 ++++ .../observability/common/ui_settings_keys.ts | 1 + .../observability/server/ui_settings.ts | 55 ++++++- .../tests/error_rate/service_apis.spec.ts | 1 + .../tests/error_rate/service_maps.spec.ts | 1 + .../tests/feature_controls.spec.ts | 4 +- .../tests/latency/service_apis.spec.ts | 1 + .../tests/latency/service_maps.spec.ts | 1 + .../observability_overview.spec.ts | 1 + .../services_detailed_statistics.spec.ts | 95 ++++++----- .../tests/services/top_services.spec.ts | 82 ++++++--- .../tests/throughput/service_apis.spec.ts | 1 + .../tests/throughput/service_maps.spec.ts | 1 + .../tests/traces/top_traces.spec.ts | 35 +++- 33 files changed, 662 insertions(+), 237 deletions(-) create mode 100644 x-pack/plugins/apm/public/hooks/use_progressive_fetcher.tsx create mode 100644 x-pack/plugins/observability/common/progressive_loading.ts diff --git a/.eslintrc.js b/.eslintrc.js index 3c1c455fc3295..dfbdd4de96f0a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -840,7 +840,10 @@ module.exports = { }, ], 'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks - 'react-hooks/exhaustive-deps': ['error', { additionalHooks: '^useFetcher$' }], + 'react-hooks/exhaustive-deps': [ + 'error', + { additionalHooks: '^(useFetcher|useProgressiveFetcher)$' }, + ], }, }, { diff --git a/src/core/types/elasticsearch/search.ts b/src/core/types/elasticsearch/search.ts index 6f2c0c28e670b..96d1bec3c5f1e 100644 --- a/src/core/types/elasticsearch/search.ts +++ b/src/core/types/elasticsearch/search.ts @@ -455,6 +455,11 @@ export type AggregateOf< reverse_nested: { doc_count: number; } & SubAggregateOf; + random_sampler: { + seed: number; + probability: number; + doc_count: number; + } & SubAggregateOf; sampler: { doc_count: number; } & SubAggregateOf; diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index 5d6dfef10d9dc..5baabca805b7c 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -19,6 +19,7 @@ import { useTimeRange } from '../../../hooks/use_time_range'; import { SearchBar } from '../../shared/search_bar'; import { ServiceList } from './service_list'; import { MLCallout, shouldDisplayMlCallout } from '../../shared/ml_callout'; +import { useProgressiveFetcher } from '../../../hooks/use_progressive_fetcher'; import { joinByKey } from '../../../../common/utils/join_by_key'; import { ServiceInventoryFieldName } from '../../../../common/service_inventory'; import { orderServiceItems } from './service_list/order_service_items'; @@ -62,7 +63,7 @@ function useServicesFetcher() { [start, end, environment, kuery, serviceGroup] ); - const mainStatisticsFetch = useFetcher( + const mainStatisticsFetch = useProgressiveFetcher( (callApmApi) => { if (start && end) { return callApmApi('GET /internal/apm/services', { @@ -88,9 +89,14 @@ function useServicesFetcher() { const { data: mainStatisticsData = initialData } = mainStatisticsFetch; - const comparisonFetch = useFetcher( + const comparisonFetch = useProgressiveFetcher( (callApmApi) => { - if (start && end && mainStatisticsData.items.length) { + if ( + start && + end && + mainStatisticsData.items.length && + mainStatisticsFetch.status === FETCH_STATUS.SUCCESS + ) { return callApmApi('GET /internal/apm/services/detailed_statistics', { params: { query: { @@ -141,14 +147,16 @@ export function ServiceInventory() { !userHasDismissedCallout && shouldDisplayMlCallout(anomalyDetectionSetupState); - const useOptimizedSorting = useKibana().services.uiSettings?.get( - apmServiceInventoryOptimizedSorting - ); + const useOptimizedSorting = + useKibana().services.uiSettings?.get( + apmServiceInventoryOptimizedSorting + ) || false; let isLoading: boolean; if (useOptimizedSorting) { isLoading = + // ensures table is usable when sorted and filtered services have loaded sortedAndFilteredServicesFetch.status === FETCH_STATUS.LOADING || (sortedAndFilteredServicesFetch.status === FETCH_STATUS.SUCCESS && sortedAndFilteredServicesFetch.data?.services.length === 0 && diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx index 141aaee1b826e..21ae0f9820890 100644 --- a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx @@ -8,13 +8,14 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import { useApmParams } from '../../../hooks/use_apm_params'; -import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { APIReturnType } from '../../../services/rest/create_call_apm_api'; import { SearchBar } from '../../shared/search_bar'; import { TraceList } from './trace_list'; import { useFallbackToTransactionsFetcher } from '../../../hooks/use_fallback_to_transactions_fetcher'; import { AggregatedTransactionsBadge } from '../../shared/aggregated_transactions_badge'; import { useTimeRange } from '../../../hooks/use_time_range'; +import { useProgressiveFetcher } from '../../../hooks/use_progressive_fetcher'; type TracesAPIResponse = APIReturnType<'GET /internal/apm/traces'>; const DEFAULT_RESPONSE: TracesAPIResponse = { @@ -31,7 +32,7 @@ export function TraceOverview() { const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const { status, data = DEFAULT_RESPONSE } = useFetcher( + const { status, data = DEFAULT_RESPONSE } = useProgressiveFetcher( (callApmApi) => { if (start && end) { return callApmApi('GET /internal/apm/traces', { diff --git a/x-pack/plugins/apm/public/hooks/use_progressive_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_progressive_fetcher.tsx new file mode 100644 index 0000000000000..a3268fe8d2958 --- /dev/null +++ b/x-pack/plugins/apm/public/hooks/use_progressive_fetcher.tsx @@ -0,0 +1,155 @@ +/* + * 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 { OmitByValue, Assign } from 'utility-types'; +import type { + ClientRequestParamsOf, + EndpointOf, + ReturnOf, +} from '@kbn/server-route-repository'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { + apmProgressiveLoading, + getProbabilityFromProgressiveLoadingQuality, + ProgressiveLoadingQuality, +} from '@kbn/observability-plugin/common'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import type { APMServerRouteRepository } from '../../server'; + +import type { + APMClient, + APMClientOptions, +} from '../services/rest/create_call_apm_api'; +import { FetcherResult, FETCH_STATUS, useFetcher } from './use_fetcher'; + +type APMProgressivelyLoadingServerRouteRepository = OmitByValue< + { + [key in keyof APMServerRouteRepository]: ClientRequestParamsOf< + APMServerRouteRepository, + key + > extends { + params: { query: { probability: any } }; + } + ? APMServerRouteRepository[key] + : undefined; + }, + undefined +>; + +type WithoutProbabilityParameter> = { + params: { query: {} }; +} & Assign< + T, + { + params: Omit & { + query: Omit; + }; + } +>; + +type APMProgressiveAPIClient = < + TEndpoint extends EndpointOf +>( + endpoint: TEndpoint, + options: Omit & + WithoutProbabilityParameter< + ClientRequestParamsOf< + APMProgressivelyLoadingServerRouteRepository, + TEndpoint + > + > +) => Promise>; + +function clientWithProbability( + regularCallApmApi: APMClient, + probability: number +) { + return < + TEndpoint extends EndpointOf + >( + endpoint: TEndpoint, + options: Omit & + WithoutProbabilityParameter< + ClientRequestParamsOf< + APMProgressivelyLoadingServerRouteRepository, + TEndpoint + > + > + ) => { + return regularCallApmApi(endpoint, { + ...options, + params: { + ...options.params, + query: { + ...options.params.query, + probability, + }, + }, + } as any); + }; +} + +export function useProgressiveFetcher( + callback: ( + callApmApi: APMProgressiveAPIClient + ) => Promise | undefined, + dependencies: any[], + options?: Parameters[2] +): FetcherResult { + const { + services: { uiSettings }, + } = useKibana(); + + const progressiveLoadingQuality = + uiSettings?.get(apmProgressiveLoading) ?? + ProgressiveLoadingQuality.off; + + const sampledProbability = getProbabilityFromProgressiveLoadingQuality( + progressiveLoadingQuality + ); + + const sampledFetch = useFetcher( + (regularCallApmApi) => { + if (progressiveLoadingQuality === ProgressiveLoadingQuality.off) { + return; + } + return callback( + clientWithProbability(regularCallApmApi, sampledProbability) + ); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + dependencies, + options + ); + + const unsampledFetch = useFetcher( + (regularCallApmApi) => { + return callback(clientWithProbability(regularCallApmApi, 1)); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + dependencies + ); + + const fetches = [unsampledFetch, sampledFetch]; + + const isError = unsampledFetch.status === FETCH_STATUS.FAILURE; + + const usedFetch = + (!isError && + fetches.find((fetch) => fetch.status === FETCH_STATUS.SUCCESS)) || + unsampledFetch; + + const status = + unsampledFetch.status === FETCH_STATUS.LOADING && + usedFetch.status === FETCH_STATUS.SUCCESS + ? FETCH_STATUS.LOADING + : usedFetch.status; + + return { + ...usedFetch, + status, + }; +} diff --git a/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts b/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts index b0869b7abcbce..5e6ac627364d8 100644 --- a/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts +++ b/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts @@ -38,7 +38,7 @@ import { eventMetadataRouteRepository } from '../event_metadata/route'; import { suggestionsRouteRepository } from '../suggestions/route'; import { agentKeysRouteRepository } from '../agent_keys/route'; -const getTypedGlobalApmServerRouteRepository = () => { +function getTypedGlobalApmServerRouteRepository() { const repository = { ...dataViewRouteRepository, ...environmentsRouteRepository, @@ -70,7 +70,7 @@ const getTypedGlobalApmServerRouteRepository = () => { }; return repository; -}; +} const getGlobalApmServerRouteRepository = (): ServerRouteRepository => { return getTypedGlobalApmServerRouteRepository(); diff --git a/x-pack/plugins/apm/server/routes/default_api_types.ts b/x-pack/plugins/apm/server/routes/default_api_types.ts index e2b969f00edc5..78668e0bf2472 100644 --- a/x-pack/plugins/apm/server/routes/default_api_types.ts +++ b/x-pack/plugins/apm/server/routes/default_api_types.ts @@ -6,7 +6,7 @@ */ import * as t from 'io-ts'; -import { isoToEpochRt } from '@kbn/io-ts-utils'; +import { isoToEpochRt, toNumberRt } from '@kbn/io-ts-utils'; export { environmentRt } from '../../common/environment_rt'; @@ -15,4 +15,7 @@ export const rangeRt = t.type({ end: isoToEpochRt, }); +export const probabilityRt = t.type({ + probability: toNumberRt, +}); export const kueryRt = t.type({ kuery: t.string }); diff --git a/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap index 4014f2a4a2acc..e09c50708c476 100644 --- a/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap @@ -81,50 +81,57 @@ Array [ }, "body": Object { "aggs": Object { - "services": Object { + "sample": Object { "aggs": Object { - "transactionType": Object { + "services": Object { "aggs": Object { - "avg_duration": Object { - "avg": Object { - "field": "transaction.duration.us", - }, - }, - "environments": Object { - "terms": Object { - "field": "service.environment", - }, - }, - "outcomes": Object { - "terms": Object { - "field": "event.outcome", - "include": Array [ - "failure", - "success", - ], - }, - }, - "sample": Object { - "top_metrics": Object { - "metrics": Array [ - Object { - "field": "agent.name", + "transactionType": Object { + "aggs": Object { + "avg_duration": Object { + "avg": Object { + "field": "transaction.duration.us", + }, + }, + "environments": Object { + "terms": Object { + "field": "service.environment", + }, + }, + "outcomes": Object { + "terms": Object { + "field": "event.outcome", + "include": Array [ + "failure", + "success", + ], + }, + }, + "sample": Object { + "top_metrics": Object { + "metrics": Array [ + Object { + "field": "agent.name", + }, + ], + "sort": Object { + "@timestamp": "desc", + }, }, - ], - "sort": Object { - "@timestamp": "desc", }, }, + "terms": Object { + "field": "transaction.type", + }, }, }, "terms": Object { - "field": "transaction.type", + "field": "service.name", + "size": 50, }, }, }, - "terms": Object { - "field": "service.name", - "size": 50, + "random_sampler": Object { + "probability": 1, }, }, }, @@ -155,29 +162,36 @@ Array [ }, "body": Object { "aggs": Object { - "services": Object { + "sample": Object { "aggs": Object { - "environments": Object { - "terms": Object { - "field": "service.environment", - }, - }, - "latest": Object { - "top_metrics": Object { - "metrics": Array [ - Object { - "field": "agent.name", + "services": Object { + "aggs": Object { + "environments": Object { + "terms": Object { + "field": "service.environment", }, - ], - "sort": Object { - "@timestamp": "desc", }, + "latest": Object { + "top_metrics": Object { + "metrics": Array [ + Object { + "field": "agent.name", + }, + ], + "sort": Object { + "@timestamp": "desc", + }, + }, + }, + }, + "terms": Object { + "field": "service.name", + "size": 50, }, }, }, - "terms": Object { - "field": "service.name", - "size": 50, + "random_sampler": Object { + "probability": 1, }, }, }, diff --git a/x-pack/plugins/apm/server/routes/services/get_services/get_service_transaction_stats.ts b/x-pack/plugins/apm/server/routes/services/get_services/get_service_transaction_stats.ts index 92014c05726c4..75322ebcd0551 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services/get_service_transaction_stats.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services/get_service_transaction_stats.ts @@ -35,6 +35,7 @@ import { ServiceGroup } from '../../../../common/service_groups'; interface AggregationParams { environment: string; kuery: string; + probability: number; setup: ServicesItemsSetup; searchAggregatedTransactions: boolean; maxNumServices: number; @@ -46,6 +47,7 @@ interface AggregationParams { export async function getServiceTransactionStats({ environment, kuery, + probability, setup, searchAggregatedTransactions, maxNumServices, @@ -90,28 +92,35 @@ export async function getServiceTransactionStats({ }, }, aggs: { - services: { - terms: { - field: SERVICE_NAME, - size: maxNumServices, + sample: { + random_sampler: { + probability, }, aggs: { - transactionType: { + services: { terms: { - field: TRANSACTION_TYPE, + field: SERVICE_NAME, + size: maxNumServices, }, aggs: { - ...metrics, - environments: { + transactionType: { terms: { - field: SERVICE_ENVIRONMENT, + field: TRANSACTION_TYPE, }, - }, - sample: { - top_metrics: { - metrics: [{ field: AGENT_NAME } as const], - sort: { - '@timestamp': 'desc' as const, + aggs: { + ...metrics, + environments: { + terms: { + field: SERVICE_ENVIRONMENT, + }, + }, + sample: { + top_metrics: { + metrics: [{ field: AGENT_NAME } as const], + sort: { + '@timestamp': 'desc' as const, + }, + }, }, }, }, @@ -125,7 +134,7 @@ export async function getServiceTransactionStats({ ); return ( - response.aggregations?.services.buckets.map((bucket) => { + response.aggregations?.sample.services.buckets.map((bucket) => { const topTransactionTypeBucket = bucket.transactionType.buckets.find( ({ key }) => diff --git a/x-pack/plugins/apm/server/routes/services/get_services/get_services_from_error_and_metric_documents.ts b/x-pack/plugins/apm/server/routes/services/get_services/get_services_from_error_and_metric_documents.ts index 63378095d72e2..45049db6ef8f1 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services/get_services_from_error_and_metric_documents.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services/get_services_from_error_and_metric_documents.ts @@ -21,6 +21,7 @@ import { ServiceGroup } from '../../../../common/service_groups'; export async function getServicesFromErrorAndMetricDocuments({ environment, setup, + probability, maxNumServices, kuery, start, @@ -29,6 +30,7 @@ export async function getServicesFromErrorAndMetricDocuments({ }: { setup: Setup; environment: string; + probability: number; maxNumServices: number; kuery: string; start: number; @@ -56,21 +58,28 @@ export async function getServicesFromErrorAndMetricDocuments({ }, }, aggs: { - services: { - terms: { - field: SERVICE_NAME, - size: maxNumServices, + sample: { + random_sampler: { + probability, }, aggs: { - environments: { + services: { terms: { - field: SERVICE_ENVIRONMENT, + field: SERVICE_NAME, + size: maxNumServices, }, - }, - latest: { - top_metrics: { - metrics: [{ field: AGENT_NAME } as const], - sort: { '@timestamp': 'desc' }, + aggs: { + environments: { + terms: { + field: SERVICE_ENVIRONMENT, + }, + }, + latest: { + top_metrics: { + metrics: [{ field: AGENT_NAME } as const], + sort: { '@timestamp': 'desc' }, + }, + }, }, }, }, @@ -81,7 +90,7 @@ export async function getServicesFromErrorAndMetricDocuments({ ); return ( - response.aggregations?.services.buckets.map((bucket) => { + response.aggregations?.sample.services.buckets.map((bucket) => { return { serviceName: bucket.key as string, environments: bucket.environments.buckets.map( diff --git a/x-pack/plugins/apm/server/routes/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/routes/services/get_services/get_services_items.ts index 1235af756b76e..a5936dd68d026 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services/get_services_items.ts @@ -21,6 +21,7 @@ const MAX_NUMBER_OF_SERVICES = 50; export async function getServicesItems({ environment, kuery, + probability, setup, searchAggregatedTransactions, logger, @@ -30,6 +31,7 @@ export async function getServicesItems({ }: { environment: string; kuery: string; + probability: number; setup: ServicesItemsSetup; searchAggregatedTransactions: boolean; logger: Logger; @@ -41,6 +43,7 @@ export async function getServicesItems({ const params = { environment, kuery, + probability, setup, searchAggregatedTransactions, maxNumServices: MAX_NUMBER_OF_SERVICES, diff --git a/x-pack/plugins/apm/server/routes/services/get_services/index.ts b/x-pack/plugins/apm/server/routes/services/get_services/index.ts index 223c0b3f613ef..fb133e4f5ad1a 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services/index.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services/index.ts @@ -14,6 +14,7 @@ import { ServiceGroup } from '../../../../common/service_groups'; export async function getServices({ environment, kuery, + probability, setup, searchAggregatedTransactions, logger, @@ -23,6 +24,7 @@ export async function getServices({ }: { environment: string; kuery: string; + probability: number; setup: Setup; searchAggregatedTransactions: boolean; logger: Logger; @@ -34,6 +36,7 @@ export async function getServices({ const items = await getServicesItems({ environment, kuery, + probability, setup, searchAggregatedTransactions, logger, diff --git a/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts b/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts index 0e3310efd400c..5f1b4faad8dc3 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts @@ -39,6 +39,7 @@ export async function getServiceTransactionDetailedStatistics({ offset, start, end, + probability, }: { serviceNames: string[]; environment: string; @@ -48,6 +49,7 @@ export async function getServiceTransactionDetailedStatistics({ offset?: string; start: number; end: number; + probability: number; }) { const { apmEventClient } = setup; const { offsetInMs, startWithOffset, endWithOffset } = getOffsetInMs({ @@ -91,33 +93,42 @@ export async function getServiceTransactionDetailedStatistics({ }, }, aggs: { - services: { - terms: { - field: SERVICE_NAME, + sample: { + random_sampler: { + probability, }, aggs: { - transactionType: { + services: { terms: { - field: TRANSACTION_TYPE, + field: SERVICE_NAME, + size: serviceNames.length, }, aggs: { - ...metrics, - timeseries: { - date_histogram: { - field: '@timestamp', - fixed_interval: getBucketSizeForAggregatedTransactions({ - start: startWithOffset, - end: endWithOffset, - numBuckets: 20, - searchAggregatedTransactions, - }).intervalString, - min_doc_count: 0, - extended_bounds: { - min: startWithOffset, - max: endWithOffset, + transactionType: { + terms: { + field: TRANSACTION_TYPE, + }, + aggs: { + ...metrics, + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: + getBucketSizeForAggregatedTransactions({ + start: startWithOffset, + end: endWithOffset, + numBuckets: 20, + searchAggregatedTransactions, + }).intervalString, + min_doc_count: 0, + extended_bounds: { + min: startWithOffset, + max: endWithOffset, + }, + }, + aggs: metrics, }, }, - aggs: metrics, }, }, }, @@ -129,7 +140,7 @@ export async function getServiceTransactionDetailedStatistics({ ); return keyBy( - response.aggregations?.services.buckets.map((bucket) => { + response.aggregations?.sample.services.buckets.map((bucket) => { const topTransactionTypeBucket = bucket.transactionType.buckets.find( ({ key }) => diff --git a/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/index.ts b/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/index.ts index d0fa24913a214..3009f9214ac31 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/index.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/index.ts @@ -18,6 +18,7 @@ export async function getServicesDetailedStatistics({ offset, start, end, + probability, }: { serviceNames: string[]; environment: string; @@ -27,6 +28,7 @@ export async function getServicesDetailedStatistics({ offset?: string; start: number; end: number; + probability: number; }) { return withApmSpan('get_service_detailed_statistics', async () => { const commonProps = { @@ -37,6 +39,7 @@ export async function getServicesDetailedStatistics({ searchAggregatedTransactions, start, end, + probability, }; const [currentPeriod, previousPeriod] = await Promise.all([ diff --git a/x-pack/plugins/apm/server/routes/services/queries.test.ts b/x-pack/plugins/apm/server/routes/services/queries.test.ts index a8a65d0388297..8faa7b9857bf7 100644 --- a/x-pack/plugins/apm/server/routes/services/queries.test.ts +++ b/x-pack/plugins/apm/server/routes/services/queries.test.ts @@ -60,6 +60,7 @@ describe('services queries', () => { start: 0, end: 50000, serviceGroup: null, + probability: 1, }) ); diff --git a/x-pack/plugins/apm/server/routes/services/route.ts b/x-pack/plugins/apm/server/routes/services/route.ts index e8de9ba8948d9..0bcdca6e3cb89 100644 --- a/x-pack/plugins/apm/server/routes/services/route.ts +++ b/x-pack/plugins/apm/server/routes/services/route.ts @@ -36,7 +36,12 @@ import { getServiceProfilingTimeline } from './profiling/get_service_profiling_t import { getServiceInfrastructure } from './get_service_infrastructure'; import { withApmSpan } from '../../utils/with_apm_span'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; -import { environmentRt, kueryRt, rangeRt } from '../default_api_types'; +import { + environmentRt, + kueryRt, + rangeRt, + probabilityRt, +} from '../default_api_types'; import { offsetPreviousPeriodCoordinates } from '../../../common/utils/offset_previous_period_coordinate'; import { getServicesDetailedStatistics } from './get_services_detailed_statistics'; import { getServiceDependenciesBreakdown } from './get_service_dependencies_breakdown'; @@ -57,6 +62,7 @@ const servicesRoute = createApmServerRoute({ kueryRt, rangeRt, t.partial({ serviceGroup: t.string }), + probabilityRt, ]), }), options: { tags: ['access:apm'] }, @@ -105,6 +111,7 @@ const servicesRoute = createApmServerRoute({ start, end, serviceGroup: serviceGroupId, + probability, } = params.query; const savedObjectsClient = context.core.savedObjects.client; @@ -123,6 +130,7 @@ const servicesRoute = createApmServerRoute({ return getServices({ environment, kuery, + probability, setup, searchAggregatedTransactions, logger, @@ -137,10 +145,14 @@ const servicesDetailedStatisticsRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/services/detailed_statistics', params: t.type({ query: t.intersection([ - environmentRt, - kueryRt, - rangeRt, - offsetRt, + // t.intersection seemingly only supports 5 arguments so let's wrap them in another intersection + t.intersection([ + environmentRt, + kueryRt, + rangeRt, + offsetRt, + probabilityRt, + ]), t.type({ serviceNames: jsonRt.pipe(t.array(t.string)) }), ]), }), @@ -181,8 +193,15 @@ const servicesDetailedStatisticsRoute = createApmServerRoute({ }> => { const setup = await setupRequest(resources); const { params } = resources; - const { environment, kuery, offset, serviceNames, start, end } = - params.query; + const { + environment, + kuery, + offset, + serviceNames, + start, + end, + probability, + } = params.query; const searchAggregatedTransactions = await getSearchAggregatedTransactions({ ...setup, start, @@ -203,6 +222,7 @@ const servicesDetailedStatisticsRoute = createApmServerRoute({ serviceNames, start, end, + probability, }); }, }); diff --git a/x-pack/plugins/apm/server/routes/traces/get_top_traces_primary_stats.ts b/x-pack/plugins/apm/server/routes/traces/get_top_traces_primary_stats.ts index 7cc415a4673c8..f6903eba7476c 100644 --- a/x-pack/plugins/apm/server/routes/traces/get_top_traces_primary_stats.ts +++ b/x-pack/plugins/apm/server/routes/traces/get_top_traces_primary_stats.ts @@ -41,6 +41,7 @@ export type BucketKey = Record< interface TopTracesParams { environment: string; kuery: string; + probability: number; transactionName?: string; searchAggregatedTransactions: boolean; start: number; @@ -50,6 +51,7 @@ interface TopTracesParams { export function getTopTracesPrimaryStats({ environment, kuery, + probability, transactionName, searchAggregatedTransactions, start, @@ -101,47 +103,52 @@ export function getTopTracesPrimaryStats({ }, }, aggs: { - transaction_groups: { - composite: { - sources: asMutableArray([ - { [SERVICE_NAME]: { terms: { field: SERVICE_NAME } } }, - { - [TRANSACTION_NAME]: { - terms: { field: TRANSACTION_NAME }, - }, - }, - ] as const), - // traces overview is hardcoded to 10000 - size: 10000, - }, + sample: { + random_sampler: { probability }, aggs: { - transaction_type: { - top_metrics: { - sort: { - '@timestamp': 'desc' as const, - }, - metrics: [ - { - field: TRANSACTION_TYPE, - } as const, + transaction_groups: { + composite: { + sources: asMutableArray([ + { [SERVICE_NAME]: { terms: { field: SERVICE_NAME } } }, { - field: AGENT_NAME, - } as const, - ], - }, - }, - avg: { - avg: { - field: getDurationFieldForTransactions( - searchAggregatedTransactions - ), + [TRANSACTION_NAME]: { + terms: { field: TRANSACTION_NAME }, + }, + }, + ] as const), + // traces overview is hardcoded to 10000 + size: 10000, }, - }, - sum: { - sum: { - field: getDurationFieldForTransactions( - searchAggregatedTransactions - ), + aggs: { + transaction_type: { + top_metrics: { + sort: { + '@timestamp': 'desc' as const, + }, + metrics: [ + { + field: TRANSACTION_TYPE, + } as const, + { + field: AGENT_NAME, + } as const, + ], + }, + }, + avg: { + avg: { + field: getDurationFieldForTransactions( + searchAggregatedTransactions + ), + }, + }, + sum: { + sum: { + field: getDurationFieldForTransactions( + searchAggregatedTransactions + ), + }, + }, }, }, }, @@ -152,12 +159,12 @@ export function getTopTracesPrimaryStats({ ); const calculateImpact = calculateImpactBuilder( - response.aggregations?.transaction_groups.buckets.map( + response.aggregations?.sample.transaction_groups.buckets.map( ({ sum }) => sum.value ) ); - const items = response.aggregations?.transaction_groups.buckets.map( + const items = response.aggregations?.sample.transaction_groups.buckets.map( (bucket) => { return { key: bucket.key as BucketKey, diff --git a/x-pack/plugins/apm/server/routes/traces/route.ts b/x-pack/plugins/apm/server/routes/traces/route.ts index 05aa27f27196d..c767a4e67aa63 100644 --- a/x-pack/plugins/apm/server/routes/traces/route.ts +++ b/x-pack/plugins/apm/server/routes/traces/route.ts @@ -10,7 +10,12 @@ import { setupRequest } from '../../lib/helpers/setup_request'; import { getTraceItems } from './get_trace_items'; import { getTopTracesPrimaryStats } from './get_top_traces_primary_stats'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; -import { environmentRt, kueryRt, rangeRt } from '../default_api_types'; +import { + environmentRt, + kueryRt, + probabilityRt, + rangeRt, +} from '../default_api_types'; import { getSearchAggregatedTransactions } from '../../lib/helpers/transactions'; import { getRootTransactionByTraceId } from '../transactions/get_transaction_by_trace'; import { getTransaction } from '../transactions/get_transaction'; @@ -18,7 +23,7 @@ import { getTransaction } from '../transactions/get_transaction'; const tracesRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/traces', params: t.type({ - query: t.intersection([environmentRt, kueryRt, rangeRt]), + query: t.intersection([environmentRt, kueryRt, rangeRt, probabilityRt]), }), options: { tags: ['access:apm'] }, handler: async ( @@ -37,7 +42,7 @@ const tracesRoute = createApmServerRoute({ }> => { const setup = await setupRequest(resources); const { params } = resources; - const { environment, kuery, start, end } = params.query; + const { environment, kuery, start, end, probability } = params.query; const searchAggregatedTransactions = await getSearchAggregatedTransactions({ ...setup, kuery, @@ -48,6 +53,7 @@ const tracesRoute = createApmServerRoute({ return await getTopTracesPrimaryStats({ environment, kuery, + probability, setup, searchAggregatedTransactions, start, diff --git a/x-pack/plugins/observability/common/index.ts b/x-pack/plugins/observability/common/index.ts index 8a2ee7c0f1718..e01b9ba3f9922 100644 --- a/x-pack/plugins/observability/common/index.ts +++ b/x-pack/plugins/observability/common/index.ts @@ -16,8 +16,14 @@ export { enableInfrastructureView, defaultApmServiceEnvironment, apmServiceInventoryOptimizedSorting, + apmProgressiveLoading, } from './ui_settings_keys'; +export { + ProgressiveLoadingQuality, + getProbabilityFromProgressiveLoadingQuality, +} from './progressive_loading'; + export const casesFeatureId = 'observabilityCases'; // The ID of the observability app. Should more appropriately be called diff --git a/x-pack/plugins/observability/common/progressive_loading.ts b/x-pack/plugins/observability/common/progressive_loading.ts new file mode 100644 index 0000000000000..04063c8a9cf79 --- /dev/null +++ b/x-pack/plugins/observability/common/progressive_loading.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const enum ProgressiveLoadingQuality { + low = 'low', + medium = 'medium', + high = 'high', + off = 'off', +} + +export function getProbabilityFromProgressiveLoadingQuality( + quality: ProgressiveLoadingQuality +): number { + switch (quality) { + case ProgressiveLoadingQuality.high: + return 0.1; + + case ProgressiveLoadingQuality.medium: + return 0.01; + + case ProgressiveLoadingQuality.low: + return 0.001; + + case ProgressiveLoadingQuality.off: + return 1; + } +} diff --git a/x-pack/plugins/observability/common/ui_settings_keys.ts b/x-pack/plugins/observability/common/ui_settings_keys.ts index 54eaa9046d874..4c1b1dc729fea 100644 --- a/x-pack/plugins/observability/common/ui_settings_keys.ts +++ b/x-pack/plugins/observability/common/ui_settings_keys.ts @@ -10,6 +10,7 @@ export const maxSuggestions = 'observability:maxSuggestions'; export const enableComparisonByDefault = 'observability:enableComparisonByDefault'; export const enableInfrastructureView = 'observability:enableInfrastructureView'; export const defaultApmServiceEnvironment = 'observability:apmDefaultServiceEnvironment'; +export const apmProgressiveLoading = 'observability:apmProgressiveLoading'; export const enableServiceGroups = 'observability:enableServiceGroups'; export const apmServiceInventoryOptimizedSorting = 'observability:apmServiceInventoryOptimizedSorting'; diff --git a/x-pack/plugins/observability/server/ui_settings.ts b/x-pack/plugins/observability/server/ui_settings.ts index 1fec1883cd6f4..02c519b10d19c 100644 --- a/x-pack/plugins/observability/server/ui_settings.ts +++ b/x-pack/plugins/observability/server/ui_settings.ts @@ -8,13 +8,14 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { UiSettingsParams } from '@kbn/core/types'; -import { observabilityFeatureId } from '../common'; +import { observabilityFeatureId, ProgressiveLoadingQuality } from '../common'; import { enableComparisonByDefault, enableInspectEsQueries, maxSuggestions, enableInfrastructureView, defaultApmServiceEnvironment, + apmProgressiveLoading, enableServiceGroups, apmServiceInventoryOptimizedSorting, } from '../common/ui_settings_keys'; @@ -86,6 +87,58 @@ export const uiSettings: Record[${technicalPreviewLabel}]` }, + }), + value: ProgressiveLoadingQuality.off, + schema: schema.oneOf([ + schema.literal(ProgressiveLoadingQuality.off), + schema.literal(ProgressiveLoadingQuality.low), + schema.literal(ProgressiveLoadingQuality.medium), + schema.literal(ProgressiveLoadingQuality.high), + ]), + requiresPageReload: false, + type: 'select', + options: [ + ProgressiveLoadingQuality.off, + ProgressiveLoadingQuality.low, + ProgressiveLoadingQuality.medium, + ProgressiveLoadingQuality.high, + ], + optionLabels: { + [ProgressiveLoadingQuality.off]: i18n.translate( + 'xpack.observability.apmProgressiveLoadingQualityOff', + { + defaultMessage: 'Off', + } + ), + [ProgressiveLoadingQuality.low]: i18n.translate( + 'xpack.observability.apmProgressiveLoadingQualityLow', + { + defaultMessage: 'Low sampling rate (fastest, least accurate)', + } + ), + [ProgressiveLoadingQuality.medium]: i18n.translate( + 'xpack.observability.apmProgressiveLoadingQualityMedium', + { + defaultMessage: 'Medium sampling rate', + } + ), + [ProgressiveLoadingQuality.high]: i18n.translate( + 'xpack.observability.apmProgressiveLoadingQualityHigh', + { + defaultMessage: 'High sampling rate (slower, most accurate)', + } + ), + }, + }, [enableServiceGroups]: { category: [observabilityFeatureId], name: i18n.translate('xpack.observability.enableServiceGroups', { diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts index 205b5838e307d..77853b78685ec 100644 --- a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts @@ -41,6 +41,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { params: { query: { ...commonQuery, + probability: 1, kuery: `service.name : "${serviceName}" and processor.event : "${processorEvent}"`, }, }, diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts b/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts index 7f7aa7a85d407..011165c874421 100644 --- a/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts +++ b/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts @@ -31,6 +31,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { ...commonQuery, kuery: `service.name : "${serviceName}" and processor.event : "${processorEvent}"`, + probability: 1, }, }, }), diff --git a/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts b/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts index 77b8faf781eb9..21d2ad617f112 100644 --- a/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts +++ b/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts @@ -79,7 +79,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }, { req: { - url: `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=&probability=1`, }, expectForbidden: expect403, expectResponse: expect200, @@ -98,7 +98,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }, { req: { - url: `/internal/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=&probability=1`, }, expectForbidden: expect403, expectResponse: expect200, diff --git a/x-pack/test/apm_api_integration/tests/latency/service_apis.spec.ts b/x-pack/test/apm_api_integration/tests/latency/service_apis.spec.ts index 3ba46802d6ee7..893a9f81d2526 100644 --- a/x-pack/test/apm_api_integration/tests/latency/service_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/latency/service_apis.spec.ts @@ -44,6 +44,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { ...commonQuery, kuery: `service.name : "${serviceName}" and processor.event : "${processorEvent}"`, + probability: 1, }, }, }), diff --git a/x-pack/test/apm_api_integration/tests/latency/service_maps.spec.ts b/x-pack/test/apm_api_integration/tests/latency/service_maps.spec.ts index dc150d598b7a7..2338e9ea128b2 100644 --- a/x-pack/test/apm_api_integration/tests/latency/service_maps.spec.ts +++ b/x-pack/test/apm_api_integration/tests/latency/service_maps.spec.ts @@ -31,6 +31,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { ...commonQuery, kuery: `service.name : "${serviceName}" and processor.event : "${processorEvent}"`, + probability: 1, }, }, }), diff --git a/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.spec.ts b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.spec.ts index 90c08433b6ab5..cb8fecb1cdf59 100644 --- a/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.spec.ts +++ b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.spec.ts @@ -29,6 +29,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { params: { query: { ...commonQuery, + probability: 1, environment: 'ENVIRONMENT_ALL', kuery: '', }, diff --git a/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.spec.ts b/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.spec.ts index feb9aa8b8e5c4..25672acc76227 100644 --- a/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.spec.ts @@ -5,19 +5,20 @@ * 2.0. */ import expect from '@kbn/expect'; -import url from 'url'; import moment from 'moment'; import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; import { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { ApmApiError } from '../../common/apm_api_supertest'; type ServicesDetailedStatisticsReturn = APIReturnType<'GET /internal/apm/services/detailed_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); - const supertest = getService('legacySupertestAsApmReadUser'); + + const apmApiClient = getService('apmApiClient'); const archiveName = 'apm_8.0.0'; const metadata = archives_metadata[archiveName]; @@ -29,9 +30,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { - const response = await supertest.get( - url.format({ - pathname: `/internal/apm/services/detailed_statistics`, + const response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/detailed_statistics`, + params: { query: { start, end, @@ -39,9 +40,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { environment: 'ENVIRONMENT_ALL', kuery: '', offset: '1d', + probability: 1, }, - }) - ); + }, + }); + expect(response.status).to.be(200); expect(response.body.currentPeriod).to.be.empty(); expect(response.body.previousPeriod).to.be.empty(); @@ -55,18 +58,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { let servicesDetailedStatistics: ServicesDetailedStatisticsReturn; before(async () => { - const response = await supertest.get( - url.format({ - pathname: `/internal/apm/services/detailed_statistics`, + const response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/detailed_statistics`, + params: { query: { start, end, serviceNames: JSON.stringify(serviceNames), environment: 'ENVIRONMENT_ALL', kuery: '', + probability: 1, }, - }) - ); + }, + }); expect(response.status).to.be(200); servicesDetailedStatistics = response.body; }); @@ -106,52 +110,61 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('returns empty when empty service names is passed', async () => { - const response = await supertest.get( - url.format({ - pathname: `/internal/apm/services/detailed_statistics`, - query: { - start, - end, - serviceNames: JSON.stringify([]), - environment: 'ENVIRONMENT_ALL', - kuery: '', + try { + await apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/detailed_statistics`, + params: { + query: { + start, + end, + serviceNames: JSON.stringify([]), + environment: 'ENVIRONMENT_ALL', + kuery: '', + probability: 1, + }, }, - }) - ); - expect(response.status).to.be(400); - expect(response.body.message).to.equal('serviceNames cannot be empty'); + }); + expect().fail('Expected API call to throw an error'); + } catch (error: unknown) { + const apiError = error as ApmApiError; + expect(apiError.res.status).eql(400); + + expect(apiError.res.body.message).eql('serviceNames cannot be empty'); + } }); it('filters by environment', async () => { - const response = await supertest.get( - url.format({ - pathname: `/internal/apm/services/detailed_statistics`, + const response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/detailed_statistics`, + params: { query: { start, end, serviceNames: JSON.stringify(serviceNames), environment: 'production', kuery: '', + probability: 1, }, - }) - ); + }, + }); expect(response.status).to.be(200); expect(Object.keys(response.body.currentPeriod).length).to.be(1); expect(response.body.currentPeriod['opbeans-java']).not.to.be.empty(); }); it('filters by kuery', async () => { - const response = await supertest.get( - url.format({ - pathname: `/internal/apm/services/detailed_statistics`, + const response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/detailed_statistics`, + params: { query: { start, end, serviceNames: JSON.stringify(serviceNames), environment: 'ENVIRONMENT_ALL', kuery: 'transaction.type : "invalid_transaction_type"', + probability: 1, }, - }) - ); + }, + }); expect(response.status).to.be(200); expect(Object.keys(response.body.currentPeriod)).to.be.empty(); }); @@ -164,9 +177,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { let servicesDetailedStatistics: ServicesDetailedStatisticsReturn; before(async () => { - const response = await supertest.get( - url.format({ - pathname: `/internal/apm/services/detailed_statistics`, + const response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/detailed_statistics`, + params: { query: { start: moment(end).subtract(15, 'minutes').toISOString(), end, @@ -174,9 +187,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { offset: '15m', environment: 'ENVIRONMENT_ALL', kuery: '', + probability: 1, }, - }) - ); + }, + }); + expect(response.status).to.be(200); servicesDetailedStatistics = response.body; }); diff --git a/x-pack/test/apm_api_integration/tests/services/top_services.spec.ts b/x-pack/test/apm_api_integration/tests/services/top_services.spec.ts index 2642553b2dfd8..3a27221304bc5 100644 --- a/x-pack/test/apm_api_integration/tests/services/top_services.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/top_services.spec.ts @@ -12,25 +12,21 @@ import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_ import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { SupertestReturnType } from '../../common/apm_api_supertest'; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); - const supertest = getService('legacySupertestAsApmReadUser'); const apmApiClient = getService('apmApiClient'); const synthtrace = getService('synthtraceEsClient'); - const supertestAsApmReadUserWithoutMlAccess = getService( - 'legacySupertestAsApmReadUserWithoutMlAccess' - ); - const archiveName = 'apm_8.0.0'; const archiveRange = archives_metadata[archiveName]; // url parameters - const archiveStart = encodeURIComponent(archiveRange.start); - const archiveEnd = encodeURIComponent(archiveRange.end); + const archiveStart = archiveRange.start; + const archiveEnd = archiveRange.end; const start = '2021-10-01T00:00:00.000Z'; const end = '2021-10-01T00:05:00.000Z'; @@ -40,9 +36,18 @@ export default function ApiTest({ getService }: FtrProviderContext) { { config: 'basic', archives: ['apm_mappings_only_8.0.0'] }, () => { it('handles the empty state', async () => { - const response = await supertest.get( - `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` - ); + const response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/services`, + params: { + query: { + start, + end, + environment: ENVIRONMENT_ALL.value, + kuery: '', + probability: 1, + }, + }, + }); expect(response.status).to.be(200); expect(response.body.items.length).to.be(0); @@ -153,6 +158,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { end, environment: ENVIRONMENT_ALL.value, kuery: '', + probability: 1, }, }, }); @@ -204,6 +210,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { end, environment: 'production', kuery: '', + probability: 1, }, }, }); @@ -238,6 +245,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { end, environment: ENVIRONMENT_ALL.value, kuery: 'service.node.name:"multiple-env-service-development"', + probability: 1, }, }, }); @@ -272,6 +280,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { end, environment: ENVIRONMENT_ALL.value, kuery: 'not (transaction.type:request)', + probability: 1, }, }, }); @@ -300,9 +309,18 @@ export default function ApiTest({ getService }: FtrProviderContext) { }; before(async () => { - response = await supertest.get( - `/internal/apm/services?start=${archiveStart}&end=${archiveEnd}&environment=ENVIRONMENT_ALL&kuery=` - ); + response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/services`, + params: { + query: { + start: archiveStart, + end: archiveEnd, + environment: ENVIRONMENT_ALL.value, + kuery: '', + probability: 1, + }, + }, + }); }); it('the response is successful', () => { @@ -344,11 +362,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('with a user that does not have access to ML', () => { - let response: Awaited>; + let response: SupertestReturnType<'GET /internal/apm/services'>; before(async () => { - response = await supertestAsApmReadUserWithoutMlAccess.get( - `/internal/apm/services?start=${archiveStart}&end=${archiveEnd}&environment=ENVIRONMENT_ALL&kuery=` - ); + response = await apmApiClient.noMlAccessUser({ + endpoint: 'GET /internal/apm/services', + params: { + query: { + start: archiveStart, + end: archiveEnd, + environment: ENVIRONMENT_ALL.value, + kuery: '', + probability: 1, + }, + }, + }); }); it('the response is successful', () => { @@ -361,7 +388,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('contains no health statuses', () => { const definedHealthStatuses = response.body.items - .map((item: any) => item.healthStatus) + .map((item) => item.healthStatus) .filter(Boolean); expect(definedHealthStatuses.length).to.be(0); @@ -369,13 +396,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('and fetching a list of services with a filter', () => { - let response: Awaited>; + let response: SupertestReturnType<'GET /internal/apm/services'>; before(async () => { - response = await supertest.get( - `/internal/apm/services?environment=ENVIRONMENT_ALL&start=${archiveStart}&end=${archiveEnd}&kuery=${encodeURIComponent( - 'service.name:opbeans-java' - )}` - ); + response = await apmApiClient.noMlAccessUser({ + endpoint: 'GET /internal/apm/services', + params: { + query: { + start: archiveStart, + end: archiveEnd, + environment: ENVIRONMENT_ALL.value, + kuery: 'service.name:opbeans-java', + probability: 1, + }, + }, + }); }); it('does not return health statuses for services that are not found in APM data', () => { diff --git a/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts b/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts index 566281b59a6af..e3b2c348a7f14 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts @@ -37,6 +37,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { params: { query: { ...commonQuery, + probability: 1, kuery: `service.name : "${serviceName}" and processor.event : "${processorEvent}"`, }, }, diff --git a/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts b/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts index 670881d783514..0530d166cc3af 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts +++ b/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts @@ -42,6 +42,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { query: { ...commonQuery, kuery: `service.name : "${serviceName}" and processor.event : "${processorEvent}"`, + probability: 1, }, }, }), diff --git a/x-pack/test/apm_api_integration/tests/traces/top_traces.spec.ts b/x-pack/test/apm_api_integration/tests/traces/top_traces.spec.ts index 06a24cbd34a4b..b49133240c865 100644 --- a/x-pack/test/apm_api_integration/tests/traces/top_traces.spec.ts +++ b/x-pack/test/apm_api_integration/tests/traces/top_traces.spec.ts @@ -12,20 +12,28 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); - const supertest = getService('legacySupertestAsApmReadUser'); + const apmApiClient = getService('apmApiClient'); const archiveName = 'apm_8.0.0'; const metadata = archives_metadata[archiveName]; // url parameters - const start = encodeURIComponent(metadata.start); - const end = encodeURIComponent(metadata.end); + const { start, end } = metadata; registry.when('Top traces when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles empty state', async () => { - const response = await supertest.get( - `/internal/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` - ); + const response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/traces`, + params: { + query: { + start, + end, + kuery: '', + environment: 'ENVIRONMENT_ALL', + probability: 1, + }, + }, + }); expect(response.status).to.be(200); expect(response.body.items.length).to.be(0); @@ -38,9 +46,18 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { let response: any; before(async () => { - response = await supertest.get( - `/internal/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` - ); + response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/traces', + params: { + query: { + start, + end, + kuery: '', + environment: 'ENVIRONMENT_ALL', + probability: 1, + }, + }, + }); }); it('returns the correct status code', async () => { From 64479235912eecd8364a7dfd4231931839c61cf9 Mon Sep 17 00:00:00 2001 From: Muhammad Ibragimov <53621505+mibragimov@users.noreply.github.com> Date: Thu, 21 Apr 2022 15:45:18 +0500 Subject: [PATCH 2/7] [Console] Fix condition auto-completion for templates (#126881) * Fix condition autocompletion for templates * Added block level matching logic * Fix lint * Fixed types * Resolved comments * Added a custom type guard * Minor refactor * Add type to type imports * Add functional tests and comments Co-authored-by: Muhammad Ibragimov Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/lib/autocomplete/autocomplete.ts | 78 ++++++++++++++++++- .../console/public/lib/autocomplete/types.ts | 9 +++ test/functional/apps/console/_autocomplete.ts | 44 +++++++++++ test/functional/page_objects/console_page.ts | 5 +- 4 files changed, 130 insertions(+), 6 deletions(-) diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts index 1dc41430e3855..3e59c4e6bb023 100644 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ b/src/plugins/console/public/lib/autocomplete/autocomplete.ts @@ -11,22 +11,22 @@ import { i18n } from '@kbn/i18n'; // TODO: All of these imports need to be moved to the core editor so that it can inject components from there. import { - getTopLevelUrlCompleteComponents, getEndpointBodyCompleteComponents, getGlobalAutocompleteComponents, + getTopLevelUrlCompleteComponents, getUnmatchedEndpointComponents, // @ts-ignore } from '../kb/kb'; import { createTokenIterator } from '../../application/factories'; -import { Position, Token, Range, CoreEditor } from '../../types'; +import type { CoreEditor, Position, Range, Token } from '../../types'; import type RowParser from '../row_parser'; import * as utils from '../utils'; // @ts-ignore import { populateContext } from './engine'; -import { AutoCompleteContext, ResultTerm } from './types'; +import type { AutoCompleteContext, DataAutoCompleteRulesOneOf, ResultTerm } from './types'; // @ts-ignore import { URL_PATH_END_MARKER } from './components'; @@ -349,14 +349,84 @@ export default function ({ }); } + /** + * Get a different set of templates based on the value configured in the request. + * For example, when creating a snapshot repository of different types (`fs`, `url` etc), + * different properties are inserted in the textarea based on the type. + * E.g. https://github.com/elastic/kibana/blob/main/src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create_repository.json + */ + function getConditionalTemplate( + name: string, + autocompleteRules: Record | null | undefined + ) { + const obj = autocompleteRules && autocompleteRules[name]; + + if (obj) { + const currentLineNumber = editor.getCurrentPosition().lineNumber; + + if (hasOneOfIn(obj)) { + // Get the line number of value that should provide different templates based on that + const startLine = getStartLineNumber(currentLineNumber, obj.__one_of); + // Join line values from start to current line + const lines = editor.getLines(startLine, currentLineNumber).join('\n'); + // Get the correct template by comparing the autocomplete rules against the lines + const prop = getProperty(lines, obj.__one_of); + if (prop && prop.__template) { + return prop.__template; + } + } + } + } + + /** + * Check if object has a property of '__one_of' + */ + function hasOneOfIn(value: unknown): value is { __one_of: DataAutoCompleteRulesOneOf[] } { + return typeof value === 'object' && value !== null && '__one_of' in value; + } + + /** + * Get the start line of value that matches the autocomplete rules condition + */ + function getStartLineNumber(currentLine: number, rules: DataAutoCompleteRulesOneOf[]): number { + if (currentLine === 1) { + return currentLine; + } + const value = editor.getLineValue(currentLine); + const prop = getProperty(value, rules); + if (prop) { + return currentLine; + } + return getStartLineNumber(currentLine - 1, rules); + } + + /** + * Get the matching property based on the given condition + */ + function getProperty(condition: string, rules: DataAutoCompleteRulesOneOf[]) { + return rules.find((rule) => { + if (rule.__condition && rule.__condition.lines_regex) { + return new RegExp(rule.__condition.lines_regex, 'm').test(condition); + } + return false; + }); + } + function applyTerm(term: { value?: string; context?: AutoCompleteContext; - template?: { __raw: boolean; value: string }; + template?: { __raw?: boolean; value?: string; [key: string]: unknown }; insertValue?: string; }) { const context = term.context!; + if (context?.endpoint && term.value) { + const { data_autocomplete_rules: autocompleteRules } = context.endpoint; + const template = getConditionalTemplate(term.value, autocompleteRules); + if (template) { + term.template = template; + } + } // make sure we get up to date replacement info. addReplacementInfoToContext(context, editor.getCurrentPosition(), term.insertValue); diff --git a/src/plugins/console/public/lib/autocomplete/types.ts b/src/plugins/console/public/lib/autocomplete/types.ts index 33c543f43be9e..15d32e6426a6c 100644 --- a/src/plugins/console/public/lib/autocomplete/types.ts +++ b/src/plugins/console/public/lib/autocomplete/types.ts @@ -15,6 +15,14 @@ export interface ResultTerm { value?: string; } +export interface DataAutoCompleteRulesOneOf { + __condition?: { + lines_regex: string; + }; + __template: Record; + [key: string]: unknown; +} + export interface AutoCompleteContext { autoCompleteSet?: null | ResultTerm[]; endpoint?: null | { @@ -24,6 +32,7 @@ export interface AutoCompleteContext { bodyAutocompleteRootComponents: unknown; id?: string; documentation?: string; + data_autocomplete_rules?: Record | null; }; urlPath?: null | unknown; urlParamsTokenPath?: Array> | null; diff --git a/test/functional/apps/console/_autocomplete.ts b/test/functional/apps/console/_autocomplete.ts index 4b424b2a79c66..57c59793f69f6 100644 --- a/test/functional/apps/console/_autocomplete.ts +++ b/test/functional/apps/console/_autocomplete.ts @@ -7,10 +7,12 @@ */ import expect from '@kbn/expect'; +import { asyncForEach } from '@kbn/std'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); + const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'console']); describe('console autocomplete feature', function describeIndexTests() { @@ -62,5 +64,47 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(lastChar).to.be.eql(','); }); }); + + describe('with conditional templates', async () => { + const CONDITIONAL_TEMPLATES = [ + { + type: 'fs', + template: `"location": "path"`, + }, + { + type: 'url', + template: `"url": ""`, + }, + { type: 's3', template: `"bucket": ""` }, + { + type: 'azure', + template: `"path": ""`, + }, + ]; + + beforeEach(async () => { + await PageObjects.console.clearTextArea(); + await PageObjects.console.enterRequest('\n POST _snapshot/test_repo'); + }); + + await asyncForEach(CONDITIONAL_TEMPLATES, async ({ type, template }) => { + it('should insert different templates depending on the value of type', async () => { + await PageObjects.console.enterText(`{\n\t"type": "${type}"`); + await PageObjects.console.pressEnter(); + // Prompt autocomplete for 'settings' + await PageObjects.console.promptAutocomplete('s'); + + await retry.waitFor('autocomplete to be visible', () => + PageObjects.console.isAutocompleteVisible() + ); + await PageObjects.console.pressEnter(); + await retry.try(async () => { + const request = await PageObjects.console.getRequest(); + log.debug(request); + expect(request).to.contain(`${template}`); + }); + }); + }); + }); }); } diff --git a/test/functional/page_objects/console_page.ts b/test/functional/page_objects/console_page.ts index 32c859cc1aed9..281c49a789acf 100644 --- a/test/functional/page_objects/console_page.ts +++ b/test/functional/page_objects/console_page.ts @@ -83,10 +83,11 @@ export class ConsolePageObject extends FtrService { } } - public async promptAutocomplete() { + // Prompt autocomplete window and provide a initial letter of properties to narrow down the results. E.g. 'b' = 'bool' + public async promptAutocomplete(letter = 'b') { const textArea = await this.testSubjects.find('console-textarea'); await textArea.clickMouseButton(); - await textArea.type('b'); + await textArea.type(letter); await this.retry.waitFor('autocomplete to be visible', () => this.isAutocompleteVisible()); } From 58acda6b9205f68e104408a04b667f508ed0a756 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Thu, 21 Apr 2022 14:08:50 +0200 Subject: [PATCH 3/7] unblocks main (#130771) --- .../detection_rules/custom_query_rule.spec.ts | 11 ++++++----- .../detection_rules/indicator_match_rule.spec.ts | 13 +++++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts index 92bd07b8a24ba..4f62fb199272f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts @@ -26,12 +26,12 @@ import { SHOWING_RULES_TEXT, } from '../../screens/alerts_detection_rules'; import { - ABOUT_CONTINUE_BTN, - ABOUT_EDIT_BUTTON, + // ABOUT_CONTINUE_BTN, + // ABOUT_EDIT_BUTTON, ACTIONS_THROTTLE_INPUT, CUSTOM_QUERY_INPUT, - DEFINE_CONTINUE_BUTTON, - DEFINE_EDIT_BUTTON, + // DEFINE_CONTINUE_BUTTON, + // DEFINE_EDIT_BUTTON, DEFINE_INDEX_INPUT, DEFAULT_RISK_SCORE_INPUT, RULE_DESCRIPTION_INPUT, @@ -134,6 +134,7 @@ describe('Custom query rules', () => { fillAboutRuleAndContinue(this.rule); fillScheduleRuleAndContinue(this.rule); + /* Commenting this piece of code due to the following issue: https://github.com/elastic/kibana/issues/130767 // expect define step to repopulate cy.get(DEFINE_EDIT_BUTTON).click(); cy.get(CUSTOM_QUERY_INPUT).should('have.value', this.rule.customQuery); @@ -144,7 +145,7 @@ describe('Custom query rules', () => { cy.get(ABOUT_EDIT_BUTTON).click(); cy.get(RULE_NAME_INPUT).invoke('val').should('eql', this.rule.name); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); - cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); + cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); */ createAndEnableRule(); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts index 49991c8546845..1d25e2eb0c508 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts @@ -177,11 +177,14 @@ describe('indicator match', () => { visitWithoutDateRange(RULE_CREATION); selectIndicatorMatchType(); }); - it('Has a default set of *:*', () => { + + // Unskip once https://github.com/elastic/kibana/issues/130770 is fixed + it.skip('Has a default set of *:*', () => { getCustomQueryInput().should('have.text', '*:*'); }); - it('Shows invalidation text if text is removed', () => { + // Unskip once https://github.com/elastic/kibana/issues/1307707 is fixed + it.skip('Shows invalidation text if text is removed', () => { getCustomQueryInput().type('{selectall}{del}'); getCustomQueryInvalidationText().should('exist'); }); @@ -398,7 +401,8 @@ describe('indicator match', () => { }); describe('Schedule', () => { - it('IM rule has 1h time interval and lookback by default', () => { + // Unskip once https://github.com/elastic/kibana/issues/1307707 is fixed + it.skip('IM rule has 1h time interval and lookback by default', () => { visitWithoutDateRange(RULE_CREATION); selectIndicatorMatchType(); fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule()); @@ -417,7 +421,8 @@ describe('indicator match', () => { deleteAlertsAndRules(); }); - it('Creates and enables a new Indicator Match rule', () => { + // Unskip once https://github.com/elastic/kibana/issues/1307707 is fixed + it.skip('Creates and enables a new Indicator Match rule', () => { visitWithoutDateRange(RULE_CREATION); selectIndicatorMatchType(); fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule()); From b5322c77b16a38e61f9d02eab00603ecbb40d285 Mon Sep 17 00:00:00 2001 From: Ignacio Rivas Date: Thu, 21 Apr 2022 14:33:13 +0200 Subject: [PATCH 4/7] [ILM] Remove `axios` dependency in tests (#129009) * Start refactoring tests * Refactor more tests * Finish fixing tests * Fix last test * Fix conflict * Address CR changes --- .../app/{app.helpers.tsx => app.helpers.ts} | 38 +- .../client_integration/app/app.test.ts | 37 +- .../features/delete_phase.helpers.ts | 5 +- .../edit_policy/features/delete_phase.test.ts | 35 +- .../edit_policy/features/edit_warning.test.ts | 19 +- .../edit_policy/features/frozen_phase.test.ts | 7 +- .../cloud_aware_behavior.helpers.ts | 14 +- .../cloud_aware_behavior.test.ts | 8 +- .../node_allocation/cold_phase.helpers.ts | 5 +- .../node_allocation/cold_phase.test.ts | 12 +- .../general_behavior.helpers.ts | 5 +- .../node_allocation/general_behavior.test.ts | 28 +- .../node_allocation/warm_phase.helpers.ts | 5 +- .../node_allocation/warm_phase.test.ts | 12 +- .../features/request_flyout.helpers.ts | 9 +- .../features/request_flyout.test.ts | 9 +- .../edit_policy/features/rollover.helpers.ts | 5 +- .../edit_policy/features/rollover.test.ts | 8 +- .../features/searchable_snapshots.helpers.ts | 12 +- .../features/searchable_snapshots.test.ts | 71 ++- .../edit_policy/features/timeline.helpers.ts | 5 +- .../edit_policy/features/timeline.test.ts | 8 +- .../edit_policy/features/timing.helpers.ts | 5 +- .../edit_policy/features/timing.test.ts | 8 +- .../cold_phase_validation.test.ts | 5 +- .../form_validation/error_indicators.test.ts | 5 +- .../hot_phase_validation.test.ts | 5 +- .../policy_name_validation.test.ts | 7 +- .../form_validation/timing.test.ts | 5 +- .../form_validation/validation.helpers.ts | 8 +- .../warm_phase_validation.test.ts | 5 +- .../edit_policy/init_test_bed.ts | 41 ++ .../edit_policy/init_test_bed.tsx | 55 -- .../policy_serialization.helpers.ts | 12 +- .../policy_serialization.test.ts | 525 ++++++++++-------- .../helpers/http_requests.ts | 115 ++-- .../client_integration/helpers/index.ts | 2 +- .../helpers/setup_environment.ts | 37 -- .../helpers/setup_environment.tsx | 56 ++ .../__jest__/extend_index_management.test.tsx | 12 +- 40 files changed, 697 insertions(+), 568 deletions(-) rename x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/{app.helpers.tsx => app.helpers.ts} (59%) create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.ts delete mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx delete mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.tsx diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.ts similarity index 59% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx rename to x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.ts index 9364d07d82382..df64fbce3fa1d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.ts @@ -5,31 +5,11 @@ * 2.0. */ -import React from 'react'; import { act } from 'react-dom/test-utils'; +import { HttpSetup } from '@kbn/core/public'; import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test-jest-helpers'; -import { docLinksServiceMock, executionContextServiceMock } from '@kbn/core/public/mocks'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; -import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; import { App } from '../../../public/application/app'; - -const breadcrumbService = createBreadcrumbsMock(); - -const AppWithContext = (props: any) => { - return ( - - - - ); -}; +import { WithAppDependencies } from '../helpers'; const getTestBedConfig = (initialEntries: string[]): TestBedConfig => ({ memoryRouter: { @@ -40,9 +20,6 @@ const getTestBedConfig = (initialEntries: string[]): TestBedConfig => ({ }, }); -const initTestBed = (initialEntries: string[]) => - registerTestBed(AppWithContext, getTestBedConfig(initialEntries))(); - export interface AppTestBed extends TestBed { actions: { clickPolicyNameLink: () => void; @@ -50,8 +27,15 @@ export interface AppTestBed extends TestBed { }; } -export const setup = async (initialEntries: string[]): Promise => { - const testBed = await initTestBed(initialEntries); +export const setup = async ( + httpSetup: HttpSetup, + initialEntries: string[] +): Promise => { + const initTestBed = registerTestBed( + WithAppDependencies(App, httpSetup), + getTestBedConfig(initialEntries) + ); + const testBed = await initTestBed(); const clickPolicyNameLink = async () => { const { component, find } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts index e2c8a82af7d8f..1aaec089724b6 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts @@ -40,16 +40,13 @@ jest.mock('@elastic/eui', () => { describe('', () => { let testBed: AppTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - afterAll(() => { - server.restore(); - }); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); describe('new policy creation', () => { test('when there are no policies', async () => { httpRequestsMockHelpers.setLoadPolicies([]); await act(async () => { - testBed = await setup(['/']); + testBed = await setup(httpSetup, ['/']); }); const { component, actions } = testBed; @@ -65,7 +62,7 @@ describe('', () => { test('when there are policies', async () => { httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy()]); await act(async () => { - testBed = await setup(['/']); + testBed = await setup(httpSetup, ['/']); }); const { component, actions } = testBed; @@ -86,7 +83,7 @@ describe('', () => { test('clicking policy name in the table works', async () => { await act(async () => { - testBed = await setup(['/']); + testBed = await setup(httpSetup, ['/']); }); const { component, actions } = testBed; @@ -100,7 +97,7 @@ describe('', () => { test('loading edit policy page url works', async () => { await act(async () => { - testBed = await setup([getEncodedPolicyEditPath(SPECIAL_CHARS_NAME)]); + testBed = await setup(httpSetup, [getEncodedPolicyEditPath(SPECIAL_CHARS_NAME)]); }); const { component } = testBed; @@ -113,7 +110,7 @@ describe('', () => { // when those links are open in a new tab, address bar contains double encoded url test('loading edit policy page url with double encoding works', async () => { await act(async () => { - testBed = await setup([getDoubleEncodedPolicyEditPath(SPECIAL_CHARS_NAME)]); + testBed = await setup(httpSetup, [getDoubleEncodedPolicyEditPath(SPECIAL_CHARS_NAME)]); }); const { component } = testBed; @@ -130,7 +127,7 @@ describe('', () => { test('loading edit policy page url works', async () => { await act(async () => { - testBed = await setup([getEncodedPolicyEditPath(PERCENT_SIGN_NAME)]); + testBed = await setup(httpSetup, [getEncodedPolicyEditPath(PERCENT_SIGN_NAME)]); }); const { component } = testBed; @@ -141,7 +138,7 @@ describe('', () => { test('loading edit policy page url with double encoding works', async () => { await act(async () => { - testBed = await setup([getDoubleEncodedPolicyEditPath(PERCENT_SIGN_NAME)]); + testBed = await setup(httpSetup, [getDoubleEncodedPolicyEditPath(PERCENT_SIGN_NAME)]); }); const { component } = testBed; @@ -160,7 +157,7 @@ describe('', () => { test('clicking policy name in the table works', async () => { await act(async () => { - testBed = await setup(['/']); + testBed = await setup(httpSetup, ['/']); }); const { component, actions } = testBed; @@ -176,7 +173,9 @@ describe('', () => { test("loading edit policy page url doesn't work", async () => { await act(async () => { - testBed = await setup([getEncodedPolicyEditPath(PERCENT_SIGN_WITH_OTHER_CHARS_NAME)]); + testBed = await setup(httpSetup, [ + getEncodedPolicyEditPath(PERCENT_SIGN_WITH_OTHER_CHARS_NAME), + ]); }); const { component } = testBed; @@ -192,7 +191,9 @@ describe('', () => { // when those links are open in a new tab, address bar contains double encoded url test('loading edit policy page url with double encoding works', async () => { await act(async () => { - testBed = await setup([getDoubleEncodedPolicyEditPath(PERCENT_SIGN_WITH_OTHER_CHARS_NAME)]); + testBed = await setup(httpSetup, [ + getDoubleEncodedPolicyEditPath(PERCENT_SIGN_WITH_OTHER_CHARS_NAME), + ]); }); const { component } = testBed; @@ -211,7 +212,7 @@ describe('', () => { test('clicking policy name in the table works correctly', async () => { await act(async () => { - testBed = await setup(['/']); + testBed = await setup(httpSetup, ['/']); }); const { component, actions } = testBed; @@ -227,7 +228,7 @@ describe('', () => { test("loading edit policy page url doesn't work", async () => { await act(async () => { - testBed = await setup([getEncodedPolicyEditPath(PERCENT_SIGN_25_SEQUENCE)]); + testBed = await setup(httpSetup, [getEncodedPolicyEditPath(PERCENT_SIGN_25_SEQUENCE)]); }); const { component } = testBed; @@ -243,7 +244,9 @@ describe('', () => { // when those links are open in a new tab, address bar contains double encoded url test('loading edit policy page url with double encoding works', async () => { await act(async () => { - testBed = await setup([getDoubleEncodedPolicyEditPath(PERCENT_SIGN_25_SEQUENCE)]); + testBed = await setup(httpSetup, [ + getDoubleEncodedPolicyEditPath(PERCENT_SIGN_25_SEQUENCE), + ]); }); const { component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.helpers.ts index 1914a056528e1..12237a05e7359 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createMinAgeActions, createSavePolicyAction, @@ -17,8 +18,8 @@ type SetupReturn = ReturnType; export type DeleteTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupDeleteTestBed = async () => { - const testBed = await initTestBed(); +export const setupDeleteTestBed = async (httpSetup: HttpSetup) => { + const testBed = await initTestBed(httpSetup); const { exists } = testBed; return { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.test.ts index f8c74ea91890e..d877f05d06ae1 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.test.ts @@ -18,11 +18,7 @@ import { DeleteTestBed, setupDeleteTestBed } from './delete_phase.helpers'; describe(' delete phase', () => { let testBed: DeleteTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeEach(async () => { httpRequestsMockHelpers.setLoadPolicies([DELETE_PHASE_POLICY]); @@ -32,7 +28,7 @@ describe(' delete phase', () => { ]); await act(async () => { - testBed = await setupDeleteTestBed(); + testBed = await setupDeleteTestBed(httpSetup); }); const { component } = testBed; @@ -43,7 +39,7 @@ describe(' delete phase', () => { httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy()]); await act(async () => { - testBed = await setupDeleteTestBed(); + testBed = await setupDeleteTestBed(httpSetup); }); const { component, actions } = testBed; @@ -72,7 +68,6 @@ describe(' delete phase', () => { await actions.savePolicy(); const expected = { - name: DELETE_PHASE_POLICY.name, phases: { ...DELETE_PHASE_POLICY.policy.phases, delete: { @@ -85,12 +80,13 @@ describe(' delete phase', () => { }, }, }, + name: DELETE_PHASE_POLICY.name, }; - const latestRequest = server.requests[server.requests.length - 1]; - expect(latestRequest.url).toBe(`${API_BASE_PATH}/policies`); - expect(latestRequest.method).toBe('POST'); - expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected); + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ body: JSON.stringify(expected) }) + ); }); test('shows a callout when the input is not an existing policy', async () => { @@ -109,7 +105,6 @@ describe(' delete phase', () => { await actions.savePolicy(); const expected = { - name: DELETE_PHASE_POLICY.name, phases: { ...DELETE_PHASE_POLICY.policy.phases, delete: { @@ -119,19 +114,22 @@ describe(' delete phase', () => { }, }, }, + name: DELETE_PHASE_POLICY.name, }; delete expected.phases.delete.actions.wait_for_snapshot; - const latestRequest = server.requests[server.requests.length - 1]; - expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected); + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ body: JSON.stringify(expected) }) + ); }); test('shows a callout when there are no snapshot policies', async () => { // need to call setup on testBed again for it to use a newly defined snapshot policies response httpRequestsMockHelpers.setLoadSnapshotPolicies([]); await act(async () => { - testBed = await setupDeleteTestBed(); + testBed = await setupDeleteTestBed(httpSetup); }); const { component, actions } = testBed; @@ -144,9 +142,10 @@ describe(' delete phase', () => { test('shows a callout when there is an error loading snapshot policies', async () => { // need to call setup on testBed again for it to use a newly defined snapshot policies response - httpRequestsMockHelpers.setLoadSnapshotPolicies([], { status: 500, body: 'error' }); + httpRequestsMockHelpers.setLoadSnapshotPolicies([], { statusCode: 500, message: 'error' }); + await act(async () => { - testBed = await setupDeleteTestBed(); + testBed = await setupDeleteTestBed(httpSetup); }); const { component, actions } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/edit_warning.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/edit_warning.test.ts index 83b3a9fc53398..0cf57f4140aa4 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/edit_warning.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/edit_warning.test.ts @@ -13,7 +13,7 @@ import { getDefaultHotPhasePolicy, POLICY_NAME } from '../constants'; describe(' edit warning', () => { let testBed: TestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -21,14 +21,13 @@ describe(' edit warning', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { component } = testBed; @@ -38,7 +37,7 @@ describe(' edit warning', () => { test('no edit warning for a new policy', async () => { httpRequestsMockHelpers.setLoadPolicies([]); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { exists, component } = testBed; component.update(); @@ -48,7 +47,7 @@ describe(' edit warning', () => { test('an edit warning is shown for an existing policy', async () => { httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy(POLICY_NAME)]); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { exists, component } = testBed; component.update(); @@ -60,7 +59,7 @@ describe(' edit warning', () => { { ...getDefaultHotPhasePolicy(POLICY_NAME), indices: [] }, ]); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { exists, component } = testBed; component.update(); @@ -72,7 +71,7 @@ describe(' edit warning', () => { { ...getDefaultHotPhasePolicy(POLICY_NAME), indexTemplates: [] }, ]); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { exists, component } = testBed; component.update(); @@ -87,7 +86,7 @@ describe(' edit warning', () => { }, ]); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { component, find } = testBed; component.update(); @@ -102,7 +101,7 @@ describe(' edit warning', () => { }, ]); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { component, find } = testBed; component.update(); @@ -117,7 +116,7 @@ describe(' edit warning', () => { }, ]); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { component, find, exists } = testBed; component.update(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts index 05370558ae69d..ffe11133be7fd 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts @@ -14,7 +14,7 @@ import { initTestBed } from '../init_test_bed'; describe(' frozen phase', () => { let testBed: TestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -22,14 +22,13 @@ describe(' frozen phase', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await initTestBed(); + testBed = await initTestBed(httpSetup); }); const { component } = testBed; @@ -41,7 +40,7 @@ describe(' frozen phase', () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await initTestBed({ + testBed = await initTestBed(httpSetup, { appServicesContext: { license: licensingMock.createLicense({ license: { type: 'basic' } }), }, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts index a0a1c80c90bbe..7092d52289de9 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { TestBedConfig } from '@kbn/test-jest-helpers'; import { AppServicesContext } from '../../../../../public/types'; @@ -15,11 +16,14 @@ type SetupReturn = ReturnType; export type CloudNodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupCloudNodeAllocation = async (arg?: { - appServicesContext?: Partial; - testBedConfig?: Partial; -}) => { - const testBed = await initTestBed(arg); +export const setupCloudNodeAllocation = async ( + httpSetup: HttpSetup, + arg?: { + appServicesContext?: Partial; + testBedConfig?: Partial; + } +) => { + const testBed = await initTestBed(httpSetup, arg); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts index 136865329eb50..820f8a4f9100a 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts @@ -14,7 +14,7 @@ import { describe(' node allocation cloud-aware behavior', () => { let testBed: CloudNodeAllocationTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -22,23 +22,21 @@ describe(' node allocation cloud-aware behavior', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); const setup = async (isOnCloud?: boolean) => { await act(async () => { if (Boolean(isOnCloud)) { - testBed = await setupCloudNodeAllocation({ + testBed = await setupCloudNodeAllocation(httpSetup, { appServicesContext: { cloud: { isCloudEnabled: true } }, }); } else { - testBed = await setupCloudNodeAllocation(); + testBed = await setupCloudNodeAllocation(httpSetup); } }); }; beforeEach(async () => { - server.respondImmediately = true; httpRequestsMockHelpers.setLoadPolicies([]); httpRequestsMockHelpers.setListNodes({ nodesByRoles: { data: ['node1'] }, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts index 9848915f8feb9..16a545f882587 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createNodeAllocationActions, createTogglePhaseAction } from '../../../helpers'; import { initTestBed } from '../../init_test_bed'; @@ -12,8 +13,8 @@ type SetupReturn = ReturnType; export type NodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupColdPhaseNodeAllocation = async () => { - const testBed = await initTestBed(); +export const setupColdPhaseNodeAllocation = async (httpSetup: HttpSetup) => { + const testBed = await initTestBed(httpSetup); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts index e2093e04b7779..63382de45f414 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts @@ -11,7 +11,7 @@ import { NodeAllocationTestBed, setupColdPhaseNodeAllocation } from './cold_phas describe(' node allocation in the cold phase', () => { let testBed: NodeAllocationTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, setDelayResponse, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -19,17 +19,15 @@ describe(' node allocation in the cold phase', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); const setup = async () => { await act(async () => { - testBed = await setupColdPhaseNodeAllocation(); + testBed = await setupColdPhaseNodeAllocation(httpSetup); }); }; beforeEach(async () => { - server.respondImmediately = true; httpRequestsMockHelpers.setLoadPolicies([]); httpRequestsMockHelpers.setListNodes({ nodesByRoles: { data: ['node1'] }, @@ -64,7 +62,8 @@ describe(' node allocation in the cold phase', () => { describe('when using node attributes', () => { test('shows spinner for node attributes input when loading', async () => { - server.respondImmediately = false; + // We don't want the request to resolve immediately. + setDelayResponse(true); const { actions } = testBed; await actions.toggleColdPhase(); @@ -78,6 +77,9 @@ describe(' node allocation in the cold phase', () => { expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeFalsy(); expect(actions.hasDefaultToDataNodesNotice()).toBeFalsy(); expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeFalsy(); + + // Reset delayed response status + setDelayResponse(false); }); describe('and some are defined', () => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts index 384478bcf4c66..ac7dbb61518c7 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createNodeAllocationActions, createReplicasAction, @@ -16,8 +17,8 @@ type SetupReturn = ReturnType; export type GeneralNodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupGeneralNodeAllocation = async () => { - const testBed = await initTestBed(); +export const setupGeneralNodeAllocation = async (httpSetup: HttpSetup) => { + const testBed = await initTestBed(httpSetup); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts index cac6c174b769e..1eecd5207664f 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts @@ -6,6 +6,7 @@ */ import { act } from 'react-dom/test-utils'; +import { HttpFetchOptionsWithPath } from '@kbn/core/public'; import { setupEnvironment } from '../../../helpers'; import { GeneralNodeAllocationTestBed, @@ -16,10 +17,11 @@ import { POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION, POLICY_WITH_NODE_ROLE_ALLOCATION, } from '../../constants'; +import { API_BASE_PATH } from '../../../../../common/constants'; describe(' node allocation general behavior', () => { let testBed: GeneralNodeAllocationTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -27,12 +29,11 @@ describe(' node allocation general behavior', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); const setup = async () => { await act(async () => { - testBed = await setupGeneralNodeAllocation(); + testBed = await setupGeneralNodeAllocation(httpSetup); }); }; @@ -53,10 +54,13 @@ describe(' node allocation general behavior', () => { await actions.setDataAllocation('node_attrs'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const warmPhase = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm; - expect(warmPhase.actions.migrate).toEqual({ enabled: false }); + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.warm.actions.migrate).toEqual({ enabled: false }); }); describe('node roles', () => { @@ -84,12 +88,16 @@ describe(' node allocation general behavior', () => { test('setting replicas serialization', async () => { const { actions } = testBed; + await actions.setReplicas('123'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const warmPhaseActions = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm - .actions; - expect(warmPhaseActions).toMatchInlineSnapshot(` + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.warm.actions).toMatchInlineSnapshot(` Object { "allocate": Object { "number_of_replicas": 123, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts index 5c576ebf74ccb..375b3a633297e 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createNodeAllocationActions, createTogglePhaseAction } from '../../../helpers'; import { initTestBed } from '../../init_test_bed'; @@ -12,8 +13,8 @@ type SetupReturn = ReturnType; export type NodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupWarmPhaseNodeAllocation = async () => { - const testBed = await initTestBed(); +export const setupWarmPhaseNodeAllocation = async (httpSetup: HttpSetup) => { + const testBed = await initTestBed(httpSetup); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts index 8d4d0afcf052d..6f96aaf07da1b 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts @@ -11,7 +11,7 @@ import { NodeAllocationTestBed, setupWarmPhaseNodeAllocation } from './warm_phas describe(' node allocation in the warm phase', () => { let testBed: NodeAllocationTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, setDelayResponse, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -19,17 +19,15 @@ describe(' node allocation in the warm phase', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); const setup = async () => { await act(async () => { - testBed = await setupWarmPhaseNodeAllocation(); + testBed = await setupWarmPhaseNodeAllocation(httpSetup); }); }; beforeEach(async () => { - server.respondImmediately = true; httpRequestsMockHelpers.setLoadPolicies([]); httpRequestsMockHelpers.setListNodes({ nodesByRoles: { data: ['node1'] }, @@ -64,7 +62,8 @@ describe(' node allocation in the warm phase', () => { describe('when using node attributes', () => { test('shows spinner for node attributes input when loading', async () => { - server.respondImmediately = false; + // We don't want the request to resolve immediately. + setDelayResponse(true); const { actions } = testBed; await actions.togglePhase(); @@ -78,6 +77,9 @@ describe(' node allocation in the warm phase', () => { expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeFalsy(); expect(actions.hasDefaultToDataNodesNotice()).toBeFalsy(); expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeFalsy(); + + // Reset delayed response status + setDelayResponse(false); }); describe('and some are defined', () => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.helpers.ts index d74e2877c9fcd..540324d171971 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createFormSetValueAction, createMinAgeActions, @@ -18,10 +19,12 @@ type SetupReturn = ReturnType; export type RequestFlyoutTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupRequestFlyoutTestBed = async (isNewPolicy?: boolean) => { +export const setupRequestFlyoutTestBed = async (httpSetup: HttpSetup, isNewPolicy?: boolean) => { const testBed = isNewPolicy - ? await initTestBed({ testBedConfig: { memoryRouter: { initialEntries: ['/policies/edit'] } } }) - : await initTestBed(); + ? await initTestBed(httpSetup, { + testBedConfig: { memoryRouter: { initialEntries: ['/policies/edit'] } }, + }) + : await initTestBed(httpSetup); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts index e49a1c5c94f90..61dda6fa65efb 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts @@ -12,7 +12,7 @@ import { getDefaultHotPhasePolicy } from '../constants'; describe(' request flyout', () => { let testBed: RequestFlyoutTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -20,14 +20,13 @@ describe(' request flyout', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupRequestFlyoutTestBed(); + testBed = await setupRequestFlyoutTestBed(httpSetup); }); const { component } = testBed; @@ -93,7 +92,7 @@ describe(' request flyout', () => { test('renders the correct json and name for a new policy', async () => { await act(async () => { - testBed = await setupRequestFlyoutTestBed(true); + testBed = await setupRequestFlyoutTestBed(httpSetup, true); }); const { component, actions } = testBed; @@ -154,7 +153,7 @@ describe(' request flyout', () => { httpRequestsMockHelpers.setLoadPolicies([policyWithMetaField]); await act(async () => { - testBed = await setupRequestFlyoutTestBed(); + testBed = await setupRequestFlyoutTestBed(httpSetup); }); const { component, actions } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.helpers.ts index b15e956c84b4c..707240c459161 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createForceMergeActions, createMinAgeActions, @@ -20,8 +21,8 @@ type SetupReturn = ReturnType; export type RolloverTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupRolloverTestBed = async () => { - const testBed = await initTestBed(); +export const setupRolloverTestBed = async (httpSetup: HttpSetup) => { + const testBed = await initTestBed(httpSetup); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts index 432b07efca038..1544390229595 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts @@ -11,17 +11,13 @@ import { RolloverTestBed, setupRolloverTestBed } from './rollover.helpers'; describe(' rollover', () => { let testBed: RolloverTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupRolloverTestBed(); + testBed = await setupRolloverTestBed(httpSetup); }); const { component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.helpers.ts index 23b64c3dade19..f4b41cedd70f1 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createForceMergeActions, createMinAgeActions, @@ -22,10 +23,13 @@ type SetupReturn = ReturnType; export type SearchableSnapshotsTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupSearchableSnapshotsTestBed = async (args?: { - appServicesContext?: Partial; -}) => { - const testBed = await initTestBed(args); +export const setupSearchableSnapshotsTestBed = async ( + httpSetup: HttpSetup, + args?: { + appServicesContext?: Partial; + } +) => { + const testBed = await initTestBed(httpSetup, args); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts index 571170b78b66b..09bb8b85082b6 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts @@ -7,8 +7,10 @@ import { act } from 'react-dom/test-utils'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; +import { HttpFetchOptionsWithPath } from '@kbn/core/public'; import { setupEnvironment } from '../../helpers'; import { getDefaultHotPhasePolicy } from '../constants'; +import { API_BASE_PATH } from '../../../../common/constants'; import { SearchableSnapshotsTestBed, setupSearchableSnapshotsTestBed, @@ -16,17 +18,13 @@ import { describe(' searchable snapshots', () => { let testBed: SearchableSnapshotsTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupSearchableSnapshotsTestBed(); + testBed = await setupSearchableSnapshotsTestBed(httpSetup); }); const { component } = testBed; @@ -78,14 +76,21 @@ describe(' searchable snapshots', () => { await actions.frozen.setMinAgeValue('15'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - expect(latestRequest.method).toBe('POST'); - expect(latestRequest.url).toBe('/api/index_lifecycle_management/policies'); - const reqBody = JSON.parse(JSON.parse(latestRequest.requestBody).body); - - expect(reqBody.phases.hot.actions.searchable_snapshot.snapshot_repository).toBe(repository); - expect(reqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toBe(repository); - expect(reqBody.phases.frozen.actions.searchable_snapshot.snapshot_repository).toBe(repository); + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.hot.actions.searchable_snapshot.snapshot_repository).toBe( + repository + ); + expect(parsedReqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toBe( + repository + ); + expect(parsedReqBody.phases.frozen.actions.searchable_snapshot.snapshot_repository).toBe( + repository + ); }); test('should update the repository in all searchable snapshot actions', async () => { @@ -101,13 +106,22 @@ describe(' searchable snapshots', () => { // We update the repository in one phase await actions.frozen.setSearchableSnapshot('changed'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const reqBody = JSON.parse(JSON.parse(latestRequest.requestBody).body); + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); // And all phases should be updated - expect(reqBody.phases.hot.actions.searchable_snapshot.snapshot_repository).toBe('changed'); - expect(reqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toBe('changed'); - expect(reqBody.phases.frozen.actions.searchable_snapshot.snapshot_repository).toBe('changed'); + expect(parsedReqBody.phases.hot.actions.searchable_snapshot.snapshot_repository).toBe( + 'changed' + ); + expect(parsedReqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toBe( + 'changed' + ); + expect(parsedReqBody.phases.frozen.actions.searchable_snapshot.snapshot_repository).toBe( + 'changed' + ); }); describe('on cloud', () => { @@ -123,7 +137,7 @@ describe(' searchable snapshots', () => { httpRequestsMockHelpers.setListSnapshotRepos({ repositories: ['found-snapshots'] }); await act(async () => { - testBed = await setupSearchableSnapshotsTestBed({ + testBed = await setupSearchableSnapshotsTestBed(httpSetup, { appServicesContext: { cloud: { isCloudEnabled: true } }, }); }); @@ -152,7 +166,7 @@ describe(' searchable snapshots', () => { httpRequestsMockHelpers.setListSnapshotRepos({ repositories: ['found-snapshots'] }); await act(async () => { - testBed = await setupSearchableSnapshotsTestBed({ + testBed = await setupSearchableSnapshotsTestBed(httpSetup, { appServicesContext: { cloud: { isCloudEnabled: true } }, }); }); @@ -166,12 +180,15 @@ describe(' searchable snapshots', () => { await actions.togglePhase('cold'); await actions.cold.setMinAgeValue('10'); await actions.cold.toggleSearchableSnapshot(); + await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - expect(latestRequest.method).toBe('POST'); - expect(latestRequest.url).toBe('/api/index_lifecycle_management/policies'); - const reqBody = JSON.parse(JSON.parse(latestRequest.requestBody).body); - expect(reqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toEqual( + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toEqual( 'found-snapshots' ); }); @@ -189,7 +206,7 @@ describe(' searchable snapshots', () => { httpRequestsMockHelpers.setListSnapshotRepos({ repositories: ['my-repo'] }); await act(async () => { - testBed = await setupSearchableSnapshotsTestBed({ + testBed = await setupSearchableSnapshotsTestBed(httpSetup, { appServicesContext: { license: licensingMock.createLicense({ license: { type: 'basic' } }), }, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.helpers.ts index 8303fdbac4837..496b27330c931 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createTogglePhaseAction } from '../../helpers'; import { initTestBed } from '../init_test_bed'; import { Phase } from '../../../../common/types'; @@ -13,8 +14,8 @@ type SetupReturn = ReturnType; export type TimelineTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupTimelineTestBed = async () => { - const testBed = await initTestBed(); +export const setupTimelineTestBed = async (httpSetup: HttpSetup) => { + const testBed = await initTestBed(httpSetup); const { exists } = testBed; return { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.test.ts index 33aeb80b38cae..760ede62606cb 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.test.ts @@ -11,17 +11,13 @@ import { setupTimelineTestBed, TimelineTestBed } from './timeline.helpers'; describe(' timeline', () => { let testBed: TimelineTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupTimelineTestBed(); + testBed = await setupTimelineTestBed(httpSetup); }); const { component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.helpers.ts index 57d6f53a21c78..dc70d2dd5ef61 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { createMinAgeActions, createTogglePhaseAction } from '../../helpers'; import { initTestBed } from '../init_test_bed'; @@ -12,8 +13,8 @@ type SetupReturn = ReturnType; export type TimingTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupTimingTestBed = async () => { - const testBed = await initTestBed(); +export const setupTimingTestBed = async (httpSetup: HttpSetup) => { + const testBed = await initTestBed(httpSetup); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.test.ts index 985ee807ed827..0aee8eb5f0be1 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.test.ts @@ -12,17 +12,13 @@ import { PhaseWithTiming } from '../../../../common/types'; describe(' timing', () => { let testBed: TimingTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupTimingTestBed(); + testBed = await setupTimingTestBed(httpSetup); }); const { component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/cold_phase_validation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/cold_phase_validation.test.ts index 4725631e6f894..98891ea72928f 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/cold_phase_validation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/cold_phase_validation.test.ts @@ -12,7 +12,7 @@ import { setupValidationTestBed, ValidationTestBed } from './validation.helpers' describe(' cold phase validation', () => { let testBed: ValidationTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -20,7 +20,6 @@ describe(' cold phase validation', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { @@ -35,7 +34,7 @@ describe(' cold phase validation', () => { ]); await act(async () => { - testBed = await setupValidationTestBed(); + testBed = await setupValidationTestBed(httpSetup); }); const { component, actions } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/error_indicators.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/error_indicators.test.ts index 1464f683ef766..bd4a2caec0be5 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/error_indicators.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/error_indicators.test.ts @@ -11,7 +11,7 @@ import { setupValidationTestBed, ValidationTestBed } from './validation.helpers' describe(' error indicators', () => { let testBed: ValidationTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -19,14 +19,13 @@ describe(' error indicators', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupValidationTestBed(); + testBed = await setupValidationTestBed(httpSetup); }); const { component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/hot_phase_validation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/hot_phase_validation.test.ts index 6dca2b7b23fb7..b0f0857bd0501 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/hot_phase_validation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/hot_phase_validation.test.ts @@ -13,7 +13,7 @@ import { setupValidationTestBed, ValidationTestBed } from './validation.helpers' describe(' hot phase validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -21,13 +21,12 @@ describe(' hot phase validation', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { httpRequestsMockHelpers.setLoadPolicies([]); await act(async () => { - testBed = await setupValidationTestBed(); + testBed = await setupValidationTestBed(httpSetup); }); const { component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/policy_name_validation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/policy_name_validation.test.ts index 799fbf89d47df..9c14584bb2fcf 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/policy_name_validation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/policy_name_validation.test.ts @@ -14,7 +14,7 @@ import { setupValidationTestBed, ValidationTestBed } from './validation.helpers' describe(' policy name validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -22,14 +22,13 @@ describe(' policy name validation', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { httpRequestsMockHelpers.setLoadPolicies(getGeneratedPolicies()); await act(async () => { - testBed = await setupValidationTestBed(); + testBed = await setupValidationTestBed(httpSetup); }); const { component } = testBed; @@ -56,7 +55,7 @@ describe(' policy name validation', () => { test(`doesn't allow to save as new policy but using the same name`, async () => { await act(async () => { - testBed = await setupValidationTestBed({ + testBed = await setupValidationTestBed(httpSetup, { testBedConfig: { memoryRouter: { initialEntries: [`/policies/edit/testy0`], diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts index be4f99103b319..3e29bc5d76580 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts @@ -15,7 +15,7 @@ import { setupValidationTestBed, ValidationTestBed } from './validation.helpers' describe(' timing validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -23,7 +23,6 @@ describe(' timing validation', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { @@ -31,7 +30,7 @@ describe(' timing validation', () => { httpRequestsMockHelpers.setLoadPolicies([]); await act(async () => { - testBed = await setupValidationTestBed(); + testBed = await setupValidationTestBed(httpSetup); }); const { component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/validation.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/validation.helpers.ts index 5b5d3819c3463..592f081929b50 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/validation.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/validation.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { TestBedConfig } from '@kbn/test-jest-helpers'; import { createColdPhaseActions, @@ -25,8 +26,11 @@ type SetupReturn = ReturnType; export type ValidationTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupValidationTestBed = async (arg?: { testBedConfig?: Partial }) => { - const testBed = await initTestBed(arg); +export const setupValidationTestBed = async ( + httpSetup: HttpSetup, + arg?: { testBedConfig?: Partial } +) => { + const testBed = await initTestBed(httpSetup, arg); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/warm_phase_validation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/warm_phase_validation.test.ts index 741611f2d2c3b..9d326e9978519 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/warm_phase_validation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/warm_phase_validation.test.ts @@ -12,7 +12,7 @@ import { setupValidationTestBed, ValidationTestBed } from './validation.helpers' describe(' warm phase validation', () => { let testBed: ValidationTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -20,7 +20,6 @@ describe(' warm phase validation', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { @@ -28,7 +27,7 @@ describe(' warm phase validation', () => { httpRequestsMockHelpers.setLoadPolicies([]); await act(async () => { - testBed = await setupValidationTestBed(); + testBed = await setupValidationTestBed(httpSetup); }); const { component, actions } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.ts new file mode 100644 index 0000000000000..875bf9db36264 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.ts @@ -0,0 +1,41 @@ +/* + * 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 { registerTestBed, TestBedConfig } from '@kbn/test-jest-helpers'; + +import { HttpSetup } from '@kbn/core/public'; +import { WithAppDependencies } from '../helpers'; +import { EditPolicy } from '../../../public/application/sections/edit_policy'; +import { AppServicesContext } from '../../../public/types'; +import { POLICY_NAME } from './constants'; + +const getTestBedConfig = (testBedConfig?: Partial): TestBedConfig => { + return { + memoryRouter: { + initialEntries: [`/policies/edit/${POLICY_NAME}`], + componentRoutePath: `/policies/edit/:policyName`, + }, + ...testBedConfig, + }; +}; + +export const initTestBed = async ( + httpSetup: HttpSetup, + arg?: { + appServicesContext?: Partial; + testBedConfig?: Partial; + } +) => { + const { testBedConfig, appServicesContext } = arg || {}; + + const createTestBed = registerTestBed( + WithAppDependencies(EditPolicy, httpSetup, appServicesContext), + getTestBedConfig(testBedConfig) + ); + + return await createTestBed(); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx deleted file mode 100644 index 6e68032d9b0ea..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx +++ /dev/null @@ -1,55 +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 React from 'react'; -import { registerTestBed, TestBedConfig } from '@kbn/test-jest-helpers'; -import { docLinksServiceMock } from '@kbn/core/public/mocks'; - -import '../helpers/global_mocks'; - -import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; -import { EditPolicy } from '../../../public/application/sections/edit_policy'; -import { KibanaContextProvider } from '../../../public/shared_imports'; -import { AppServicesContext } from '../../../public/types'; -import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; -import { POLICY_NAME } from './constants'; - -const getTestBedConfig = (testBedConfigArgs?: Partial): TestBedConfig => { - return { - memoryRouter: { - initialEntries: [`/policies/edit/${POLICY_NAME}`], - componentRoutePath: `/policies/edit/:policyName`, - }, - ...testBedConfigArgs, - }; -}; - -const breadcrumbService = createBreadcrumbsMock(); - -const EditPolicyContainer = ({ appServicesContext, ...rest }: any) => { - return ( - {}, - ...appServicesContext, - }} - > - - - ); -}; - -export const initTestBed = (arg?: { - appServicesContext?: Partial; - testBedConfig?: Partial; -}) => { - const { testBedConfig: testBedConfigArgs, ...rest } = arg || {}; - return registerTestBed(EditPolicyContainer, getTestBedConfig(testBedConfigArgs))(rest); -}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.helpers.ts index 52d4debca9315..417f53c63a20c 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { AppServicesContext } from '../../../../public/types'; import { createColdPhaseActions, @@ -23,10 +24,13 @@ type SetupReturn = ReturnType; export type SerializationTestBed = SetupReturn extends Promise ? U : SetupReturn; -export const setupSerializationTestBed = async (arg?: { - appServicesContext?: Partial; -}) => { - const testBed = await initTestBed(arg); +export const setupSerializationTestBed = async ( + httpSetup: HttpSetup, + arg?: { + appServicesContext?: Partial; + } +) => { + const testBed = await initTestBed(httpSetup, arg); return { ...testBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.test.ts index 4e3d9fe737f4c..d539d7a6e48ce 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.test.ts @@ -7,7 +7,9 @@ import { act } from 'react-dom/test-utils'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; +import { HttpFetchOptionsWithPath } from '@kbn/core/public'; import { setupEnvironment } from '../../helpers'; +import { API_BASE_PATH } from '../../../../common/constants'; import { getDefaultHotPhasePolicy, POLICY_WITH_INCLUDE_EXCLUDE, @@ -17,17 +19,13 @@ import { SerializationTestBed, setupSerializationTestBed } from './policy_serial describe(' serialization', () => { let testBed: SerializationTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); + const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); beforeEach(async () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupSerializationTestBed(); + testBed = await setupSerializationTestBed(httpSetup); }); const { component } = testBed; @@ -35,6 +33,16 @@ describe(' serialization', () => { }); describe('top level form', () => { + afterAll(async () => { + await act(async () => { + testBed = await setupSerializationTestBed(httpSetup, { + appServicesContext: { + license: licensingMock.createLicense({ license: { type: 'enterprise' } }), + }, + }); + }); + }); + /** * We assume that policies that populate this form are loaded directly from ES and so * are valid according to ES. There may be settings in the policy created through the ILM @@ -44,7 +52,7 @@ describe(' serialization', () => { it('preserves policy settings it did not configure', async () => { httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_KNOWN_AND_UNKNOWN_FIELDS]); await act(async () => { - testBed = await setupSerializationTestBed(); + testBed = await setupSerializationTestBed(httpSetup); }); const { component, actions } = testBed; @@ -56,42 +64,44 @@ describe(' serialization', () => { await actions.togglePhase('delete'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - - expect(entirePolicy).toEqual({ - foo: 'bar', // Made up value - name: 'my_policy', - phases: { - hot: { - actions: { - rollover: { - max_docs: 1000, - max_size: '50gb', - unknown_setting: 123, // Made up setting that should stay preserved + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ + body: JSON.stringify({ + foo: 'bar', // Made up value + phases: { + hot: { + min_age: '0ms', + actions: { + rollover: { + unknown_setting: 123, // Made up setting that should stay preserved + max_size: '50gb', + max_docs: 1000, + }, + }, }, - }, - min_age: '0ms', - }, - warm: { - actions: { - my_unfollow_action: {}, // Made up action - set_priority: { - priority: 22, - unknown_setting: true, + warm: { + min_age: '10d', + actions: { + my_unfollow_action: {}, // Made up action + set_priority: { + priority: 22, + unknown_setting: true, + }, + }, }, }, - min_age: '10d', - }, - }, - }); + name: 'my_policy', + }), + }) + ); }); it('default policy (only policy name input) on enterprise license', async () => { httpRequestsMockHelpers.setLoadPolicies([]); await act(async () => { - testBed = await setupSerializationTestBed(); + testBed = await setupSerializationTestBed(httpSetup); }); const { component, actions } = testBed; @@ -99,33 +109,35 @@ describe(' serialization', () => { await actions.setPolicyName('test_policy'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - - expect(entirePolicy).toEqual({ - name: 'test_policy', - phases: { - hot: { - actions: { - rollover: { - max_age: '30d', - max_primary_shard_size: '50gb', - }, - set_priority: { - priority: 100, + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ + body: JSON.stringify({ + name: 'test_policy', + phases: { + hot: { + actions: { + rollover: { + max_age: '30d', + max_primary_shard_size: '50gb', + }, + set_priority: { + priority: 100, + }, + }, + min_age: '0ms', }, }, - min_age: '0ms', - }, - }, - }); + }), + }) + ); }); it('default policy (only policy name input) on basic license', async () => { httpRequestsMockHelpers.setLoadPolicies([]); await act(async () => { - testBed = await setupSerializationTestBed({ + testBed = await setupSerializationTestBed(httpSetup, { appServicesContext: { license: licensingMock.createLicense({ license: { type: 'basic' } }), }, @@ -137,30 +149,44 @@ describe(' serialization', () => { await actions.setPolicyName('test_policy'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - - expect(entirePolicy).toEqual({ - name: 'test_policy', - phases: { - hot: { - actions: { - rollover: { - max_age: '30d', - max_primary_shard_size: '50gb', - }, - set_priority: { - priority: 100, + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ + body: JSON.stringify({ + name: 'test_policy', + phases: { + hot: { + actions: { + rollover: { + max_age: '30d', + max_primary_shard_size: '50gb', + }, + set_priority: { + priority: 100, + }, + }, + min_age: '0ms', }, }, - min_age: '0ms', - }, - }, - }); + }), + }) + ); }); }); describe('hot phase', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setDefaultResponses(); + + await act(async () => { + testBed = await setupSerializationTestBed(httpSetup); + }); + + const { component } = testBed; + + component.update(); + }); + test('setting all values', async () => { const { actions } = testBed; @@ -176,61 +202,72 @@ describe(' serialization', () => { await actions.hot.setIndexPriority('123'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - expect(entirePolicy).toMatchInlineSnapshot(` - Object { - "name": "my_policy", - "phases": Object { - "hot": Object { - "actions": Object { - "forcemerge": Object { - "index_codec": "best_compression", - "max_num_segments": 123, - }, - "readonly": Object {}, - "rollover": Object { - "max_age": "123h", - "max_docs": 123, - "max_primary_shard_size": "50gb", - "max_size": "123mb", - }, - "set_priority": Object { - "priority": 123, - }, - "shrink": Object { - "number_of_shards": 2, + + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ + body: JSON.stringify({ + name: 'my_policy', + phases: { + hot: { + min_age: '0ms', + actions: { + rollover: { + max_age: '123h', + max_primary_shard_size: '50gb', + max_docs: 123, + max_size: '123mb', + }, + forcemerge: { + max_num_segments: 123, + index_codec: 'best_compression', + }, + shrink: { + number_of_shards: 2, + }, + set_priority: { + priority: 123, + }, + readonly: {}, }, }, - "min_age": "0ms", }, - }, - } - `); + }), + }) + ); }); test('setting searchable snapshot', async () => { const { actions } = testBed; - await actions.hot.setSearchableSnapshot('my-repo'); + await actions.hot.setSearchableSnapshot('abc'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - expect(entirePolicy.phases.hot.actions.searchable_snapshot.snapshot_repository).toBe( - 'my-repo' - ); + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.hot.actions.searchable_snapshot.snapshot_repository).toBe('abc'); }); test('disabling rollover', async () => { const { actions } = testBed; + await actions.rollover.toggleDefault(); await actions.rollover.toggle(); + await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const policy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - const hotActions = policy.phases.hot.actions; + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + const hotActions = parsedReqBody.phases.hot.actions; const rolloverAction = hotActions.rollover; + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); expect(rolloverAction).toBe(undefined); expect(hotActions).toMatchInlineSnapshot(`Object {}`); }); @@ -241,7 +278,7 @@ describe(' serialization', () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupSerializationTestBed(); + testBed = await setupSerializationTestBed(httpSetup); }); const { component } = testBed; @@ -253,9 +290,13 @@ describe(' serialization', () => { await actions.togglePhase('warm'); await actions.warm.setMinAgeValue('11'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const warmPhase = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm; - expect(warmPhase).toMatchInlineSnapshot(` + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.warm).toMatchInlineSnapshot(` Object { "actions": Object { "set_priority": Object { @@ -281,47 +322,48 @@ describe(' serialization', () => { await actions.warm.toggleReadonly(); await actions.warm.setIndexPriority('123'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - // Check shape of entire policy - expect(entirePolicy).toMatchInlineSnapshot(` - Object { - "name": "my_policy", - "phases": Object { - "hot": Object { - "actions": Object { - "rollover": Object { - "max_age": "30d", - "max_primary_shard_size": "50gb", + + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ + body: JSON.stringify({ + name: 'my_policy', + phases: { + hot: { + min_age: '0ms', + actions: { + rollover: { + max_age: '30d', + max_primary_shard_size: '50gb', + }, }, }, - "min_age": "0ms", - }, - "warm": Object { - "actions": Object { - "allocate": Object { - "number_of_replicas": 123, - "require": Object { - "test": "123", + warm: { + min_age: '11d', + actions: { + set_priority: { + priority: 123, }, - }, - "forcemerge": Object { - "index_codec": "best_compression", - "max_num_segments": 123, - }, - "readonly": Object {}, - "set_priority": Object { - "priority": 123, - }, - "shrink": Object { - "number_of_shards": 123, + shrink: { + number_of_shards: 123, + }, + forcemerge: { + max_num_segments: 123, + index_codec: 'best_compression', + }, + allocate: { + require: { + test: '123', + }, + number_of_replicas: 123, + }, + readonly: {}, }, }, - "min_age": "11d", }, - }, - } - `); + }), + }) + ); }); describe('policy with include and exclude', () => { @@ -335,7 +377,7 @@ describe(' serialization', () => { httpRequestsMockHelpers.setLoadSnapshotPolicies([]); await act(async () => { - testBed = await setupSerializationTestBed(); + testBed = await setupSerializationTestBed(httpSetup); }); const { component } = testBed; @@ -347,9 +389,14 @@ describe(' serialization', () => { await actions.warm.setDataAllocation('node_attrs'); await actions.warm.setSelectedNodeAttribute('test:123'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const warmPhaseAllocate = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm - .actions.allocate; + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + const warmPhaseAllocate = parsedReqBody.phases.warm.actions.allocate; + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); expect(warmPhaseAllocate).toMatchInlineSnapshot(` Object { "exclude": Object { @@ -372,7 +419,7 @@ describe(' serialization', () => { httpRequestsMockHelpers.setDefaultResponses(); await act(async () => { - testBed = await setupSerializationTestBed(); + testBed = await setupSerializationTestBed(httpSetup); }); const { component } = testBed; @@ -385,9 +432,13 @@ describe(' serialization', () => { await actions.togglePhase('cold'); await actions.cold.setMinAgeValue('11'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - expect(entirePolicy.phases.cold).toMatchInlineSnapshot(` + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.cold).toMatchInlineSnapshot(` Object { "actions": Object { "set_priority": Object { @@ -412,40 +463,41 @@ describe(' serialization', () => { await actions.cold.setIndexPriority('123'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - - expect(entirePolicy).toMatchInlineSnapshot(` - Object { - "name": "my_policy", - "phases": Object { - "cold": Object { - "actions": Object { - "allocate": Object { - "number_of_replicas": 123, - "require": Object { - "test": "123", + + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ + body: JSON.stringify({ + name: 'my_policy', + phases: { + hot: { + min_age: '0ms', + actions: { + rollover: { + max_age: '30d', + max_primary_shard_size: '50gb', }, }, - "readonly": Object {}, - "set_priority": Object { - "priority": 123, - }, }, - "min_age": "123s", - }, - "hot": Object { - "actions": Object { - "rollover": Object { - "max_age": "30d", - "max_primary_shard_size": "50gb", + cold: { + min_age: '123s', + actions: { + set_priority: { + priority: 123, + }, + allocate: { + require: { + test: '123', + }, + number_of_replicas: 123, + }, + readonly: {}, }, }, - "min_age": "0ms", }, - }, - } - `); + }), + }) + ); }); // Setting searchable snapshot field disables setting replicas so we test this separately @@ -455,15 +507,30 @@ describe(' serialization', () => { await actions.cold.setMinAgeValue('10'); await actions.cold.setSearchableSnapshot('my-repo'); await actions.savePolicy(); - const latestRequest2 = server.requests[server.requests.length - 1]; - const entirePolicy2 = JSON.parse(JSON.parse(latestRequest2.requestBody).body); - expect(entirePolicy2.phases.cold.actions.searchable_snapshot.snapshot_repository).toEqual( + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toEqual( 'my-repo' ); }); }); describe('frozen phase', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setDefaultResponses(); + + await act(async () => { + testBed = await setupSerializationTestBed(httpSetup); + }); + + const { component } = testBed; + component.update(); + }); + test('default value', async () => { const { actions } = testBed; await actions.togglePhase('frozen'); @@ -472,9 +539,12 @@ describe(' serialization', () => { await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - expect(entirePolicy.phases.frozen).toEqual({ + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.frozen).toEqual({ min_age: '13d', actions: { searchable_snapshot: { snapshot_repository: 'myRepo' }, @@ -499,7 +569,7 @@ describe(' serialization', () => { }); await act(async () => { - testBed = await setupSerializationTestBed(); + testBed = await setupSerializationTestBed(httpSetup); }); const { component } = testBed; @@ -511,9 +581,12 @@ describe(' serialization', () => { await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - expect(entirePolicy.phases.frozen).toEqual({ + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.frozen).toEqual({ min_age: '1234m', actions: { searchable_snapshot: { @@ -531,9 +604,13 @@ describe(' serialization', () => { await actions.togglePhase('delete'); await actions.delete.setSnapshotPolicy('test'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - expect(entirePolicy.phases.delete).toEqual({ + + const lastReq: HttpFetchOptionsWithPath[] = httpSetup.post.mock.calls.pop() || []; + const [requestUrl, requestBody] = lastReq; + const parsedReqBody = JSON.parse((requestBody as Record).body); + + expect(requestUrl).toBe(`${API_BASE_PATH}/policies`); + expect(parsedReqBody.phases.delete).toEqual({ min_age: '365d', actions: { delete: {}, @@ -555,38 +632,40 @@ describe(' serialization', () => { await actions.warm.setShrinkSize('100'); await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const entirePolicy = JSON.parse(JSON.parse(latestRequest.requestBody).body); - expect(entirePolicy).toMatchInlineSnapshot(` - Object { - "name": "my_policy", - "phases": Object { - "hot": Object { - "actions": Object { - "rollover": Object { - "max_age": "30d", - "max_primary_shard_size": "50gb", - }, - "shrink": Object { - "max_primary_shard_size": "50gb", + + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/policies`, + expect.objectContaining({ + body: JSON.stringify({ + name: 'my_policy', + phases: { + hot: { + min_age: '0ms', + actions: { + rollover: { + max_age: '30d', + max_primary_shard_size: '50gb', + }, + shrink: { + max_primary_shard_size: '50gb', + }, }, }, - "min_age": "0ms", - }, - "warm": Object { - "actions": Object { - "set_priority": Object { - "priority": 50, - }, - "shrink": Object { - "max_primary_shard_size": "100gb", + warm: { + min_age: '11d', + actions: { + set_priority: { + priority: 50, + }, + shrink: { + max_primary_shard_size: '100gb', + }, }, }, - "min_age": "11d", }, - }, - } - `); + }), + }) + ); }); }); }); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts index 7f37d4c6ccbf0..7ddcd5358404f 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { fakeServer, SinonFakeServer } from 'sinon'; +import { httpServiceMock } from '@kbn/core/public/mocks'; import { API_BASE_PATH } from '../../../common/constants'; import { ListNodesRouteResponse, @@ -14,61 +14,92 @@ import { } from '../../../common/types'; import { getDefaultHotPhasePolicy } from '../edit_policy/constants'; +type HttpMethod = 'GET' | 'PUT' | 'DELETE' | 'POST'; +export interface ResponseError { + statusCode: number; + message: string | Error; +} + export const init = () => { - const server = fakeServer.create(); - server.respondImmediately = true; - server.respondWith([200, {}, 'DefaultServerResponse']); + let isResponseDelayed = false; + const getDelayResponse = () => isResponseDelayed; + const setDelayResponse = (shouldDelayResponse: boolean) => { + isResponseDelayed = shouldDelayResponse; + }; + + const httpSetup = httpServiceMock.createSetupContract(); + const httpRequestsMockHelpers = registerHttpRequestMockHelpers(httpSetup, getDelayResponse); return { - server, - httpRequestsMockHelpers: registerHttpRequestMockHelpers(server), + httpSetup, + setDelayResponse, + httpRequestsMockHelpers, }; }; -const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { - const setLoadPolicies = (response: any = []) => { - server.respondWith('GET', `${API_BASE_PATH}/policies`, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify(response), - ]); - }; +const registerHttpRequestMockHelpers = ( + httpSetup: ReturnType, + shouldDelayResponse: () => boolean +) => { + const mockResponses = new Map>>( + ['GET', 'PUT', 'DELETE', 'POST'].map( + (method) => [method, new Map()] as [HttpMethod, Map>] + ) + ); - const setLoadSnapshotPolicies = (response: any = [], error?: { status: number; body: any }) => { - const status = error ? error.status : 200; - const body = error ? error.body : response; + const mockMethodImplementation = (method: HttpMethod, path: string) => { + const responsePromise = mockResponses.get(method)?.get(path) ?? Promise.resolve({}); + if (shouldDelayResponse()) { + return new Promise((resolve) => { + setTimeout(() => resolve(responsePromise), 1000); + }); + } - server.respondWith('GET', `${API_BASE_PATH}/snapshot_policies`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); + return responsePromise; }; - const setListNodes = (body: ListNodesRouteResponse) => { - server.respondWith('GET', `${API_BASE_PATH}/nodes/list`, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + httpSetup.get.mockImplementation((path) => + mockMethodImplementation('GET', path as unknown as string) + ); + httpSetup.delete.mockImplementation((path) => + mockMethodImplementation('DELETE', path as unknown as string) + ); + httpSetup.post.mockImplementation((path) => + mockMethodImplementation('POST', path as unknown as string) + ); + httpSetup.put.mockImplementation((path) => + mockMethodImplementation('PUT', path as unknown as string) + ); - const setNodesDetails = (nodeAttributes: string, body: NodesDetailsResponse) => { - server.respondWith('GET', `${API_BASE_PATH}/nodes/${nodeAttributes}/details`, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + const mockResponse = (method: HttpMethod, path: string, response?: unknown, error?: unknown) => { + const defuse = (promise: Promise) => { + promise.catch(() => {}); + return promise; + }; - const setListSnapshotRepos = (body: ListSnapshotReposResponse) => { - server.respondWith('GET', `${API_BASE_PATH}/snapshot_repositories`, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); + return mockResponses + .get(method)! + .set(path, error ? defuse(Promise.reject({ body: error })) : Promise.resolve(response)); }; + const setLoadPolicies = (response: any = [], error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/policies`, response, error); + + const setLoadSnapshotPolicies = (response: any = [], error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/snapshot_policies`, response, error); + + const setListNodes = (response: ListNodesRouteResponse, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/nodes/list`, response, error); + + const setNodesDetails = ( + nodeAttributes: string, + response: NodesDetailsResponse, + error?: ResponseError + ) => mockResponse('GET', `${API_BASE_PATH}/nodes/${nodeAttributes}/details`, response, error); + + const setListSnapshotRepos = (response: ListSnapshotReposResponse, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/snapshot_repositories`, response, error); + const setDefaultResponses = () => { setLoadPolicies([getDefaultHotPhasePolicy()]); setLoadSnapshotPolicies([]); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts index f9cd1f27b72be..6794509f6c84f 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts @@ -7,4 +7,4 @@ export * from './actions'; -export { setupEnvironment } from './setup_environment'; +export { setupEnvironment, WithAppDependencies } from './setup_environment'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.ts deleted file mode 100644 index eb212787fa62f..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.ts +++ /dev/null @@ -1,37 +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 axios from 'axios'; -import axiosXhrAdapter from 'axios/lib/adapters/xhr'; - -import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; -import { notificationServiceMock, fatalErrorsServiceMock } from '@kbn/core/public/mocks'; -import { init as initHttp } from '../../../public/application/services/http'; -import { init as initHttpRequests } from './http_requests'; -import { init as initUiMetric } from '../../../public/application/services/ui_metric'; -import { init as initNotification } from '../../../public/application/services/notification'; - -const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); - -export const setupEnvironment = () => { - initUiMetric(usageCollectionPluginMock.createSetupContract()); - initNotification( - notificationServiceMock.createSetupContract().toasts, - fatalErrorsServiceMock.createSetupContract() - ); - - mockHttpClient.interceptors.response.use(({ data }) => data); - // This expects HttpSetup but we're giving it AxiosInstance. - // @ts-ignore - initHttp(mockHttpClient); - const { server, httpRequestsMockHelpers } = initHttpRequests(); - - return { - server, - httpRequestsMockHelpers, - }; -}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.tsx new file mode 100644 index 0000000000000..dde9368f81294 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { merge } from 'lodash'; + +import './global_mocks'; + +import { HttpSetup } from '@kbn/core/public'; +import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; +import { notificationServiceMock, fatalErrorsServiceMock } from '@kbn/core/public/mocks'; +import { executionContextServiceMock } from '@kbn/core/public/execution_context/execution_context_service.mock'; +import { docLinksServiceMock } from '@kbn/core/public/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; +import { init as initHttp } from '../../../public/application/services/http'; +import { init as initHttpRequests } from './http_requests'; +import { init as initUiMetric } from '../../../public/application/services/ui_metric'; +import { init as initNotification } from '../../../public/application/services/notification'; +import { KibanaContextProvider } from '../../../public/shared_imports'; +import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; + +const breadcrumbService = createBreadcrumbsMock(); +const appContextMock = { + breadcrumbService, + license: licensingMock.createLicense({ license: { type: 'enterprise' } }), + docLinks: docLinksServiceMock.createStartContract(), + getUrlForApp: () => {}, + executionContext: executionContextServiceMock.createSetupContract(), +}; + +export const WithAppDependencies = + (Comp: any, httpSetup: HttpSetup, overrides: Record = {}) => + (props: Record) => { + initHttp(httpSetup); + breadcrumbService.setup(() => ''); + + return ( + + + + ); + }; + +export const setupEnvironment = () => { + initUiMetric(usageCollectionPluginMock.createSetupContract()); + initNotification( + notificationServiceMock.createSetupContract().toasts, + fatalErrorsServiceMock.createSetupContract() + ); + + return initHttpRequests(); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/extend_index_management.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/extend_index_management.test.tsx index a769a310fff5d..07ae90e8bc861 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/extend_index_management.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/extend_index_management.test.tsx @@ -6,9 +6,8 @@ */ import moment from 'moment-timezone'; -import axios from 'axios'; -import axiosXhrAdapter from 'axios/lib/adapters/xhr'; +import { init } from './client_integration/helpers/http_requests'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; import { Index } from '../common/types'; @@ -23,12 +22,9 @@ import { import { init as initHttp } from '../public/application/services/http'; import { init as initUiMetric } from '../public/application/services/ui_metric'; -// We need to init the http with a mock for any tests that depend upon the http service. -// For example, add_lifecycle_confirm_modal makes an API request in its componentDidMount -// lifecycle method. If we don't mock this, CI will fail with "Call retries were exceeded". -// This expects HttpSetup but we're giving it AxiosInstance. -// @ts-ignore -initHttp(axios.create({ adapter: axiosXhrAdapter })); +const { httpSetup } = init(); + +initHttp(httpSetup); initUiMetric(usageCollectionPluginMock.createSetupContract()); jest.mock('@kbn/index-management-plugin/public', async () => { From 275ce8f055c4a325586e71f27a170471fd7e16fb Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 21 Apr 2022 07:02:57 -0600 Subject: [PATCH 5/7] [Maps] update MapLibre to 2.1.latest (#127501) * [Maps] update MapLibre to 2.0 * fix ts erros in sort_layers test * more ts fixes * tslint fixes * replace deprecated properties * revert previous change * get map to fire load event * revert changes to setting and clearing featureState * incorporate type fixes to maplibre * replace GeoJSONFeature with MapGeoJSONFeature * bump to prerelease 3, eslint * vega type fixes * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * update to 2.1.9 release and fix remaining ts errors * upgrade to ems-client 8.3.0 * update license check * cleanup * remove cast to unknown Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 4 +- packages/kbn-mapbox-gl/src/index.ts | 47 +++--- src/dev/license_checker/config.ts | 2 +- .../vega_view/vega_map_view/constants.ts | 4 +- .../vega_view/vega_map_view/view.test.ts | 26 ++-- .../public/vega_view/vega_map_view/view.ts | 19 ++- x-pack/plugins/maps/public/_mapbox_hacks.scss | 12 +- .../ems_vector_tile_layer.tsx | 14 +- .../layers/heatmap_layer/heatmap_layer.ts | 4 +- .../geojson_vector_layer.tsx | 6 +- .../mvt_vector_layer/mvt_vector_layer.tsx | 20 ++- .../layers/vector_layer/vector_layer.tsx | 14 +- .../vector/components/symbol/icon_preview.tsx | 5 +- .../classes/util/mb_filter_expressions.ts | 33 +++-- .../draw_feature_control.tsx | 7 +- .../connected_components/mb_map/mb_map.tsx | 20 ++- .../mb_map/sort_layers.test.ts | 80 +++++----- .../mb_map/sort_layers.ts | 16 +- .../tooltip_control/tooltip_control.test.tsx | 6 +- .../tooltip_control/tooltip_control.tsx | 13 +- .../connected_components/mb_map/utils.ts | 1 + yarn.lock | 140 ++++++++++-------- 22 files changed, 266 insertions(+), 227 deletions(-) diff --git a/package.json b/package.json index 43d47614b7fd2..8a4b8a3e11d4d 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "@elastic/charts": "46.0.1", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.2.0-canary.2", - "@elastic/ems-client": "8.2.0", + "@elastic/ems-client": "8.3.0", "@elastic/eui": "54.0.0", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", @@ -307,7 +307,7 @@ "lru-cache": "^4.1.5", "lz-string": "^1.4.4", "mapbox-gl-draw-rectangle-mode": "1.0.4", - "maplibre-gl": "1.15.2", + "maplibre-gl": "2.1.9", "markdown-it": "^12.3.2", "md5": "^2.1.0", "mdast-util-to-hast": "10.0.1", diff --git a/packages/kbn-mapbox-gl/src/index.ts b/packages/kbn-mapbox-gl/src/index.ts index 3b31aec0011f3..481702b15771b 100644 --- a/packages/kbn-mapbox-gl/src/index.ts +++ b/packages/kbn-mapbox-gl/src/index.ts @@ -8,51 +8,56 @@ import type { Map, + LayerSpecification, + Source, GeoJSONSource, - VectorSource, - Layer, - AnyLayer, - FeatureIdentifier, - Style, - MapboxOptions, + VectorTileSource, + StyleSpecification, + MapEvent, + MapOptions, MapMouseEvent, MapSourceDataEvent, LngLat, LngLatBounds, + Point2D, PointLike, - MapboxGeoJSONFeature, - Point, + MapGeoJSONFeature, CustomLayerInterface, + FilterSpecification, + FeatureIdentifier, } from 'maplibre-gl'; + // @ts-expect-error -import mapboxglDist from 'maplibre-gl/dist/maplibre-gl-csp'; +import maplibreglDist from 'maplibre-gl/dist/maplibre-gl-csp'; // @ts-expect-error import mbRtlPlugin from '!!file-loader!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js'; // @ts-expect-error import mbWorkerUrl from '!!file-loader!maplibre-gl/dist/maplibre-gl-csp-worker'; import 'maplibre-gl/dist/maplibre-gl.css'; -const mapboxgl: any = mapboxglDist; -mapboxgl.workerUrl = mbWorkerUrl; -mapboxgl.setRTLTextPlugin(mbRtlPlugin); +const maplibregl: any = maplibreglDist; +maplibregl.workerUrl = mbWorkerUrl; +maplibregl.setRTLTextPlugin(mbRtlPlugin); -export { mapboxgl }; +export { maplibregl }; export type { Map, + LayerSpecification, + StyleSpecification, + Source, GeoJSONSource, - VectorSource, - Layer, - AnyLayer, - FeatureIdentifier, - Style, - MapboxOptions, + VectorTileSource, + MapEvent, + MapOptions, MapMouseEvent, MapSourceDataEvent, LngLat, LngLatBounds, + Point2D, PointLike, - MapboxGeoJSONFeature, - Point, + MapGeoJSONFeature, CustomLayerInterface, + FilterSpecification, + FeatureIdentifier, }; diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 2a067a54d472e..1cfcb7b5cf284 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -76,7 +76,7 @@ export const DEV_ONLY_LICENSE_ALLOWED = ['MPL-2.0']; export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint - '@elastic/ems-client@8.2.0': ['Elastic License 2.0'], + '@elastic/ems-client@8.3.0': ['Elastic License 2.0'], '@elastic/eui@54.0.0': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry }; diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_map_view/constants.ts b/src/plugins/vis_types/vega/public/vega_view/vega_map_view/constants.ts index 60f699904c965..d219581647612 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_map_view/constants.ts +++ b/src/plugins/vis_types/vega/public/vega_view/vega_map_view/constants.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Style } from '@kbn/mapbox-gl'; +import type { StyleSpecification } from '@kbn/mapbox-gl'; export const vegaLayerId = 'vega'; export const defaultMapConfig = { @@ -15,7 +15,7 @@ export const defaultMapConfig = { tileSize: 256, }; -export const defaultMabBoxStyle: Style = { +export const defaultMabBoxStyle: StyleSpecification = { /** * according to the MapBox documentation that value should be '8' * @see (https://docs.mapbox.com/mapbox-gl-js/style-spec/root/#version) diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.test.ts b/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.test.ts index 6dca45de21835..6c0d693349ef6 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.test.ts +++ b/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.test.ts @@ -29,14 +29,14 @@ import { } from '../../services'; import { initVegaLayer, initTmsRasterLayer } from './layers'; -import { mapboxgl } from '@kbn/mapbox-gl'; +import { maplibregl } from '@kbn/mapbox-gl'; jest.mock('@kbn/mapbox-gl', () => { const zoomTo = jest.fn(); const setCenter = jest.fn(); const fitBounds = jest.fn(); return { - mapboxgl: { + maplibregl: { mocks: { zoomTo, setCenter, @@ -171,7 +171,7 @@ describe('vega_map_view/view', () => { await vegaMapView.init(); const { longitude, latitude, scrollWheelZoom } = vegaMapView._parser.mapConfig; - expect(mapboxgl.Map).toHaveBeenCalledWith({ + expect(maplibregl.Map).toHaveBeenCalledWith({ style: { version: 8, sources: {}, @@ -195,7 +195,7 @@ describe('vega_map_view/view', () => { await vegaMapView.init(); const { longitude, latitude, scrollWheelZoom } = vegaMapView._parser.mapConfig; - expect(mapboxgl.Map).toHaveBeenCalledWith({ + expect(maplibregl.Map).toHaveBeenCalledWith({ style: { version: 8, sources: {}, @@ -217,7 +217,7 @@ describe('vega_map_view/view', () => { const vegaMapView = await createVegaMapView(); await vegaMapView.init(); - expect(mapboxgl.NavigationControl).toHaveBeenCalled(); + expect(maplibregl.NavigationControl).toHaveBeenCalled(); }); describe('setMapView', () => { @@ -225,25 +225,25 @@ describe('vega_map_view/view', () => { beforeEach(async () => { vegaMapView = await createVegaMapView(); await vegaMapView.init(); - mapboxgl.mocks.setCenter.mockReset(); - mapboxgl.mocks.zoomTo.mockReset(); - mapboxgl.mocks.fitBounds.mockReset(); + maplibregl.mocks.setCenter.mockReset(); + maplibregl.mocks.zoomTo.mockReset(); + maplibregl.mocks.fitBounds.mockReset(); }); test('should set just lat lng', async () => { vegaMapView.setMapViewHandler(1, 2); - expect(mapboxgl.mocks.setCenter).toHaveBeenCalledWith({ lat: 1, lng: 2 }); + expect(maplibregl.mocks.setCenter).toHaveBeenCalledWith({ lat: 1, lng: 2 }); }); test('should set just lng lat via array', async () => { vegaMapView.setMapViewHandler([1, 2]); - expect(mapboxgl.mocks.setCenter).toHaveBeenCalledWith({ lat: 2, lng: 1 }); + expect(maplibregl.mocks.setCenter).toHaveBeenCalledWith({ lat: 2, lng: 1 }); }); test('should set lat lng and zoom', async () => { vegaMapView.setMapViewHandler(1, 2, 6); - expect(mapboxgl.mocks.setCenter).toHaveBeenCalledWith({ lat: 1, lng: 2 }); - expect(mapboxgl.mocks.zoomTo).toHaveBeenCalledWith(6); + expect(maplibregl.mocks.setCenter).toHaveBeenCalledWith({ lat: 1, lng: 2 }); + expect(maplibregl.mocks.zoomTo).toHaveBeenCalledWith(6); }); test('should set bounds', async () => { @@ -251,7 +251,7 @@ describe('vega_map_view/view', () => { [1, 2], [6, 7], ]); - expect(mapboxgl.mocks.fitBounds).toHaveBeenCalledWith([ + expect(maplibregl.mocks.fitBounds).toHaveBeenCalledWith([ { lat: 2, lng: 1 }, { lat: 7, lng: 6 }, ]); diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.ts b/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.ts index 2050e0271d03f..fe1d6a27f3605 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.ts +++ b/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.ts @@ -7,11 +7,11 @@ */ import { i18n } from '@kbn/i18n'; -import type { Map, Style, MapboxOptions } from '@kbn/mapbox-gl'; +import type { Map, StyleSpecification, MapOptions } from '@kbn/mapbox-gl'; import { View, parse, expressionFunction } from 'vega'; -import { mapboxgl } from '@kbn/mapbox-gl'; +import { maplibregl } from '@kbn/mapbox-gl'; import { initTmsRasterLayer, initVegaLayer } from './layers'; import { VegaBaseView } from '../vega_base_view'; @@ -76,7 +76,7 @@ export class VegaMapView extends VegaBaseView { return Boolean(this._parser.mapConfig.zoomControl); } - private getMapParams(defaults: { maxZoom: number; minZoom: number }): Partial { + private getMapParams(defaults: { maxZoom: number; minZoom: number }): Partial { const { longitude, latitude, scrollWheelZoom } = this._parser.mapConfig; const { zoom, maxZoom, minZoom } = validateZoomSettings( this._parser.mapConfig, @@ -106,8 +106,8 @@ export class VegaMapView extends VegaBaseView { } private async initMapContainer(vegaView: View) { - let style: Style = defaultMabBoxStyle; - let customAttribution: MapboxOptions['customAttribution'] = []; + let style: StyleSpecification = defaultMabBoxStyle; + let customAttribution: MapOptions['customAttribution'] = []; const zoomSettings = { minZoom: defaultMapConfig.minZoom, maxZoom: defaultMapConfig.maxZoom, @@ -129,7 +129,7 @@ export class VegaMapView extends VegaBaseView { zoomSettings.maxZoom = defaultMapConfig.maxZoom; zoomSettings.minZoom = defaultMapConfig.minZoom; customAttribution = this._serviceSettings.getAttributionsFromTMSServce(tmsService); - style = (await tmsService.getVectorStyleSheet()) as Style; + style = (await tmsService.getVectorStyleSheet()) as StyleSpecification; } else { const config = this._serviceSettings.getTileMapConfig(); customAttribution = config.options.attribution; @@ -140,7 +140,7 @@ export class VegaMapView extends VegaBaseView { // For the correct geration of the PDF/PNG report, we must wait until the map is fully rendered. return new Promise((resolve) => { - const mapBoxInstance = new mapboxgl.Map({ + const mapBoxInstance = new maplibregl.Map({ style, customAttribution, container: this._$container.get(0), @@ -171,7 +171,10 @@ export class VegaMapView extends VegaBaseView { private initControls(mapBoxInstance: Map) { if (this.shouldShowZoomControl) { - mapBoxInstance.addControl(new mapboxgl.NavigationControl({ showCompass: false }), 'top-left'); + mapBoxInstance.addControl( + new maplibregl.NavigationControl({ showCompass: false }), + 'top-left' + ); } // disable map rotation using right click + drag diff --git a/x-pack/plugins/maps/public/_mapbox_hacks.scss b/x-pack/plugins/maps/public/_mapbox_hacks.scss index 480232007995d..118e0b1862066 100644 --- a/x-pack/plugins/maps/public/_mapbox_hacks.scss +++ b/x-pack/plugins/maps/public/_mapbox_hacks.scss @@ -3,12 +3,12 @@ .mapContainer { flex-grow: 1; - .mapboxgl-ctrl-top-left .mapboxgl-ctrl { + .maplibregl-ctrl-top-left .maplibregl-ctrl { margin-left: $euiSizeM; margin-top: $euiSizeM; } - .mapboxgl-ctrl-group:not(:empty) { + .maplibregl-ctrl-group:not(:empty) { @include euiBottomShadowLarge; @include mapToolbarButtonGroupBorderRadius; background-color: $euiColorEmptyShade; @@ -27,7 +27,7 @@ } } - .mapboxgl-ctrl button:not(:disabled) { + .maplibregl-ctrl button:not(:disabled) { transition: background $euiAnimSpeedNormal ease-in-out; &:hover { @@ -35,14 +35,14 @@ } } - .mapboxgl-ctrl-group button:focus:focus-visible { + .maplibregl-ctrl-group button:focus:focus-visible { box-shadow: none; } } // Custom SVG as background for zoom controls based off of EUI glyphs plusInCircleFilled and minusInCircleFilled // Also fixes dark mode -.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon { +.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon { // sass-lint:disable-block no-important background-repeat: no-repeat !important; // sass-lint:disable-block quotes @@ -50,7 +50,7 @@ background-position: center !important; } -.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon { +.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon { // sass-lint:disable-block no-important background-repeat: no-repeat !important; // sass-lint:disable-block quotes diff --git a/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx index c6d594617c448..428156165c4c3 100644 --- a/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import type { Map as MbMap, Layer as MbLayer, Style as MbStyle } from '@kbn/mapbox-gl'; +import type { Map as MbMap, LayerSpecification, StyleSpecification } from '@kbn/mapbox-gl'; import _ from 'lodash'; // @ts-expect-error import { RGBAImage } from './image_utils'; @@ -38,7 +38,7 @@ export interface EmsSpriteSheet { interface SourceRequestData { spriteSheetImageData?: ImageData; - vectorStyleSheet?: MbStyle; + vectorStyleSheet?: StyleSpecification; spriteMeta?: { png: string; json: EmsSpriteSheet; @@ -141,7 +141,7 @@ export class EmsVectorTileLayer extends AbstractLayer { return `${this._generateMbSourceIdPrefix()}${name}`; } - _getVectorStyle() { + _getVectorStyle(): StyleSpecification | null | undefined { const sourceDataRequest = this.getSourceDataRequest(); if (!sourceDataRequest) { return null; @@ -317,8 +317,8 @@ export class EmsVectorTileLayer extends AbstractLayer { const newLayerObject = { ...layer, source: this._generateMbSourceId( - typeof (layer as MbLayer).source === 'string' - ? ((layer as MbLayer).source as string) + 'source' in layer && typeof layer.source === 'string' + ? (layer.source as string) : undefined ), id: mbLayerId, @@ -375,7 +375,7 @@ export class EmsVectorTileLayer extends AbstractLayer { return []; } - _setOpacityForType(mbMap: MbMap, mbLayer: MbLayer, mbLayerId: string) { + _setOpacityForType(mbMap: MbMap, mbLayer: LayerSpecification, mbLayerId: string) { this._getOpacityProps(mbLayer.type).forEach((opacityProp) => { const mbPaint = mbLayer.paint as { [key: string]: unknown } | undefined; if (mbPaint && typeof mbPaint[opacityProp] === 'number') { @@ -387,7 +387,7 @@ export class EmsVectorTileLayer extends AbstractLayer { }); } - _setLayerZoomRange(mbMap: MbMap, mbLayer: MbLayer, mbLayerId: string) { + _setLayerZoomRange(mbMap: MbMap, mbLayer: LayerSpecification, mbLayerId: string) { let minZoom = this.getMinZoom(); if (typeof mbLayer.minzoom === 'number') { minZoom = Math.max(minZoom, mbLayer.minzoom); diff --git a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts index a977ed10fdade..66aef6d38803f 100644 --- a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Map as MbMap, VectorSource as MbVectorSource } from '@kbn/mapbox-gl'; +import type { Map as MbMap, VectorTileSource } from '@kbn/mapbox-gl'; import { AbstractLayer } from '../layer'; import { HeatmapStyle } from '../../styles/heatmap/heatmap_style'; import { LAYER_TYPE } from '../../../../common/constants'; @@ -97,7 +97,7 @@ export class HeatmapLayer extends AbstractLayer { } _requiresPrevSourceCleanup(mbMap: MbMap): boolean { - const mbSource = mbMap.getSource(this.getMbSourceId()) as MbVectorSource; + const mbSource = mbMap.getSource(this.getMbSourceId()) as VectorTileSource; if (!mbSource) { return false; } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx index 18972e89607e4..c6f9a282d0fa4 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiIcon } from '@elastic/eui'; import { Feature, FeatureCollection } from 'geojson'; -import type { Map as MbMap, GeoJSONSource as MbGeoJSONSource } from '@kbn/mapbox-gl'; +import type { FilterSpecification, Map as MbMap, GeoJSONSource } from '@kbn/mapbox-gl'; import { EMPTY_FEATURE_COLLECTION, FEATURE_VISIBLE_PROPERTY_NAME, @@ -160,7 +160,7 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer { this._setMbLinePolygonProperties(mbMap, undefined, timesliceMaskConfig); } - _getJoinFilterExpression(): unknown | undefined { + _getJoinFilterExpression(): FilterSpecification | undefined { return this.hasJoins() ? // Remove unjoined source features by filtering out features without GeoJSON feature.property[FEATURE_VISIBLE_PROPERTY_NAME] is true ['==', ['get', FEATURE_VISIBLE_PROPERTY_NAME], true] @@ -168,7 +168,7 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer { } _syncFeatureCollectionWithMb(mbMap: MbMap) { - const mbGeoJSONSource = mbMap.getSource(this.getId()) as MbGeoJSONSource; + const mbGeoJSONSource = mbMap.getSource(this.getId()) as GeoJSONSource; const featureCollection = this._getSourceFeatureCollection(); const featureCollectionOnMap = AbstractLayer.getBoundDataForSource(mbMap, this.getId()); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx index 4d15cddcf44db..a7a190c1c5b56 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx @@ -8,10 +8,10 @@ import _ from 'lodash'; import type { FeatureIdentifier, + FilterSpecification, Map as MbMap, - AnyLayer as MbLayer, - GeoJSONSource as MbGeoJSONSource, - VectorSource as MbVectorSource, + LayerSpecification, + VectorTileSource, } from '@kbn/mapbox-gl'; import { Feature } from 'geojson'; import { i18n } from '@kbn/i18n'; @@ -302,7 +302,7 @@ export class MvtVectorLayer extends AbstractVectorLayer { return this.makeMbLayerId('toomanyfeatures'); } - _getJoinFilterExpression(): unknown | undefined { + _getJoinFilterExpression(): FilterSpecification | undefined { const { join, joinPropertiesMap } = this._getJoinResults(); if (!join) { return undefined; @@ -321,13 +321,13 @@ export class MvtVectorLayer extends AbstractVectorLayer { return joinKeys.length ? // Unable to check FEATURE_VISIBLE_PROPERTY_NAME flag since filter expressions do not support feature-state // Instead, remove unjoined source features by filtering out features without matching join keys - [ + ([ 'match', ['get', join.getLeftField().getName()], joinKeys, true, // match value false, // fallback - value with no match - ] + ] as FilterSpecification) : hideAllFilter; } @@ -408,7 +408,7 @@ export class MvtVectorLayer extends AbstractVectorLayer { const tooManyFeaturesLayerId = this._getMbTooManyFeaturesLayerId(); if (!mbMap.getLayer(tooManyFeaturesLayerId)) { - const mbTooManyFeaturesLayer: MbLayer = { + const mbTooManyFeaturesLayer: LayerSpecification = { id: tooManyFeaturesLayerId, type: 'line', source: this.getId(), @@ -448,7 +448,7 @@ export class MvtVectorLayer extends AbstractVectorLayer { // Must remove/add vector source to update source attributes. // _requiresPrevSourceCleanup returns true when vector source needs to be removed so it can be re-added with updated attributes _requiresPrevSourceCleanup(mbMap: MbMap): boolean { - const mbSource = mbMap.getSource(this.getMbSourceId()) as MbVectorSource | MbGeoJSONSource; + const mbSource = mbMap.getSource(this.getMbSourceId()); if (!mbSource) { return false; } @@ -456,7 +456,7 @@ export class MvtVectorLayer extends AbstractVectorLayer { // Expected source is not compatible, so remove. return true; } - const mbTileSource = mbSource as MbVectorSource; + const mbTileSource = mbSource as VectorTileSource; const sourceDataRequest = this.getSourceDataRequest(); if (!sourceDataRequest) { @@ -484,9 +484,7 @@ export class MvtVectorLayer extends AbstractVectorLayer { // but the programmable JS-object uses camelcase `sourceLayer` if ( mbLayer && - // @ts-expect-error mbLayer.sourceLayer !== sourceData.tileSourceLayer && - // @ts-expect-error mbLayer.sourceLayer !== ES_MVT_META_LAYER_NAME ) { // If the source-pointer of one of the layers is stale, they will all be stale. diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index 23c76924e911e..1b178d204f6e2 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -7,7 +7,7 @@ import React from 'react'; import uuid from 'uuid/v4'; -import type { Map as MbMap, AnyLayer as MbLayer } from '@kbn/mapbox-gl'; +import type { FilterSpecification, Map as MbMap, LayerSpecification } from '@kbn/mapbox-gl'; import type { Query } from '@kbn/data-plugin/common'; import { Feature, GeoJsonProperties, Geometry, Position } from 'geojson'; import _ from 'lodash'; @@ -673,7 +673,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { } } - _getJoinFilterExpression(): unknown | undefined { + _getJoinFilterExpression(): FilterSpecification | undefined { return undefined; } @@ -698,7 +698,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { if (this.getCurrentStyle().arePointsSymbolizedAsCircles()) { markerLayerId = pointLayerId; if (!pointLayer) { - const mbLayer: MbLayer = { + const mbLayer: LayerSpecification = { id: pointLayerId, type: 'circle', source: sourceId, @@ -716,7 +716,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { } else { markerLayerId = symbolLayerId; if (!symbolLayer) { - const mbLayer: MbLayer = { + const mbLayer: LayerSpecification = { id: symbolLayerId, type: 'symbol', source: sourceId, @@ -768,7 +768,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { const lineLayerId = this._getMbLineLayerId(); if (!mbMap.getLayer(fillLayerId)) { - const mbLayer: MbLayer = { + const mbLayer: LayerSpecification = { id: fillLayerId, type: 'fill', source: sourceId, @@ -780,7 +780,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { mbMap.addLayer(mbLayer, labelLayerId); } if (!mbMap.getLayer(lineLayerId)) { - const mbLayer: MbLayer = { + const mbLayer: LayerSpecification = { id: lineLayerId, type: 'line', source: sourceId, @@ -824,7 +824,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { const labelLayerId = this._getMbLabelLayerId(); const labelLayer = mbMap.getLayer(labelLayerId); if (!labelLayer) { - const mbLayer: MbLayer = { + const mbLayer: LayerSpecification = { id: labelLayerId, type: 'symbol', source: this.getId(), diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_preview.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_preview.tsx index 385e86cbfd693..d93be52dcc4d4 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_preview.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_preview.tsx @@ -16,7 +16,7 @@ import { EuiTitle, EuiToolTip, } from '@elastic/eui'; -import { mapboxgl, Map as MapboxMap } from '@kbn/mapbox-gl'; +import { maplibregl, Map as MapboxMap } from '@kbn/mapbox-gl'; import { i18n } from '@kbn/i18n'; import { ResizeChecker } from '@kbn/kibana-utils-plugin/public'; import { @@ -94,7 +94,6 @@ export class IconPreview extends Component { } const imageData = await createSdfIcon({ svg, cutoff, radius }); if (map.hasImage(IconPreview.iconId)) { - // @ts-expect-error map.updateImage(IconPreview.iconId, imageData); } else { map.addImage(IconPreview.iconId, imageData, { @@ -128,7 +127,7 @@ export class IconPreview extends Component { } _createMapInstance(): MapboxMap { - const map = new mapboxgl.Map({ + const map = new maplibregl.Map({ container: this._containerRef!, interactive: false, center: [0, 0], diff --git a/x-pack/plugins/maps/public/classes/util/mb_filter_expressions.ts b/x-pack/plugins/maps/public/classes/util/mb_filter_expressions.ts index 025d1d830c87d..2f25dc84fe224 100644 --- a/x-pack/plugins/maps/public/classes/util/mb_filter_expressions.ts +++ b/x-pack/plugins/maps/public/classes/util/mb_filter_expressions.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { FilterSpecification } from '@kbn/mapbox-gl'; import { GEO_JSON_TYPE, KBN_IS_CENTROID_FEATURE } from '../../../common/constants'; import { Timeslice } from '../../../common/descriptor_types'; @@ -14,14 +15,18 @@ export interface TimesliceMaskConfig { timeslice: Timeslice; } -export const EXCLUDE_CENTROID_FEATURES = ['!=', ['get', KBN_IS_CENTROID_FEATURE], true]; +export const EXCLUDE_CENTROID_FEATURES = [ + '!=', + ['get', KBN_IS_CENTROID_FEATURE], + true, +] as FilterSpecification; function getFilterExpression( - filters: unknown[], - joinFilter?: unknown, + filters: FilterSpecification[], + joinFilter?: FilterSpecification, timesliceMaskConfig?: TimesliceMaskConfig -) { - const allFilters: unknown[] = [...filters]; +): FilterSpecification { + const allFilters: FilterSpecification[] = [...filters]; if (joinFilter) { allFilters.push(joinFilter); @@ -45,9 +50,9 @@ function getFilterExpression( } export function getFillFilterExpression( - joinFilter?: unknown, + joinFilter?: FilterSpecification, timesliceMaskConfig?: TimesliceMaskConfig -): unknown[] { +): FilterSpecification { return getFilterExpression( [ // explicit EXCLUDE_CENTROID_FEATURES filter not needed. Centroids are points and are filtered out by geometry narrowing @@ -63,9 +68,9 @@ export function getFillFilterExpression( } export function getLineFilterExpression( - joinFilter?: unknown, + joinFilter?: FilterSpecification, timesliceMaskConfig?: TimesliceMaskConfig -): unknown[] { +): FilterSpecification { return getFilterExpression( [ // explicit EXCLUDE_CENTROID_FEATURES filter not needed. Centroids are points and are filtered out by geometry narrowing @@ -89,9 +94,9 @@ const IS_POINT_FEATURE = [ ]; export function getPointFilterExpression( - joinFilter?: unknown, + joinFilter?: FilterSpecification, timesliceMaskConfig?: TimesliceMaskConfig -): unknown[] { +): FilterSpecification { return getFilterExpression( [EXCLUDE_CENTROID_FEATURES, IS_POINT_FEATURE], joinFilter, @@ -101,10 +106,10 @@ export function getPointFilterExpression( export function getLabelFilterExpression( isSourceGeoJson: boolean, - joinFilter?: unknown, + joinFilter?: FilterSpecification, timesliceMaskConfig?: TimesliceMaskConfig -): unknown[] { - const filters: unknown[] = []; +): FilterSpecification { + const filters: FilterSpecification[] = []; if (isSourceGeoJson) { // Centroid feature added to GeoJSON feature collection for LINE_STRING, MULTI_LINE_STRING, POLYGON, MULTI_POLYGON, and GEOMETRY_COLLECTION geometries diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx index b6ffacc491030..df70d4b7cfd9b 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx @@ -6,14 +6,13 @@ */ import React, { Component } from 'react'; -import { Map as MbMap, Point as MbPoint } from '@kbn/mapbox-gl'; // @ts-expect-error import MapboxDraw from '@mapbox/mapbox-gl-draw'; import { Feature, Geometry, Position } from 'geojson'; import { i18n } from '@kbn/i18n'; // @ts-expect-error import * as jsts from 'jsts'; -import { MapMouseEvent } from '@kbn/mapbox-gl'; +import type { Map as MbMap, MapMouseEvent, PointLike } from '@kbn/mapbox-gl'; import { getToasts } from '../../../../kibana_services'; import { DrawControl } from '../draw_control'; import { DRAW_MODE, DRAW_SHAPE } from '../../../../../common/constants'; @@ -84,7 +83,7 @@ export class DrawFeatureControl extends Component { }; _onClick = async (event: MapMouseEvent, drawControl?: MapboxDraw) => { - const mbLngLatPoint: MbPoint = event.point; + const mbLngLatPoint: PointLike = event.point; // Currently feature deletion is the only onClick handling if (!this.props.editLayer || this.props.drawShape !== DRAW_SHAPE.DELETE) { return; @@ -103,7 +102,7 @@ export class DrawFeatureControl extends Component { x: mbLngLatPoint.x + PADDING, y: mbLngLatPoint.y + PADDING, }, - ] as [MbPoint, MbPoint]; + ] as [PointLike, PointLike]; const selectedFeatures = this.props.mbMap.queryRenderedFeatures(mbBbox, { layers: mbEditLayerIds, filter: ['all', EXCLUDE_CENTROID_FEATURES], diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx index 8c430647cbbd3..7dda17442e5e1 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx @@ -10,9 +10,8 @@ import React, { Component } from 'react'; import { Adapters } from '@kbn/inspector-plugin/public'; import { Filter } from '@kbn/es-query'; import { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; - -import { mapboxgl } from '@kbn/mapbox-gl'; -import type { Map as MapboxMap, MapboxOptions, MapMouseEvent } from '@kbn/mapbox-gl'; +import { maplibregl } from '@kbn/mapbox-gl'; +import type { Map as MapboxMap, MapOptions, MapMouseEvent } from '@kbn/mapbox-gl'; import { ResizeChecker } from '@kbn/kibana-utils-plugin/public'; import { DrawFilterControl } from './draw_control/draw_filter_control'; import { ScaleControl } from './scale_control'; @@ -92,7 +91,7 @@ export class MbMap extends Component { private _prevDisableInteractive?: boolean; private _prevLayerList?: ILayer[]; private _prevTimeslice?: Timeslice; - private _navigationControl = new mapboxgl.NavigationControl({ showCompass: false }); + private _navigationControl = new maplibregl.NavigationControl({ showCompass: false }); private _tileStatusTracker?: TileStatusTracker; state: State = { @@ -176,13 +175,13 @@ export class MbMap extends Component { async _createMbMapInstance(initialView: MapCenterAndZoom | null): Promise { return new Promise((resolve) => { const mbStyle = { - version: 8, + version: 8 as 8, sources: {}, layers: [], glyphs: getGlyphUrl(), }; - const options: MapboxOptions = { + const options: MapOptions = { attributionControl: false, container: this._containerRef!, style: mbStyle, @@ -200,7 +199,7 @@ export class MbMap extends Component { } else { options.bounds = [-170, -60, 170, 75]; } - const mbMap = new mapboxgl.Map(options); + const mbMap = new maplibregl.Map(options); mbMap.dragRotate.disable(); mbMap.touchZoomRotate.disableRotation(); @@ -329,12 +328,12 @@ export class MbMap extends Component { if (goto.bounds) { // clamping ot -89/89 latitudes since Mapboxgl does not seem to handle bounds that contain the poles (logs errors to the console when using -90/90) - const lnLatBounds = new mapboxgl.LngLatBounds( - new mapboxgl.LngLat( + const lnLatBounds = new maplibregl.LngLatBounds( + new maplibregl.LngLat( clampToLonBounds(goto.bounds.minLon), clampToLatBounds(goto.bounds.minLat) ), - new mapboxgl.LngLat( + new maplibregl.LngLat( clampToLonBounds(goto.bounds.maxLon), clampToLatBounds(goto.bounds.maxLat) ) @@ -418,7 +417,6 @@ export class MbMap extends Component { for (const { symbolId, svg, cutoff, radius } of this.props.customIcons) { createSdfIcon({ svg, renderSize: CUSTOM_ICON_SIZE, cutoff, radius }).then( (imageData: ImageData) => { - // @ts-expect-error MapboxMap type is missing updateImage method if (mbMap.hasImage(symbolId)) mbMap.updateImage(symbolId, imageData); else mbMap.addImage(symbolId, imageData, { diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts index 74a57bd76ab8f..77ce71472df95 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts @@ -8,7 +8,7 @@ /* eslint-disable max-classes-per-file */ import _ from 'lodash'; -import type { Map as MbMap, AnyLayer as MbLayer, Style as MbStyle } from '@kbn/mapbox-gl'; +import type { Map as MbMap, LayerSpecification, StyleSpecification } from '@kbn/mapbox-gl'; import { getIsTextLayer, syncLayerOrder } from './sort_layers'; import { SPATIAL_FILTERS_LAYER_ID } from '../../../common/constants'; import { ILayer } from '../../classes/layers/layer'; @@ -16,9 +16,9 @@ import { ILayer } from '../../classes/layers/layer'; let moveCounter = 0; class MockMbMap { - private _style: MbStyle; + private _style: StyleSpecification; - constructor(style: MbStyle) { + constructor(style: StyleSpecification) { this._style = _.cloneDeep(style); } @@ -88,24 +88,24 @@ test('getIsTextLayer', () => { id: `mylayer_text`, type: 'symbol', paint: { 'text-color': 'red' }, - } as MbLayer; + } as LayerSpecification; expect(getIsTextLayer(paintLabelMbLayer)).toBe(true); const layoutLabelMbLayer = { id: `mylayer_text`, type: 'symbol', layout: { 'text-size': 'red' }, - } as MbLayer; + } as unknown as LayerSpecification; expect(getIsTextLayer(layoutLabelMbLayer)).toBe(true); const iconMbLayer = { id: `mylayer_text`, type: 'symbol', paint: { 'icon-color': 'house' }, - } as MbLayer; + } as LayerSpecification; expect(getIsTextLayer(iconMbLayer)).toBe(false); - const circleMbLayer = { id: `mylayer_text`, type: 'circle' } as MbLayer; + const circleMbLayer = { id: `mylayer_text`, type: 'circle' } as LayerSpecification; expect(getIsTextLayer(circleMbLayer)).toBe(false); }); @@ -128,22 +128,23 @@ describe('sortLayer', () => { // Initial order that styles are added to mapbox is non-deterministic and depends on the order of data fetches. test('Should sort initial layer load order to expected order', () => { const initialMbStyle = { - version: 0, + version: 8 as 8, layers: [ - { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as MbLayer, - { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as MbLayer, - { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as MbLayer, - { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as MbLayer, - { id: `gl-draw-polygon-fill-active.cold`, type: 'fill' } as MbLayer, + { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as LayerSpecification, + { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, + { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as LayerSpecification, + { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, + { id: `gl-draw-polygon-fill-active.cold`, type: 'fill' } as LayerSpecification, { id: `${CHARLIE_LAYER_ID}_text`, type: 'symbol', paint: { 'text-color': 'red' }, - } as MbLayer, - { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as MbLayer, - { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as MbLayer, - { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + } as LayerSpecification, + { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as LayerSpecification, + { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as LayerSpecification, + { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, ], + sources: {}, }; const mbMap = new MockMbMap(initialMbStyle); syncLayerOrder(mbMap as unknown as MbMap, spatialFilterLayer, mapLayers); @@ -167,21 +168,22 @@ describe('sortLayer', () => { // Test case testing when layer is moved in Table of Contents test('Should sort single layer single move to expected order', () => { const initialMbStyle = { - version: 0, + version: 8 as 8, layers: [ - { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as MbLayer, - { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as MbLayer, - { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as MbLayer, - { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as MbLayer, - { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as LayerSpecification, + { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as LayerSpecification, + { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, + { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as LayerSpecification, + { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, { id: `${CHARLIE_LAYER_ID}_text`, type: 'symbol', paint: { 'text-color': 'red' }, - } as MbLayer, - { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as MbLayer, - { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + } as LayerSpecification, + { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as LayerSpecification, + { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, ], + sources: {}, }; const mbMap = new MockMbMap(initialMbStyle); syncLayerOrder(mbMap as unknown as MbMap, spatialFilterLayer, mapLayers); @@ -205,11 +207,12 @@ describe('sortLayer', () => { test('Should sort with missing mblayers to expected order', () => { // Notice there are no bravo mbLayers in initial style. const initialMbStyle = { - version: 0, + version: 8 as 8, layers: [ - { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as MbLayer, - { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as LayerSpecification, + { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, ], + sources: {}, }; const mbMap = new MockMbMap(initialMbStyle); syncLayerOrder(mbMap as unknown as MbMap, spatialFilterLayer, mapLayers); @@ -222,21 +225,22 @@ describe('sortLayer', () => { test('Should not call move layers when layers are in expected order', () => { const initialMbStyle = { - version: 0, + version: 8 as 8, layers: [ - { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as MbLayer, - { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as MbLayer, - { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as MbLayer, - { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as MbLayer, - { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as LayerSpecification, + { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as LayerSpecification, + { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, + { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as LayerSpecification, + { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, { id: `${CHARLIE_LAYER_ID}_text`, type: 'symbol', paint: { 'text-color': 'red' }, - } as MbLayer, - { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as MbLayer, - { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + } as LayerSpecification, + { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as LayerSpecification, + { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as LayerSpecification, ], + sources: {}, }; const mbMap = new MockMbMap(initialMbStyle); syncLayerOrder(mbMap as unknown as MbMap, spatialFilterLayer, mapLayers); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts index ef248380af36a..b7f27755ad9cf 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts @@ -5,14 +5,14 @@ * 2.0. */ -import type { Map as MbMap, Layer as MbLayer } from '@kbn/mapbox-gl'; +import type { Map as MbMap, LayerSpecification } from '@kbn/mapbox-gl'; import { ILayer } from '../../classes/layers/layer'; // "Layer" is overloaded and can mean the following // 1) Map layer (ILayer): A single map layer consists of one to many mapbox layers. -// 2) Mapbox layer (MbLayer): Individual unit of rendering such as text, circles, polygons, or lines. +// 2) Mapbox layer (LayerSpecification): Individual unit of rendering such as text, circles, polygons, or lines. -export function getIsTextLayer(mbLayer: MbLayer) { +export function getIsTextLayer(mbLayer: LayerSpecification) { if (mbLayer.type !== 'symbol') { return false; } @@ -35,7 +35,7 @@ export function isGlDrawLayer(mbLayerId: string) { function doesMbLayerBelongToMapLayerAndClass( mapLayer: ILayer, - mbLayer: MbLayer, + mbLayer: LayerSpecification, layerClass: LAYER_CLASS ) { if (!mapLayer.ownsMbLayerId(mbLayer.id)) { @@ -58,7 +58,7 @@ enum LAYER_CLASS { function moveMapLayer( mbMap: MbMap, - mbLayers: MbLayer[], + mbLayers: LayerSpecification[], mapLayer: ILayer, layerClass: LAYER_CLASS, beneathMbLayerId?: string @@ -72,7 +72,11 @@ function moveMapLayer( }); } -function getBottomMbLayerId(mbLayers: MbLayer[], mapLayer: ILayer, layerClass: LAYER_CLASS) { +function getBottomMbLayerId( + mbLayers: LayerSpecification[], + mapLayer: ILayer, + layerClass: LAYER_CLASS +) { const bottomMbLayer = mbLayers.find((mbLayer) => { return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); }); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx index a018fe0a4a200..eafa2c66b6d50 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx @@ -15,12 +15,12 @@ import sinon from 'sinon'; import React from 'react'; import { mount, shallow } from 'enzyme'; import { Feature } from 'geojson'; -import type { Map as MbMap, MapMouseEvent, MapboxGeoJSONFeature } from '@kbn/mapbox-gl'; +import type { Map as MbMap, MapMouseEvent, MapGeoJSONFeature } from '@kbn/mapbox-gl'; import { TooltipControl } from './tooltip_control'; import { IVectorLayer } from '../../../classes/layers/vector_layer'; // mutable map state -let featuresAtLocation: MapboxGeoJSONFeature[] = []; +let featuresAtLocation: MapGeoJSONFeature[] = []; const layerId = 'tfi3f'; const mbLayerId = 'tfi3f_circle'; @@ -254,7 +254,7 @@ describe('TooltipControl', () => { properties: { __kbn__feature_id__: 1, }, - } as unknown as MapboxGeoJSONFeature; + } as unknown as MapGeoJSONFeature; featuresAtLocation = [feature, feature]; mount( { } _getTooltipFeatures( - mbFeatures: MapboxGeoJSONFeature[], + mbFeatures: MapGeoJSONFeature[], isLocked: boolean, tooltipId: string ): TooltipFeature[] { @@ -340,7 +341,7 @@ export class TooltipControl extends Component { }); } - _getMbFeaturesUnderPointer(mbLngLatPoint: MbPoint) { + _getMbFeaturesUnderPointer(mbLngLatPoint: Point2D) { if (!this.props.mbMap) { return []; } @@ -356,7 +357,7 @@ export class TooltipControl extends Component { x: mbLngLatPoint.x + PADDING, y: mbLngLatPoint.y + PADDING, }, - ] as [MbPoint, MbPoint]; + ] as [PointLike, PointLike]; return this.props.mbMap.queryRenderedFeatures(mbBbox, { layers: mbLayerIds, }); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/utils.ts b/x-pack/plugins/maps/public/connected_components/mb_map/utils.ts index a79c1a1f71b76..5fc9fd62b20c2 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/utils.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/utils.ts @@ -66,6 +66,7 @@ export function getTileMetaFeatures(mbMap: MbMap, mbSourceId: string): TileMetaF // Tile meta will never have duplicated features since by there nature, tile meta is a feature contained within a single tile const mbFeatures = mbMap.querySourceFeatures(mbSourceId, { sourceLayer: ES_MVT_META_LAYER_NAME, + filter: [], }); return mbFeatures diff --git a/yarn.lock b/yarn.lock index a1c9373e08175..ed9e850e8c6c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1504,10 +1504,10 @@ "@elastic/transport" "^8.0.2" tslib "^2.3.0" -"@elastic/ems-client@8.2.0": - version "8.2.0" - resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-8.2.0.tgz#35ca17f07a576c464b15a17ef9b228a51043e329" - integrity sha512-NtU/KjTMGntnrCY6+2jdkugn6pqMJj2EV4/Mff/MGlBLrWSlxYkYa6Q6KFhmT3V68RJUxOsId47mUdoQbRi/yg== +"@elastic/ems-client@8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-8.3.0.tgz#9d40c02e33c407d433b8e509d83c5edec24c4902" + integrity sha512-DlJDyUQzNrxGbS0AWxGiBNfq1hPQUP3Ib/Zyotgv7+VGGklb0mBwppde7WLVvuj0E+CYc6E63TJsoD8KNUO0MQ== dependencies: "@types/geojson" "^7946.0.7" "@types/lru-cache" "^5.1.0" @@ -3437,10 +3437,13 @@ concat-stream "~2.0.0" minimist "^1.2.5" -"@mapbox/geojson-types@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz#9aecf642cb00eab1080a57c4f949a65b4a5846d6" - integrity sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw== +"@mapbox/geojson-rewind@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.1.tgz#adbe16dc683eb40e90934c51a5e28c7bbf44f4e1" + integrity sha512-eL7fMmfTBKjrb+VFHXCGv9Ot0zc3C0U+CwXo1IrP+EPwDczLoXv34Tgq3y+2mPSFNVUXgU42ILWJTC7145KPTA== + dependencies: + get-stream "^6.0.1" + minimist "^1.2.5" "@mapbox/hast-util-table-cell-style@^0.1.3": version "0.1.3" @@ -3472,25 +3475,25 @@ resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-rtl-text/-/mapbox-gl-rtl-text-0.2.3.tgz#a26ecfb3f0061456d93ee8570dd9587d226ea8bd" integrity sha512-RaCYfnxULUUUxNwcUimV9C/o2295ktTyLEUzD/+VWkqXqvaVfFcZ5slytGzb2Sd/Jj4MlbxD0DCZbfa6CzcmMw== -"@mapbox/mapbox-gl-supported@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz#f60b6a55a5d8e5ee908347d2ce4250b15103dc8e" - integrity sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg== +"@mapbox/mapbox-gl-supported@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz#c15367178d8bfe4765e6b47b542fe821ce259c7b" + integrity sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ== "@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" integrity sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI= -"@mapbox/tiny-sdf@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-1.1.1.tgz#16a20c470741bfe9191deb336f46e194da4a91ff" - integrity sha512-Ihn1nZcGIswJ5XGbgFAvVumOgWpvIjBX9jiRlIl46uQG9vJOF51ViBYHF95rEZupuyQbEmhLaDPLQlU7fUTsBg== +"@mapbox/tiny-sdf@^2.0.4": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.5.tgz#cdba698d3d65087643130f9af43a2b622ce0b372" + integrity sha512-OhXt2lS//WpLdkqrzo/KwB7SRD8AiNTFFzuo9n14IBupzIMa67yGItcK7I2W9D8Ghpa4T04Sw9FWsKCJG50Bxw== -"@mapbox/unitbezier@^0.0.0": - version "0.0.0" - resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e" - integrity sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4= +"@mapbox/unitbezier@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz#d32deb66c7177e9e9dfc3bbd697083e2e657ff01" + integrity sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw== "@mapbox/vector-tile@1.3.1", "@mapbox/vector-tile@^1.3.1": version "1.3.1" @@ -5707,6 +5710,11 @@ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.7.tgz#c8fa532b60a0042219cdf173ca21a975ef0666ad" integrity sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ== +"@types/geojson@^7946.0.8": + version "7946.0.8" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca" + integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA== + "@types/getos@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/getos/-/getos-3.0.0.tgz#582c758e99e9d634f31f471faf7ce59cf1c39a71" @@ -6394,12 +6402,12 @@ resolved "https://registry.yarnpkg.com/@types/lz-string/-/lz-string-1.3.34.tgz#69bfadde419314b4a374bf2c8e58659c035ed0a5" integrity sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow== -"@types/mapbox__point-geometry@*": +"@types/mapbox__point-geometry@*", "@types/mapbox__point-geometry@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz#488a9b76e8457d6792ea2504cdd4ecdd9860a27e" integrity sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA== -"@types/mapbox__vector-tile@1.3.0": +"@types/mapbox__vector-tile@1.3.0", "@types/mapbox__vector-tile@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz#8fa1379dbaead1e1b639b8d96cfd174404c379d6" integrity sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g== @@ -6638,7 +6646,7 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== -"@types/pbf@*", "@types/pbf@3.0.2": +"@types/pbf@*", "@types/pbf@3.0.2", "@types/pbf@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.2.tgz#8d291ad68b4b8c533e96c174a2e3e6399a59ed61" integrity sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ== @@ -12951,10 +12959,10 @@ each-props@^1.3.0: is-plain-object "^2.0.1" object.defaults "^1.1.0" -earcut@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.2.tgz#41b0bc35f63e0fe80da7cddff28511e7e2e80d11" - integrity sha512-eZoZPPJcUHnfRZ0PjLvx2qBordSiO8ofC3vt+qACLM95u+4DovnbYNpQtJh0DNsWj8RnxrQytD4WA8gj5cRIaQ== +earcut@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.3.tgz#d44ced2ff5a18859568e327dd9c7d46b16f55cf4" + integrity sha512-iRDI1QeCQIhMCZk48DRDMVgQSSBDmbzzNhnxIo+pwx3swkfjMh6vh0nWLq1NdvGHLKH6wIrAM3vQWeTj6qeoug== ecc-jsbn@~0.1.1: version "0.1.2" @@ -15193,6 +15201,11 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -15262,7 +15275,12 @@ github-slugger@^1.0.0: dependencies: emoji-regex ">=6.0.0 <=6.1.1" -gl-matrix@^3.2.1, gl-matrix@~3.3.0: +gl-matrix@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.4.3.tgz#fc1191e8320009fd4d20e9339595c6041ddc22c9" + integrity sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA== + +gl-matrix@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.3.0.tgz#232eef60b1c8b30a28cbbe75b2caf6c48fd6358b" integrity sha512-COb7LDz+SXaHtl/h4LeaFcNdJdAQSDeVqjiIihSXNrkWObZLhDI4hIkZC11Aeqp7bcE72clzB0BnDXr2SmslRA== @@ -15652,11 +15670,6 @@ graphql@^16.3.0: resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05" integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A== -grid-index@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/grid-index/-/grid-index-1.1.0.tgz#97f8221edec1026c8377b86446a7c71e79522ea7" - integrity sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA== - growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" @@ -19713,34 +19726,34 @@ mapcap@^1.0.0: resolved "https://registry.yarnpkg.com/mapcap/-/mapcap-1.0.0.tgz#e8e29d04a160eaf8c92ec4bcbd2c5d07ed037e5a" integrity sha512-KcNlZSlFPx+r1jYZmxEbTVymG+dIctf10WmWkuhrhrblM+KMoF77HelwihL5cxYlORye79KoR4IlOOk99lUJ0g== -maplibre-gl@1.15.2: - version "1.15.2" - resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-1.15.2.tgz#7fb47868b62455af916c090903f2154394450f9c" - integrity sha512-uPeV530apb4JfX3cRFfE+awFnbcJTOnCv2QvY4mw4huiInbybElWYkNzTs324YLSADq0f4bidRoYcR81ho3aLA== +maplibre-gl@2.1.9: + version "2.1.9" + resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-2.1.9.tgz#042f3ef4224fa890ecf7a410145243f1fc943dcd" + integrity sha512-pnWJmILeZpgA5QSI7K7xFK3yrkyYTd9srw3fCi2Ca52Phm78hsznPwUErEQcZLfxXKn/1h9t8IPdj0TH0NBNbg== dependencies: - "@mapbox/geojson-rewind" "^0.5.0" - "@mapbox/geojson-types" "^1.0.2" + "@mapbox/geojson-rewind" "^0.5.1" "@mapbox/jsonlint-lines-primitives" "^2.0.2" - "@mapbox/mapbox-gl-supported" "^1.5.0" + "@mapbox/mapbox-gl-supported" "^2.0.1" "@mapbox/point-geometry" "^0.1.0" - "@mapbox/tiny-sdf" "^1.1.1" - "@mapbox/unitbezier" "^0.0.0" + "@mapbox/tiny-sdf" "^2.0.4" + "@mapbox/unitbezier" "^0.0.1" "@mapbox/vector-tile" "^1.3.1" "@mapbox/whoots-js" "^3.1.0" + "@types/geojson" "^7946.0.8" + "@types/mapbox__point-geometry" "^0.1.2" + "@types/mapbox__vector-tile" "^1.3.0" + "@types/pbf" "^3.0.2" csscolorparser "~1.0.3" - earcut "^2.2.2" + earcut "^2.2.3" geojson-vt "^3.2.1" - gl-matrix "^3.2.1" - grid-index "^1.1.0" - minimist "^1.2.5" + gl-matrix "^3.4.3" murmurhash-js "^1.0.0" pbf "^3.2.1" - potpack "^1.0.1" + potpack "^1.0.2" quickselect "^2.0.0" - rw "^1.3.3" - supercluster "^7.1.0" + supercluster "^7.1.4" tinyqueue "^2.0.3" - vt-pbf "^3.1.1" + vt-pbf "^3.1.3" marge@^1.0.1: version "1.0.1" @@ -22969,10 +22982,10 @@ postcss@^7.0.1, postcss@^7.0.27, postcss@^7.0.35, postcss@^7.0.36: picocolors "^0.2.1" source-map "^0.6.1" -potpack@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.1.tgz#d1b1afd89e4c8f7762865ec30bd112ab767e2ebf" - integrity sha512-15vItUAbViaYrmaB/Pbw7z6qX2xENbFSTA7Ii4tgbPtasxm5v6ryKhKtL91tpWovDJzTiZqdwzhcFBCwiMVdVw== +potpack@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14" + integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ== preact-render-to-string@^5.1.19: version "5.1.19" @@ -25465,7 +25478,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rw@1, rw@^1.3.2, rw@^1.3.3: +rw@1, rw@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= @@ -27310,10 +27323,10 @@ superagent@^3.8.2: qs "^6.5.1" readable-stream "^2.3.5" -supercluster@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.0.tgz#f0a457426ec0ab95d69c5f03b51e049774b94479" - integrity sha512-LDasImUAFMhTqhK+cUXfy9C2KTUqJ3gucLjmNLNFmKWOnDUBxLFLH9oKuXOTCLveecmxh8fbk8kgh6Q0gsfe2w== +supercluster@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.4.tgz#6762aabfd985d3390b49f13b815567d5116a828a" + integrity sha512-GhKkRM1jMR6WUwGPw05fs66pOFWhf59lXq+Q3J3SxPvhNcmgOtLRV6aVQPMRsmXdpaeFJGivt+t7QXUPL3ff4g== dependencies: kdbush "^3.0.0" @@ -29716,6 +29729,15 @@ vt-pbf@^3.1.1: "@mapbox/vector-tile" "^1.3.1" pbf "^3.0.5" +vt-pbf@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac" + integrity sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA== + dependencies: + "@mapbox/point-geometry" "0.1.0" + "@mapbox/vector-tile" "^1.3.1" + pbf "^3.2.1" + w3c-hr-time@^1.0.1, w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" From 551759859eb1e08a9dfa3e9d663ed291989e883e Mon Sep 17 00:00:00 2001 From: mgiota Date: Thu, 21 Apr 2022 15:31:37 +0200 Subject: [PATCH 6/7] remove autorefresh button from o11y rules page (#130526) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/pages/rules/index.tsx | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/rules/index.tsx b/x-pack/plugins/observability/public/pages/rules/index.tsx index 03e8ca86b2fa2..b161625379fd8 100644 --- a/x-pack/plugins/observability/public/pages/rules/index.tsx +++ b/x-pack/plugins/observability/public/pages/rules/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useMemo, useEffect } from 'react'; +import React, { useState, useMemo } from 'react'; import { capitalize, sortBy } from 'lodash'; import { EuiButton, @@ -15,10 +15,8 @@ import { EuiButtonEmpty, EuiText, EuiHorizontalRule, - EuiAutoRefreshButton, EuiTableSortingType, EuiFieldSearch, - OnRefreshChangeProps, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -93,8 +91,6 @@ export function RulesPage() { }); const [inputText, setInputText] = useState(); const [searchText, setSearchText] = useState(); - const [refreshInterval, setRefreshInterval] = useState(60000); - const [isPaused, setIsPaused] = useState(false); const [ruleLastResponseFilter, setRuleLastResponseFilter] = useState([]); const [typesFilter, setTypesFilter] = useState([]); const [currentRuleToEdit, setCurrentRuleToEdit] = useState(null); @@ -108,14 +104,6 @@ export function RulesPage() { setCurrentRuleToEdit(ruleItem); }; - const onRefreshChange = ({ - isPaused: isPausedChanged, - refreshInterval: refreshIntervalChanged, - }: OnRefreshChangeProps) => { - setIsPaused(isPausedChanged); - setRefreshInterval(refreshIntervalChanged); - }; - const { rulesState, setRulesState, reload, noData, initialLoad } = useFetchRules({ searchText, ruleLastResponseFilter, @@ -160,15 +148,6 @@ export function RulesPage() { (ruleType) => ruleType.authorizedConsumers[ALERTS_FEATURE_ID]?.all ); - useEffect(() => { - const interval = setInterval(() => { - if (!isPaused) { - reload(); - } - }, refreshInterval); - return () => clearInterval(interval); - }, [refreshInterval, reload, isPaused]); - useBreadcrumbs([ { text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', { @@ -368,14 +347,6 @@ export function RulesPage() { /> - - - From a3fd86c22a3f65b659b592e735e990900349d688 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Thu, 21 Apr 2022 10:00:52 -0400 Subject: [PATCH 7/7] [Workplace Search] New Network Drive, Outlook, Teams, and Zoom Server integration tiles (#130421) --- .../apis/custom_integration/integrations.ts | 2 +- .../assets/source_icons/network_drive.svg | 1 + .../public/assets/source_icons/outlook.svg | 1 + .../public/assets/source_icons/teams.svg | 1 + .../public/assets/source_icons/zoom.svg | 1 + .../enterprise_search/server/integrations.ts | 59 +++++++++++++++++++ 6 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/enterprise_search/public/assets/source_icons/network_drive.svg create mode 100644 x-pack/plugins/enterprise_search/public/assets/source_icons/outlook.svg create mode 100644 x-pack/plugins/enterprise_search/public/assets/source_icons/teams.svg create mode 100644 x-pack/plugins/enterprise_search/public/assets/source_icons/zoom.svg diff --git a/test/api_integration/apis/custom_integration/integrations.ts b/test/api_integration/apis/custom_integration/integrations.ts index e71cc045da321..c4fda918328f8 100644 --- a/test/api_integration/apis/custom_integration/integrations.ts +++ b/test/api_integration/apis/custom_integration/integrations.ts @@ -22,7 +22,7 @@ export default function ({ getService }: FtrProviderContext) { expect(resp.body).to.be.an('array'); - expect(resp.body.length).to.be(38); + expect(resp.body.length).to.be(42); // Test for sample data card expect(resp.body.findIndex((c: { id: string }) => c.id === 'sample_data_all')).to.be.above( diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/network_drive.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/network_drive.svg new file mode 100644 index 0000000000000..cc07fbbc50877 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/network_drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/outlook.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/outlook.svg new file mode 100644 index 0000000000000..932a3756a522b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/outlook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/teams.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/teams.svg new file mode 100644 index 0000000000000..bf3aef9277eb6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/teams.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/zoom.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/zoom.svg new file mode 100644 index 0000000000000..d7498817bc62d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/zoom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/server/integrations.ts b/x-pack/plugins/enterprise_search/server/integrations.ts index 2a5ed4be69cfa..d2d3b5d4d6829 100644 --- a/x-pack/plugins/enterprise_search/server/integrations.ts +++ b/x-pack/plugins/enterprise_search/server/integrations.ts @@ -162,6 +162,21 @@ const workplaceSearchIntegrations: WorkplaceSearchIntegration[] = [ ), categories: ['productivity'], }, + { + id: 'network_drive', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.networkDriveName', { + defaultMessage: 'Network Drive', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.networkDriveDescription', + { + defaultMessage: + 'Search over your files and folders stored on network drives with Enterprise Search.', + } + ), + categories: ['enterprise_search', 'file_storage'], + uiInternalPath: '/app/enterprise_search/workplace_search/sources/add/network_drive', + }, { id: 'onedrive', title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.onedriveName', { @@ -176,6 +191,20 @@ const workplaceSearchIntegrations: WorkplaceSearchIntegration[] = [ categories: ['file_storage'], uiInternalPath: '/app/enterprise_search/workplace_search/sources/add/one_drive', }, + { + id: 'outlook', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.outlookName', { + defaultMessage: 'Outlook', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.outlookDescription', + { + defaultMessage: 'Search over your email and calendars with Enterprise Search.', + } + ), + categories: ['enterprise_search', 'microsoft_365', 'communications', 'productivity'], + uiInternalPath: '/app/enterprise_search/workplace_search/sources/add/outlook', + }, { id: 'salesforce', title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.salesforceName', { @@ -266,6 +295,21 @@ const workplaceSearchIntegrations: WorkplaceSearchIntegration[] = [ ), categories: ['communications'], }, + { + id: 'teams', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.teamsName', { + defaultMessage: 'Teams', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.teamsDescription', + { + defaultMessage: + 'Search over meeting recordings, chats and other communications with Enterprise Search.', + } + ), + categories: ['enterprise_search', 'microsoft_365', 'communications', 'productivity'], + uiInternalPath: '/app/enterprise_search/workplace_search/sources/add/teams', + }, { id: 'zendesk', title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.zendeskName', { @@ -279,6 +323,21 @@ const workplaceSearchIntegrations: WorkplaceSearchIntegration[] = [ ), categories: ['communications'], }, + { + id: 'zoom', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.zoomName', { + defaultMessage: 'Zoom', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.zoomDescription', + { + defaultMessage: + 'Search over meeting recordings, chats and other communications with Enterprise Search.', + } + ), + categories: ['enterprise_search', 'communications', 'productivity'], + uiInternalPath: '/app/enterprise_search/workplace_search/sources/add/zoom', + }, { id: 'custom_api_source', title: i18n.translate(