From 70a1b3892a6d2a3d57cc9851b276087c29b6e1e3 Mon Sep 17 00:00:00 2001 From: Orhan Toy Date: Thu, 2 Dec 2021 15:52:41 +0100 Subject: [PATCH 01/65] [App Search, Crawler] Use consistent casing for validation step titles (#120064) --- .../components/add_domain/add_domain_logic.test.ts | 12 ++++++------ .../components/add_domain/add_domain_validation.tsx | 2 +- .../crawler/components/add_domain/utils.ts | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/add_domain_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/add_domain_logic.test.ts index 4b229b3687174..466ccc61838f0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/add_domain_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/add_domain_logic.test.ts @@ -507,16 +507,16 @@ describe('AddDomainLogic', () => { networkConnectivity: { state: 'invalid', message: - 'Unable to establish a network connection because the "Initial Validation" check failed.', + 'Unable to establish a network connection because the "Initial validation" check failed.', }, indexingRestrictions: { state: 'invalid', message: - 'Unable to determine indexing restrictions because the "Network Connectivity" check failed.', + 'Unable to determine indexing restrictions because the "Network connectivity" check failed.', }, contentVerification: { state: 'invalid', - message: 'Unable to verify content because the "Indexing Restrictions" check failed.', + message: 'Unable to verify content because the "Indexing restrictions" check failed.', }, }); }); @@ -602,16 +602,16 @@ describe('AddDomainLogic', () => { networkConnectivity: { state: 'invalid', message: - 'Unable to establish a network connection because the "Initial Validation" check failed.', + 'Unable to establish a network connection because the "Initial validation" check failed.', }, indexingRestrictions: { state: 'invalid', message: - 'Unable to determine indexing restrictions because the "Network Connectivity" check failed.', + 'Unable to determine indexing restrictions because the "Network connectivity" check failed.', }, contentVerification: { state: 'invalid', - message: 'Unable to verify content because the "Indexing Restrictions" check failed.', + message: 'Unable to verify content because the "Indexing restrictions" check failed.', }, }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/add_domain_validation.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/add_domain_validation.tsx index 8840b8d355864..817c43df4f8b6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/add_domain_validation.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/add_domain_validation.tsx @@ -86,7 +86,7 @@ export const AddDomainValidation: React.FC = () => { label={i18n.translate( 'xpack.enterpriseSearch.appSearch.crawler.addDomainForm.contentVerificationLabel', { - defaultMessage: 'Content Verification', + defaultMessage: 'Content verification', } )} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/utils.ts index e5886a726f261..1f744d5eabf9b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/utils.ts @@ -82,7 +82,7 @@ const allFailureResultChanges: CrawlerDomainValidationResultChange = { 'xpack.enterpriseSearch.appSearch.crawler.addDomainForm.networkConnectivityFailureMessage', { defaultMessage: - 'Unable to establish a network connection because the "Initial Validation" check failed.', + 'Unable to establish a network connection because the "Initial validation" check failed.', } ), }, @@ -92,7 +92,7 @@ const allFailureResultChanges: CrawlerDomainValidationResultChange = { 'xpack.enterpriseSearch.appSearch.crawler.addDomainForm.indexingRestrictionsFailureMessage', { defaultMessage: - 'Unable to determine indexing restrictions because the "Network Connectivity" check failed.', + 'Unable to determine indexing restrictions because the "Network connectivity" check failed.', } ), }, @@ -102,7 +102,7 @@ const allFailureResultChanges: CrawlerDomainValidationResultChange = { 'xpack.enterpriseSearch.appSearch.crawler.addDomainForm.contentVerificationFailureMessage', { defaultMessage: - 'Unable to verify content because the "Indexing Restrictions" check failed.', + 'Unable to verify content because the "Indexing restrictions" check failed.', } ), }, From 8b762138091c4608b66cba2ffa63ab58f9481d6b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 2 Dec 2021 17:11:06 +0200 Subject: [PATCH 02/65] [Storybook] Fixes Storybook is endlessly refreshed. (#120216) --- packages/kbn-storybook/src/lib/theme_switcher.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-storybook/src/lib/theme_switcher.tsx b/packages/kbn-storybook/src/lib/theme_switcher.tsx index c06e57035819f..3d6f7999545a0 100644 --- a/packages/kbn-storybook/src/lib/theme_switcher.tsx +++ b/packages/kbn-storybook/src/lib/theme_switcher.tsx @@ -20,7 +20,7 @@ export function ThemeSwitcher() { const [globals, updateGlobals] = useGlobals(); const selectedTheme = globals.euiTheme; - if (!selectedTheme || selectedTheme === defaultTheme) { + if (!selectedTheme) { updateGlobals({ euiTheme: defaultTheme }); } From 482aae27686946364c3e6c0b11ea995223a557ce Mon Sep 17 00:00:00 2001 From: Kuldeep M Date: Thu, 2 Dec 2021 15:49:45 +0000 Subject: [PATCH 03/65] Workplace Search fix overview page spacing (#120224) --- .../views/content_sources/components/overview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx index a33f5ec90e3a0..fd29b5f590967 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx @@ -257,6 +257,7 @@ export const Overview: React.FC = () => { const groupsSummary = ( <> +
{GROUP_ACCESS_TITLE}
@@ -479,7 +480,6 @@ export const Overview: React.FC = () => { const syncTriggerCallout = ( -
{SOURCE_SYNCHRONIZATION_TITLE}
From 2c4dfde1740ea3456ce905cb8dcb48a4234e7b44 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 2 Dec 2021 08:01:01 -0800 Subject: [PATCH 04/65] [Fleet] Re-enable registry version check (#120045) * Re-enable registry version check * Update registry image * Use dockerized registry for base x-pack api integration and functional tests * Revert "Use dockerized registry for base x-pack api integration and functional tests" This reverts commit 2fd4ec17d486a584e729a277c96bca3b66f8971f. --- x-pack/plugins/apm/ftr_e2e/ftr_config_run.ts | 2 +- x-pack/plugins/fleet/server/index.ts | 3 +-- x-pack/plugins/fleet/server/services/epm/registry/index.ts | 3 +-- x-pack/test/fleet_api_integration/config.ts | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/apm/ftr_e2e/ftr_config_run.ts b/x-pack/plugins/apm/ftr_e2e/ftr_config_run.ts index a5a0b52e3fbef..8fc87b49a6607 100644 --- a/x-pack/plugins/apm/ftr_e2e/ftr_config_run.ts +++ b/x-pack/plugins/apm/ftr_e2e/ftr_config_run.ts @@ -13,7 +13,7 @@ import { packageRegistryPort } from './ftr_config'; import { FtrProviderContext } from './ftr_provider_context'; export const dockerImage = - 'docker.elastic.co/package-registry/distribution@sha256:13d9996dd24161624784704e080f5f5b7f0ef34ff0d9259f8f05010ccae00058'; + 'docker.elastic.co/package-registry/distribution@sha256:de952debe048d903fc73e8a4472bb48bb95028d440cba852f21b863d47020c61'; async function ftrConfigRun({ readConfigFile }: FtrConfigProviderContext) { const kibanaConfig = await readConfigFile(require.resolve('./ftr_config.ts')); diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 9fc0edd0b7cf8..8cbfa311081d2 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -94,8 +94,7 @@ export const config: PluginConfigDescriptor = { outputs: PreconfiguredOutputsSchema, agentIdVerificationEnabled: schema.boolean({ defaultValue: true }), developer: schema.object({ - // TODO: change default to false as soon as EPR issue fixed. Blocker for 8.0. - disableRegistryVersionCheck: schema.boolean({ defaultValue: true }), + disableRegistryVersionCheck: schema.boolean({ defaultValue: false }), }), }), }; diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index ff829eca5ec18..1b6e28a07f8e0 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -138,9 +138,8 @@ export async function fetchFile(filePath: string): Promise { } function setKibanaVersion(url: URL) { - // TODO: change default to false as soon as EPR issue fixed. Blocker for 8.0. const disableVersionCheck = - appContextService.getConfig()?.developer?.disableRegistryVersionCheck ?? true; + appContextService.getConfig()?.developer?.disableRegistryVersionCheck ?? false; if (disableVersionCheck) { return; } diff --git a/x-pack/test/fleet_api_integration/config.ts b/x-pack/test/fleet_api_integration/config.ts index 308c4ab66f15c..fb9dc7b6b4ce6 100644 --- a/x-pack/test/fleet_api_integration/config.ts +++ b/x-pack/test/fleet_api_integration/config.ts @@ -15,7 +15,7 @@ import { defineDockerServersConfig } from '@kbn/test'; // example: https://beats-ci.elastic.co/blue/organizations/jenkins/Ingest-manager%2Fpackage-storage/detail/snapshot/74/pipeline/257#step-302-log-1. // It should be updated any time there is a new Docker image published for the Snapshot Distribution of the Package Registry. export const dockerImage = - 'docker.elastic.co/package-registry/distribution@sha256:13d9996dd24161624784704e080f5f5b7f0ef34ff0d9259f8f05010ccae00058'; + 'docker.elastic.co/package-registry/distribution@sha256:de952debe048d903fc73e8a4472bb48bb95028d440cba852f21b863d47020c61'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); From fcb1b9d08a0b02d8a3709f770549702a43454ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Thu, 2 Dec 2021 17:12:24 +0100 Subject: [PATCH 05/65] [Security Solution] [Endpoint] Adds new artifact hook to remove policies from endpoint artifacts (#120085) * Adds new artifact hook to remove policies from endpoint artifacts. Removed dedicated one for Trusted Apps and updated test * Removes trusted app hook * Add comment, remove unused async statements and fix wrong test name * removes unused import Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../endpoint/endpoint_app_context_services.ts | 5 +- .../fleet_integration.test.ts | 56 +++++-------- .../fleet_integration/fleet_integration.ts | 10 +-- .../handlers/remove_policy_from_artifacts.ts | 79 +++++++++++++++++++ .../remove_policy_from_trusted_apps.ts | 61 -------------- 5 files changed, 104 insertions(+), 107 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_artifacts.ts delete mode 100644 x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_trusted_apps.ts diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 79436c66d073f..5d6dbeca3e0c6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -114,10 +114,7 @@ export class EndpointAppContextService { dependencies.registerIngestCallback( 'postPackagePolicyDelete', - getPackagePolicyDeleteCallback( - dependencies.exceptionListsClient, - dependencies.config.experimentalFeatures - ) + getPackagePolicyDeleteCallback(dependencies.exceptionListsClient) ); } } diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts index 71c093e0781b0..2a4c59f94c8c6 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts @@ -40,11 +40,8 @@ import { getMockArtifacts, toArtifactRecords } from '../endpoint/lib/artifacts/m import { Manifest } from '../endpoint/lib/artifacts'; import { NewPackagePolicy } from '../../../fleet/common/types/models'; import { ManifestSchema } from '../../common/endpoint/schema/manifest'; -import { - allowedExperimentalValues, - ExperimentalFeatures, -} from '../../common/experimental_features'; import { DeletePackagePoliciesResponse } from '../../../fleet/common'; +import { ARTIFACT_LISTS_IDS_TO_REMOVE } from './handlers/remove_policy_from_artifacts'; describe('ingest_integration tests ', () => { let endpointAppContextMock: EndpointAppContextServiceStartContract; @@ -286,44 +283,41 @@ describe('ingest_integration tests ', () => { }); }); - describe('package policy delete callback with trusted apps by policy enabled', () => { - const invokeDeleteCallback = async ( - experimentalFeatures?: ExperimentalFeatures - ): Promise => { - const callback = getPackagePolicyDeleteCallback(exceptionListClient, experimentalFeatures); + describe('package policy delete callback', () => { + const invokeDeleteCallback = async (): Promise => { + const callback = getPackagePolicyDeleteCallback(exceptionListClient); await callback(deletePackagePolicyMock()); }; let removedPolicies: DeletePackagePoliciesResponse; let policyId: string; - let fakeTA: ExceptionListSchema; + let fakeArtifact: ExceptionListSchema; beforeEach(() => { removedPolicies = deletePackagePolicyMock(); policyId = removedPolicies[0].id; - fakeTA = { + fakeArtifact = { ...getExceptionListSchemaMock(), tags: [`policy:${policyId}`], }; - exceptionListClient.findExceptionListItem = jest + exceptionListClient.findExceptionListsItem = jest .fn() - .mockResolvedValueOnce({ data: [fakeTA], total: 1 }); + .mockResolvedValueOnce({ data: [fakeArtifact], total: 1 }); exceptionListClient.updateExceptionListItem = jest .fn() - .mockResolvedValueOnce({ ...fakeTA, tags: [] }); + .mockResolvedValueOnce({ ...fakeArtifact, tags: [] }); }); - it('removes policy from trusted app FF enabled', async () => { - await invokeDeleteCallback({ - ...allowedExperimentalValues, - trustedAppsByPolicyEnabled: true, // Needs to be enabled, it needs also a test with this disabled. - }); + it('removes policy from artifact', async () => { + await invokeDeleteCallback(); - expect(exceptionListClient.findExceptionListItem).toHaveBeenCalledWith({ - filter: `exception-list-agnostic.attributes.tags:"policy:${policyId}"`, - listId: 'endpoint_trusted_apps', - namespaceType: 'agnostic', + expect(exceptionListClient.findExceptionListsItem).toHaveBeenCalledWith({ + listId: ARTIFACT_LISTS_IDS_TO_REMOVE, + filter: ARTIFACT_LISTS_IDS_TO_REMOVE.map( + () => `exception-list-agnostic.attributes.tags:"policy:${policyId}"` + ), + namespaceType: ARTIFACT_LISTS_IDS_TO_REMOVE.map(() => 'agnostic'), page: 1, perPage: 50, sortField: undefined, @@ -331,21 +325,11 @@ describe('ingest_integration tests ', () => { }); expect(exceptionListClient.updateExceptionListItem).toHaveBeenCalledWith({ - ...fakeTA, - namespaceType: fakeTA.namespace_type, - osTypes: fakeTA.os_types, + ...fakeArtifact, + namespaceType: fakeArtifact.namespace_type, + osTypes: fakeArtifact.os_types, tags: [], }); }); - - it("doesn't remove policy from trusted app if feature flag is disabled", async () => { - await invokeDeleteCallback({ - ...allowedExperimentalValues, - trustedAppsByPolicyEnabled: false, // since it was changed to `true` by default - }); - - expect(exceptionListClient.findExceptionListItem).toHaveBeenCalledTimes(0); - expect(exceptionListClient.updateExceptionListItem).toHaveBeenCalledTimes(0); - }); }); }); diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts index a53d5d43de524..4c54dcb0f7725 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts @@ -17,7 +17,6 @@ import { import { NewPackagePolicy, UpdatePackagePolicy } from '../../../fleet/common'; import { NewPolicyData, PolicyConfig } from '../../common/endpoint/types'; -import { ExperimentalFeatures } from '../../common/experimental_features'; import { LicenseService } from '../../common/license'; import { ManifestManager } from '../endpoint/services'; import { IRequestContextFactory } from '../request_context_factory'; @@ -25,7 +24,7 @@ import { installPrepackagedRules } from './handlers/install_prepackaged_rules'; import { createPolicyArtifactManifest } from './handlers/create_policy_artifact_manifest'; import { createDefaultPolicy } from './handlers/create_default_policy'; import { validatePolicyAgainstLicense } from './handlers/validate_policy_against_license'; -import { removePolicyFromTrustedApps } from './handlers/remove_policy_from_trusted_apps'; +import { removePolicyFromArtifacts } from './handlers/remove_policy_from_artifacts'; const isEndpointPackagePolicy = ( packagePolicy: T @@ -131,8 +130,7 @@ export const getPackagePolicyUpdateCallback = ( }; export const getPackagePolicyDeleteCallback = ( - exceptionsClient: ExceptionListClient | undefined, - experimentalFeatures: ExperimentalFeatures | undefined + exceptionsClient: ExceptionListClient | undefined ): PostPackagePolicyDeleteCallback => { return async (deletePackagePolicy): Promise => { if (!exceptionsClient) { @@ -140,8 +138,8 @@ export const getPackagePolicyDeleteCallback = ( } const policiesToRemove: Array> = []; for (const policy of deletePackagePolicy) { - if (isEndpointPackagePolicy(policy) && experimentalFeatures?.trustedAppsByPolicyEnabled) { - policiesToRemove.push(removePolicyFromTrustedApps(exceptionsClient, policy)); + if (isEndpointPackagePolicy(policy)) { + policiesToRemove.push(removePolicyFromArtifacts(exceptionsClient, policy)); } } await Promise.all(policiesToRemove); diff --git a/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_artifacts.ts b/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_artifacts.ts new file mode 100644 index 0000000000000..57a23d677e014 --- /dev/null +++ b/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_artifacts.ts @@ -0,0 +1,79 @@ +/* + * 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 pMap from 'p-map'; + +import { + ENDPOINT_TRUSTED_APPS_LIST_ID, + ENDPOINT_EVENT_FILTERS_LIST_ID, + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, +} from '@kbn/securitysolution-list-constants'; +import { ExceptionListClient } from '../../../../lists/server'; +import { PostPackagePolicyDeleteCallback } from '../../../../fleet/server'; + +export const ARTIFACT_LISTS_IDS_TO_REMOVE = [ + ENDPOINT_TRUSTED_APPS_LIST_ID, + ENDPOINT_EVENT_FILTERS_LIST_ID, + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, +]; + +/** + * Removes policy from artifacts + */ +export const removePolicyFromArtifacts = async ( + exceptionsClient: ExceptionListClient, + policy: Parameters[0][0] +) => { + let page = 1; + + const findArtifactsByPolicy = (currentPage: number) => { + return exceptionsClient.findExceptionListsItem({ + listId: ARTIFACT_LISTS_IDS_TO_REMOVE, + filter: ARTIFACT_LISTS_IDS_TO_REMOVE.map( + () => `exception-list-agnostic.attributes.tags:"policy:${policy.id}"` + ), + namespaceType: ARTIFACT_LISTS_IDS_TO_REMOVE.map(() => 'agnostic'), + page: currentPage, + perPage: 50, + sortField: undefined, + sortOrder: undefined, + }); + }; + + let findResponse = await findArtifactsByPolicy(page); + if (!findResponse) { + return; + } + const artifacts = findResponse.data; + + while (findResponse && (artifacts.length < findResponse.total || findResponse.data.length)) { + page += 1; + findResponse = await findArtifactsByPolicy(page); + if (findResponse) { + artifacts.push(...findResponse.data); + } + } + + await pMap( + artifacts, + (artifact) => + exceptionsClient.updateExceptionListItem({ + ...artifact, + itemId: artifact.item_id, + namespaceType: artifact.namespace_type, + osTypes: artifact.os_types, + tags: artifact.tags.filter((currentPolicy) => currentPolicy !== `policy:${policy.id}`), + }), + { + /** Number of concurrent executions till the end of the artifacts array */ + concurrency: 5, + /** When set to false, instead of stopping when a promise rejects, it will wait for all the promises to + * settle and then reject with an aggregated error containing all the errors from the rejected promises. */ + stopOnError: false, + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_trusted_apps.ts b/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_trusted_apps.ts deleted file mode 100644 index 88af71508f33a..0000000000000 --- a/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_trusted_apps.ts +++ /dev/null @@ -1,61 +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 { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants'; -import { ExceptionListClient } from '../../../../lists/server'; -import { PostPackagePolicyDeleteCallback } from '../../../../fleet/server'; - -/** - * Removes policy from trusted apps - */ -export const removePolicyFromTrustedApps = async ( - exceptionsClient: ExceptionListClient, - policy: Parameters[0][0] -) => { - let page = 1; - - const findTrustedAppsByPolicy = async (currentPage: number) => { - return exceptionsClient.findExceptionListItem({ - listId: ENDPOINT_TRUSTED_APPS_LIST_ID, - filter: `exception-list-agnostic.attributes.tags:"policy:${policy.id}"`, - namespaceType: 'agnostic', - page: currentPage, - perPage: 50, - sortField: undefined, - sortOrder: undefined, - }); - }; - - let findResponse = await findTrustedAppsByPolicy(page); - if (!findResponse) { - return; - } - const trustedApps = findResponse.data; - - while (findResponse && (trustedApps.length < findResponse.total || findResponse.data.length)) { - page += 1; - findResponse = await findTrustedAppsByPolicy(page); - if (findResponse) { - trustedApps.push(...findResponse.data); - } - } - - const updates = []; - for (const trustedApp of trustedApps) { - updates.push( - exceptionsClient.updateExceptionListItem({ - ...trustedApp, - itemId: trustedApp.item_id, - namespaceType: trustedApp.namespace_type, - osTypes: trustedApp.os_types, - tags: trustedApp.tags.filter((currentPolicy) => currentPolicy !== `policy:${policy.id}`), - }) - ); - } - - await Promise.all(updates); -}; From d874c4c798f2faaf4219d7568f0c900325fb94bd Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Thu, 2 Dec 2021 09:42:51 -0700 Subject: [PATCH 06/65] Removes tech debt from export all (#120170) ## Summary See: https://github.com/elastic/kibana/issues/110903 This removes the `export *` from: * lists plugin This also adds `import type` and `export type` in a few areas and fixes the `LicenseType` by changing it from `server` to using the version from `common` to remove the restricted paths. This extra addition prevents more memory leaks when we run jest. --- x-pack/plugins/lists/public/index.ts | 6 +- .../security/common/licensing/index.mock.ts | 6 +- .../add_exception_modal/index.test.tsx | 65 +++++++++++-------- .../exceptions/add_exception_modal/index.tsx | 5 +- .../edit_exception_modal/index.test.tsx | 60 ++++++++--------- .../exceptions/edit_exception_modal/index.tsx | 4 +- .../view/components/form/index.tsx | 8 +-- .../security_solution/public/plugin.tsx | 2 +- .../public/shared_imports.ts | 2 - .../plugins/security_solution/public/types.ts | 64 +++++++++--------- 10 files changed, 109 insertions(+), 113 deletions(-) diff --git a/x-pack/plugins/lists/public/index.ts b/x-pack/plugins/lists/public/index.ts index 977b7f462777e..f35de2328714b 100644 --- a/x-pack/plugins/lists/public/index.ts +++ b/x-pack/plugins/lists/public/index.ts @@ -5,10 +5,8 @@ * 2.0. */ -// TODO: https://github.com/elastic/kibana/issues/110903 -/* eslint-disable @kbn/eslint/no_export_all */ - -export * from './shared_exports'; +export { getExceptionBuilderComponentLazy } from './exceptions/components/builder/index'; +export type { OnChangeProps } from './exceptions/components/builder/index'; import type { PluginInitializerContext } from '../../../../src/core/public'; diff --git a/x-pack/plugins/security/common/licensing/index.mock.ts b/x-pack/plugins/security/common/licensing/index.mock.ts index bd85df3dea638..f9e6a0b756d00 100644 --- a/x-pack/plugins/security/common/licensing/index.mock.ts +++ b/x-pack/plugins/security/common/licensing/index.mock.ts @@ -7,10 +7,8 @@ import { of } from 'rxjs'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LICENSE_TYPE } from '../../../licensing/server'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { LicenseType } from '../../../licensing/server'; +import type { LicenseType } from '../../../licensing/common/types'; +import { LICENSE_TYPE } from '../../../licensing/common/types'; import type { SecurityLicenseFeatures } from './license_features'; import type { SecurityLicense } from './license_service'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx index ac70d5276beb9..0e118d527c69b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx @@ -12,7 +12,7 @@ import { waitFor } from '@testing-library/react'; import { AddExceptionModal } from './'; import { useCurrentUser } from '../../../../common/lib/kibana'; -import { ExceptionBuilder } from '../../../../shared_imports'; +import { getExceptionBuilderComponentLazy } from '../../../../../../lists/public'; import { useAsync } from '@kbn/securitysolution-hook-utils'; import { getExceptionListSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_schema.mock'; import { useFetchIndex } from '../../../containers/source'; @@ -54,47 +54,58 @@ jest.mock('@kbn/securitysolution-hook-utils', () => ({ useAsync: jest.fn(), })); jest.mock('../../../../detections/containers/detection_engine/rules/use_rule_async'); +jest.mock('../../../../../../lists/public'); + +const mockGetExceptionBuilderComponentLazy = getExceptionBuilderComponentLazy as jest.Mock< + ReturnType +>; +const mockUseAsync = useAsync as jest.Mock>; +const mockUseAddOrUpdateException = useAddOrUpdateException as jest.Mock< + ReturnType +>; +const mockUseFetchOrCreateRuleExceptionList = useFetchOrCreateRuleExceptionList as jest.Mock< + ReturnType +>; +const mockUseSignalIndex = useSignalIndex as jest.Mock>>; +const mockUseFetchIndex = useFetchIndex as jest.Mock; +const mockUseCurrentUser = useCurrentUser as jest.Mock>>; +const mockUseRuleAsync = useRuleAsync as jest.Mock; describe('When the add exception modal is opened', () => { const ruleName = 'test rule'; let defaultEndpointItems: jest.SpyInstance< ReturnType >; - let ExceptionBuilderComponent: jest.SpyInstance< - ReturnType - >; beforeEach(() => { - const emptyComp = ; + mockGetExceptionBuilderComponentLazy.mockReturnValue( + + ); defaultEndpointItems = jest.spyOn(helpers, 'defaultEndpointExceptionItems'); - ExceptionBuilderComponent = jest - .spyOn(ExceptionBuilder, 'getExceptionBuilderComponentLazy') - .mockReturnValue(emptyComp); - (useAsync as jest.Mock).mockImplementation(() => ({ + mockUseAsync.mockImplementation(() => ({ start: jest.fn(), loading: false, + error: {}, + result: true, })); - (useAddOrUpdateException as jest.Mock).mockImplementation(() => [ - { isLoading: false }, - jest.fn(), - ]); - (useFetchOrCreateRuleExceptionList as jest.Mock).mockImplementation(() => [ + mockUseAddOrUpdateException.mockImplementation(() => [{ isLoading: false }, jest.fn()]); + mockUseFetchOrCreateRuleExceptionList.mockImplementation(() => [ false, getExceptionListSchemaMock(), ]); - (useSignalIndex as jest.Mock).mockImplementation(() => ({ + mockUseSignalIndex.mockImplementation(() => ({ loading: false, signalIndexName: 'mock-siem-signals-index', })); - (useFetchIndex as jest.Mock).mockImplementation(() => [ + mockUseFetchIndex.mockImplementation(() => [ false, { indexPatterns: stubIndexPattern, }, ]); - (useCurrentUser as jest.Mock).mockReturnValue({ username: 'test-username' }); - (useRuleAsync as jest.Mock).mockImplementation(() => ({ + mockUseCurrentUser.mockReturnValue({ username: 'test-username' }); + mockUseRuleAsync.mockImplementation(() => ({ rule: getRulesSchemaMock(), })); }); @@ -108,7 +119,7 @@ describe('When the add exception modal is opened', () => { let wrapper: ReactWrapper; beforeEach(() => { // Mocks one of the hooks as loading - (useFetchIndex as jest.Mock).mockImplementation(() => [ + mockUseFetchIndex.mockImplementation(() => [ true, { indexPatterns: stubIndexPattern, @@ -147,7 +158,7 @@ describe('When the add exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => callProps.onChange({ exceptionItems: [] })); }); it('has the add exception button disabled', () => { @@ -192,7 +203,7 @@ describe('When the add exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) ); @@ -252,7 +263,7 @@ describe('When the add exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] }) ); @@ -288,7 +299,7 @@ describe('When the add exception modal is opened', () => { describe('when there is an exception being created on a sequence eql rule type', () => { let wrapper: ReactWrapper; beforeEach(async () => { - (useRuleAsync as jest.Mock).mockImplementation(() => ({ + mockUseRuleAsync.mockImplementation(() => ({ rule: { ...getRulesEqlSchemaMock(), query: @@ -313,7 +324,7 @@ describe('When the add exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] }) ); @@ -354,7 +365,7 @@ describe('When the add exception modal is opened', () => { }; beforeEach(async () => { // Mocks the index patterns to contain the pre-populated endpoint fields so that the exception qualifies as bulk closable - (useFetchIndex as jest.Mock).mockImplementation(() => [ + mockUseFetchIndex.mockImplementation(() => [ false, { indexPatterns: { @@ -387,7 +398,7 @@ describe('When the add exception modal is opened', () => { /> ); - callProps = ExceptionBuilderComponent.mock.calls[0][0]; + callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) ); @@ -456,7 +467,7 @@ describe('When the add exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => callProps.onChange({ exceptionItems: [], errorExists: true })); expect( wrapper.find('button[data-test-subj="add-exception-confirm-button"]').getDOMNode() diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx index 228b7a2491fde..afff935619740 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx @@ -38,8 +38,7 @@ import { isThresholdRule, } from '../../../../../common/detection_engine/utils'; import { Status } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { ExceptionBuilder } from '../../../../../public/shared_imports'; - +import { getExceptionBuilderComponentLazy } from '../../../../../../lists/public'; import * as i18nCommon from '../../../translations'; import * as i18n from './translations'; import * as sharedI18n from '../translations'; @@ -475,7 +474,7 @@ export const AddExceptionModal = memo(function AddExceptionModal({ )} - {ExceptionBuilder.getExceptionBuilderComponentLazy({ + {getExceptionBuilderComponentLazy({ allowLargeValueLists: !isEqlRule(maybeRule?.type) && !isThresholdRule(maybeRule?.type), httpService: http, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx index 8c719373eda71..7a634b7fe10ec 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx @@ -24,7 +24,7 @@ import { } from '../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; import { useRuleAsync } from '../../../../detections/containers/detection_engine/rules/use_rule_async'; import { getMockTheme } from '../../../lib/kibana/kibana_react.mock'; -import { ExceptionBuilder } from '../../../../shared_imports'; +import { getExceptionBuilderComponentLazy } from '../../../../../../lists/public'; const mockTheme = getMockTheme({ eui: { @@ -44,39 +44,31 @@ jest.mock('../../../containers/source'); jest.mock('../use_fetch_or_create_rule_exception_list'); jest.mock('../../../../detections/containers/detection_engine/alerts/use_signal_index'); jest.mock('../../../../detections/containers/detection_engine/rules/use_rule_async'); -jest.mock('../../../../shared_imports', () => { - const originalModule = jest.requireActual('../../../../shared_imports'); - const emptyComp = ; - return { - ...originalModule, - ExceptionBuilder: { - getExceptionBuilderComponentLazy: () => emptyComp, - }, - }; -}); +jest.mock('../../../../../../lists/public'); + +const mockGetExceptionBuilderComponentLazy = getExceptionBuilderComponentLazy as jest.Mock< + ReturnType +>; +const mockUseSignalIndex = useSignalIndex as jest.Mock>>; +const mockUseAddOrUpdateException = useAddOrUpdateException as jest.Mock< + ReturnType +>; +const mockUseFetchIndex = useFetchIndex as jest.Mock; +const mockUseCurrentUser = useCurrentUser as jest.Mock>>; +const mockUseRuleAsync = useRuleAsync as jest.Mock; describe('When the edit exception modal is opened', () => { const ruleName = 'test rule'; - let ExceptionBuilderComponent: jest.SpyInstance< - ReturnType - >; - beforeEach(() => { const emptyComp = ; - ExceptionBuilderComponent = jest - .spyOn(ExceptionBuilder, 'getExceptionBuilderComponentLazy') - .mockReturnValue(emptyComp); - - (useSignalIndex as jest.Mock).mockReturnValue({ + mockGetExceptionBuilderComponentLazy.mockReturnValue(emptyComp); + mockUseSignalIndex.mockReturnValue({ loading: false, signalIndexName: 'test-signal', }); - (useAddOrUpdateException as jest.Mock).mockImplementation(() => [ - { isLoading: false }, - jest.fn(), - ]); - (useFetchIndex as jest.Mock).mockImplementation(() => [ + mockUseAddOrUpdateException.mockImplementation(() => [{ isLoading: false }, jest.fn()]); + mockUseFetchIndex.mockImplementation(() => [ false, { indexPatterns: createStubIndexPattern({ @@ -96,8 +88,8 @@ describe('When the edit exception modal is opened', () => { }), }, ]); - (useCurrentUser as jest.Mock).mockReturnValue({ username: 'test-username' }); - (useRuleAsync as jest.Mock).mockImplementation(() => ({ + mockUseCurrentUser.mockReturnValue({ username: 'test-username' }); + mockUseRuleAsync.mockImplementation(() => ({ rule: getRulesSchemaMock(), })); }); @@ -109,7 +101,7 @@ describe('When the edit exception modal is opened', () => { describe('when the modal is loading', () => { it('renders the loading spinner', async () => { - (useFetchIndex as jest.Mock).mockImplementation(() => [ + mockUseFetchIndex.mockImplementation(() => [ true, { indexPatterns: stubIndexPattern, @@ -157,7 +149,7 @@ describe('When the edit exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => { callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); }); @@ -202,7 +194,7 @@ describe('When the edit exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => { callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); }); @@ -255,7 +247,7 @@ describe('When the edit exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = (getExceptionBuilderComponentLazy as jest.Mock).mock.calls[0][0]; await waitFor(() => { callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); }); @@ -299,7 +291,7 @@ describe('When the edit exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => { callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); }); @@ -344,7 +336,7 @@ describe('When the edit exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => { callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); }); @@ -380,7 +372,7 @@ describe('When the edit exception modal is opened', () => { /> ); - const callProps = ExceptionBuilderComponent.mock.calls[0][0]; + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => callProps.onChange({ exceptionItems: [], errorExists: true })); expect( diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx index 375648e0abc8d..1724f616e7fc8 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx @@ -37,7 +37,7 @@ import { import { useFetchIndex } from '../../../containers/source'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import { useRuleAsync } from '../../../../detections/containers/detection_engine/rules/use_rule_async'; -import { ExceptionBuilder } from '../../../../../public/shared_imports'; +import { getExceptionBuilderComponentLazy } from '../../../../../../lists/public'; import * as i18n from './translations'; import * as sharedI18n from '../translations'; @@ -344,7 +344,7 @@ export const EditExceptionModal = memo(function EditExceptionModal({ )} - {ExceptionBuilder.getExceptionBuilderComponentLazy({ + {getExceptionBuilderComponentLazy({ allowLargeValueLists: !isEqlRule(maybeRule?.type) && !isThresholdRule(maybeRule?.type), httpService: http, diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx index 8f0737067ec6a..703c6c098d11e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx @@ -30,8 +30,8 @@ import { Loader } from '../../../../../../common/components/loader'; import { useKibana } from '../../../../../../common/lib/kibana'; import { useFetchIndex } from '../../../../../../common/containers/source'; import { AppAction } from '../../../../../../common/store/actions'; -import { ExceptionBuilder } from '../../../../../../shared_imports'; - +import { getExceptionBuilderComponentLazy } from '../../../../../../../../lists/public'; +import type { OnChangeProps } from '../../../../../../../../lists/public'; import { useEventFiltersSelector } from '../../hooks'; import { getFormEntryStateMutable, getHasNameError, getNewComment } from '../../../store/selector'; import { NAME_LABEL, NAME_ERROR, NAME_PLACEHOLDER, OS_LABEL, RULE_NAME } from './translations'; @@ -67,7 +67,7 @@ export const EventFiltersForm: React.FC = memo( ); const handleOnBuilderChange = useCallback( - (arg: ExceptionBuilder.OnChangeProps) => { + (arg: OnChangeProps) => { dispatch({ type: 'eventFiltersChangeForm', payload: { @@ -121,7 +121,7 @@ export const EventFiltersForm: React.FC = memo( const exceptionBuilderComponentMemo = useMemo( () => - ExceptionBuilder.getExceptionBuilderComponentLazy({ + getExceptionBuilderComponentLazy({ allowLargeValueLists: false, httpService: http, autocompleteService: data.autocomplete, diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 1d60175d56f60..d3a88004bc8dc 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -10,7 +10,7 @@ import reduceReducers from 'reduce-reducers'; import { BehaviorSubject, Subject, Subscription } from 'rxjs'; import { pluck } from 'rxjs/operators'; import { AnyAction, Reducer } from 'redux'; -import { +import type { PluginSetup, PluginStart, SetupPlugins, diff --git a/x-pack/plugins/security_solution/public/shared_imports.ts b/x-pack/plugins/security_solution/public/shared_imports.ts index 8934ad9dab4cd..421b92a6e8b7f 100644 --- a/x-pack/plugins/security_solution/public/shared_imports.ts +++ b/x-pack/plugins/security_solution/public/shared_imports.ts @@ -30,5 +30,3 @@ export { export { Field, SelectField } from '../../../../src/plugins/es_ui_shared/static/forms/components'; export { fieldValidators } from '../../../../src/plugins/es_ui_shared/static/forms/helpers'; export type { ERROR_CODE } from '../../../../src/plugins/es_ui_shared/static/forms/helpers/field_validators/types'; - -export { ExceptionBuilder } from '../../lists/public'; diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index cfca95fddc507..6a5a0f3b42e04 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -5,43 +5,43 @@ * 2.0. */ -import { CoreStart } from '../../../../src/core/public'; -import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; +import type { CoreStart } from '../../../../src/core/public'; +import type { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import type { DataPublicPluginStart } from '../../../../src/plugins/data/public'; -import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; -import { LensPublicStart } from '../../../plugins/lens/public'; -import { NewsfeedPublicPluginStart } from '../../../../src/plugins/newsfeed/public'; -import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; -import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; -import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; -import { Storage } from '../../../../src/plugins/kibana_utils/public'; -import { FleetStart } from '../../fleet/public'; -import { PluginStart as ListsPluginStart } from '../../lists/public'; -import { SpacesPluginStart } from '../../spaces/public'; -import { +import type { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; +import type { LensPublicStart } from '../../../plugins/lens/public'; +import type { NewsfeedPublicPluginStart } from '../../../../src/plugins/newsfeed/public'; +import type { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; +import type { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; +import type { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; +import type { Storage } from '../../../../src/plugins/kibana_utils/public'; +import type { FleetStart } from '../../fleet/public'; +import type { PluginStart as ListsPluginStart } from '../../lists/public'; +import type { SpacesPluginStart } from '../../spaces/public'; +import type { TriggersAndActionsUIPublicPluginSetup as TriggersActionsSetup, TriggersAndActionsUIPublicPluginStart as TriggersActionsStart, } from '../../triggers_actions_ui/public'; -import { CasesUiStart } from '../../cases/public'; -import { SecurityPluginSetup } from '../../security/public'; -import { TimelinesUIStart } from '../../timelines/public'; -import { ResolverPluginSetup } from './resolver/types'; -import { Inspect } from '../common/search_strategy'; -import { MlPluginSetup, MlPluginStart } from '../../ml/public'; +import type { CasesUiStart } from '../../cases/public'; +import type { SecurityPluginSetup } from '../../security/public'; +import type { TimelinesUIStart } from '../../timelines/public'; +import type { ResolverPluginSetup } from './resolver/types'; +import type { Inspect } from '../common/search_strategy'; +import type { MlPluginSetup, MlPluginStart } from '../../ml/public'; -import { Detections } from './detections'; -import { Cases } from './cases'; -import { Exceptions } from './exceptions'; -import { Hosts } from './hosts'; -import { Network } from './network'; -import { Overview } from './overview'; -import { Rules } from './rules'; -import { Timelines } from './timelines'; -import { Management } from './management'; -import { Ueba } from './ueba'; -import { LicensingPluginStart, LicensingPluginSetup } from '../../licensing/public'; -import { DashboardStart } from '../../../../src/plugins/dashboard/public'; -import { IndexPatternFieldEditorStart } from '../../../../src/plugins/data_view_field_editor/public'; +import type { Detections } from './detections'; +import type { Cases } from './cases'; +import type { Exceptions } from './exceptions'; +import type { Hosts } from './hosts'; +import type { Network } from './network'; +import type { Overview } from './overview'; +import type { Rules } from './rules'; +import type { Timelines } from './timelines'; +import type { Management } from './management'; +import type { Ueba } from './ueba'; +import type { LicensingPluginStart, LicensingPluginSetup } from '../../licensing/public'; +import type { DashboardStart } from '../../../../src/plugins/dashboard/public'; +import type { IndexPatternFieldEditorStart } from '../../../../src/plugins/data_view_field_editor/public'; export interface SetupPlugins { home?: HomePublicPluginSetup; From 319fc9fb7f898b50d37d9c8955413d0a31ff65f6 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 2 Dec 2021 19:11:10 +0200 Subject: [PATCH 07/65] [Cases] Move disabling features to the cases context (#119864) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/cases/common/constants.ts | 9 +++ x-pack/plugins/cases/common/ui/types.ts | 13 +++++ .../public/common/mock/test_providers.tsx | 41 +++++++++----- .../all_cases/all_cases_list.test.tsx | 56 +++++++++---------- .../components/all_cases/all_cases_list.tsx | 3 - .../public/components/all_cases/columns.tsx | 24 +++----- .../components/all_cases/index.test.tsx | 14 ++--- .../public/components/all_cases/index.tsx | 8 +-- .../cases/public/components/app/routes.tsx | 6 +- .../cases/public/components/app/types.ts | 2 - .../components/case_action_bar/index.test.tsx | 4 +- .../components/case_action_bar/index.tsx | 6 +- .../public/components/case_view/index.tsx | 5 -- .../public/components/cases_context/index.tsx | 24 ++++---- .../cases_context/use_cases_features.tsx | 22 ++++++++ .../create/flyout/create_case_flyout.tsx | 6 +- .../public/components/create/form.test.tsx | 12 ++-- .../cases/public/components/create/form.tsx | 21 +++---- .../components/create/form_context.test.tsx | 9 ++- .../public/components/create/form_context.tsx | 8 +-- .../cases/public/components/create/index.tsx | 2 - .../cases/public/methods/get_cases.tsx | 7 +-- .../public/methods/get_create_case_flyout.tsx | 5 +- .../public/pages/cases/cases.tsx | 3 +- .../timeline/cases/add_to_case_action.tsx | 10 ++-- .../components/t_grid/standalone/index.tsx | 4 +- 26 files changed, 169 insertions(+), 155 deletions(-) create mode 100644 x-pack/plugins/cases/public/components/cases_context/use_cases_features.tsx diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts index bc0578191f8b7..70ccb1ddebd10 100644 --- a/x-pack/plugins/cases/common/constants.ts +++ b/x-pack/plugins/cases/common/constants.ts @@ -5,6 +5,7 @@ * 2.0. */ import { ConnectorTypes } from './api'; +import { CasesContextValue } from './ui/types'; export const DEFAULT_DATE_FORMAT = 'dateFormat'; export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz'; @@ -104,3 +105,11 @@ export const MAX_CONCURRENT_SEARCHES = 10; */ export const MAX_TITLE_LENGTH = 64; + +/** + * Cases features + */ + +export const DEFAULT_FEATURES: CasesContextValue['features'] = Object.freeze({ + alerts: { sync: true }, +}); diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index 402e44618c7cc..c51c25c5b976c 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -19,6 +19,19 @@ import { ActionConnector, } from '../api'; +interface CasesFeatures { + alerts: { sync: boolean }; +} + +export interface CasesContextValue { + owner: string[]; + appId: string; + appTitle: string; + userCanCrud: boolean; + basePath: string; + features: CasesFeatures; +} + export interface CasesUiConfigType { markdownPlugins: { lens: boolean; diff --git a/x-pack/plugins/cases/public/common/mock/test_providers.tsx b/x-pack/plugins/cases/public/common/mock/test_providers.tsx index 41754fad836b0..d016dce48a24e 100644 --- a/x-pack/plugins/cases/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/cases/public/common/mock/test_providers.tsx @@ -5,11 +5,12 @@ * 2.0. */ +import React from 'react'; +import { merge } from 'lodash'; import { euiDarkVars } from '@kbn/ui-shared-deps-src/theme'; import { I18nProvider } from '@kbn/i18n-react'; -import React from 'react'; import { ThemeProvider } from 'styled-components'; -import { SECURITY_SOLUTION_OWNER } from '../../../common'; +import { CasesContextValue, DEFAULT_FEATURES, SECURITY_SOLUTION_OWNER } from '../../../common'; import { CasesProvider } from '../../components/cases_context'; import { createKibanaContextProviderMock } from '../lib/kibana/kibana_react.mock'; import { FieldHook } from '../shared_imports'; @@ -17,23 +18,37 @@ import { FieldHook } from '../shared_imports'; interface Props { children: React.ReactNode; userCanCrud?: boolean; + features?: CasesContextValue['features']; } window.scrollTo = jest.fn(); const MockKibanaContextProvider = createKibanaContextProviderMock(); /** A utility for wrapping children in the providers required to run most tests */ -const TestProvidersComponent: React.FC = ({ children, userCanCrud = true }) => ( - - - ({ eui: euiDarkVars, darkMode: true })}> - - {children} - - - - -); +const TestProvidersComponent: React.FC = ({ + children, + userCanCrud = true, + features = {}, +}) => { + /** + * The empty object at the beginning avoids the mutation + * of the DEFAULT_FEATURES object + */ + const featuresOptions = merge({}, DEFAULT_FEATURES, features); + return ( + + + ({ eui: euiDarkVars, darkMode: true })}> + + {children} + + + + + ); +}; export const TestProviders = React.memo(TestProvidersComponent); diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index 9cde5da159960..bf02202ff83b2 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -29,7 +29,7 @@ import { useUpdateCases } from '../../containers/use_bulk_update_case'; import { useGetActionLicense } from '../../containers/use_get_action_license'; import { useConnectors } from '../../containers/configure/use_connectors'; import { useKibana } from '../../common/lib/kibana'; -import { AllCasesList, AllCasesListProps } from './all_cases_list'; +import { AllCasesList } from './all_cases_list'; import { CasesColumns, GetCasesColumn, useCasesColumns } from './columns'; import { triggersActionsUiMock } from '../../../../triggers_actions_ui/public/mocks'; import { registerConnectorsToMockActionRegistry } from '../../common/mock/register_connectors'; @@ -64,10 +64,6 @@ const mockKibana = () => { }; describe('AllCasesListGeneric', () => { - const defaultAllCasesListProps: AllCasesListProps = { - disableAlerts: false, - }; - const dispatchResetIsDeleted = jest.fn(); const dispatchResetIsUpdated = jest.fn(); const dispatchUpdateCaseProperty = jest.fn(); @@ -161,7 +157,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); @@ -215,7 +211,7 @@ describe('AllCasesListGeneric', () => { }); const wrapper = mount( - + ); const checkIt = (columnName: string, key: number) => { @@ -245,7 +241,7 @@ describe('AllCasesListGeneric', () => { }); const wrapper = mount( - + ); await waitFor(() => { @@ -281,7 +277,7 @@ describe('AllCasesListGeneric', () => { }); const wrapper = mount( - + ); @@ -293,7 +289,7 @@ describe('AllCasesListGeneric', () => { it('should tableHeaderSortButton AllCasesList', async () => { const wrapper = mount( - + ); wrapper.find('[data-test-subj="tableHeaderSortButton"]').first().simulate('click'); @@ -310,7 +306,7 @@ describe('AllCasesListGeneric', () => { it('Updates status when status context menu is updated', async () => { const wrapper = mount( - + ); wrapper.find(`[data-test-subj="case-view-status-dropdown"] button`).first().simulate('click'); @@ -351,7 +347,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); @@ -388,7 +384,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); @@ -431,7 +427,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); @@ -458,7 +454,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); @@ -481,7 +477,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); @@ -500,7 +496,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); @@ -521,7 +517,7 @@ describe('AllCasesListGeneric', () => { mount( - + ); await waitFor(() => { @@ -539,7 +535,7 @@ describe('AllCasesListGeneric', () => { mount( - + ); await waitFor(() => { @@ -552,7 +548,7 @@ describe('AllCasesListGeneric', () => { it('should not render table utility bar when isSelectorView=true', async () => { const wrapper = mount( - + ); await waitFor(() => { @@ -566,7 +562,7 @@ describe('AllCasesListGeneric', () => { it('case table should not be selectable when isSelectorView=true', async () => { const wrapper = mount( - + ); await waitFor(() => { @@ -588,7 +584,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); @@ -600,7 +596,7 @@ describe('AllCasesListGeneric', () => { it('should call onRowClick when clicking a case with modal=true', async () => { const wrapper = mount( - + ); @@ -657,7 +653,7 @@ describe('AllCasesListGeneric', () => { it('should NOT call onRowClick when clicking a case with modal=true', async () => { const wrapper = mount( - + ); wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); @@ -669,7 +665,7 @@ describe('AllCasesListGeneric', () => { it('should change the status to closed', async () => { const wrapper = mount( - + ); wrapper.find('button[data-test-subj="case-status-filter"]').simulate('click'); @@ -684,7 +680,7 @@ describe('AllCasesListGeneric', () => { it('should change the status to in-progress', async () => { const wrapper = mount( - + ); wrapper.find('button[data-test-subj="case-status-filter"]').simulate('click'); @@ -699,7 +695,7 @@ describe('AllCasesListGeneric', () => { it('should change the status to open', async () => { const wrapper = mount( - + ); wrapper.find('button[data-test-subj="case-status-filter"]').simulate('click'); @@ -714,7 +710,7 @@ describe('AllCasesListGeneric', () => { it('should show the correct count on stats', async () => { const wrapper = mount( - + ); wrapper.find('button[data-test-subj="case-status-filter"]').simulate('click'); @@ -734,7 +730,7 @@ describe('AllCasesListGeneric', () => { it('should not render status when isSelectorView=true', async () => { const wrapper = mount( - + ); @@ -769,7 +765,7 @@ describe('AllCasesListGeneric', () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx index bf42cfe38863e..58c17695d0dfe 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx @@ -53,7 +53,6 @@ const getSortField = (field: string): SortFieldCase => export interface AllCasesListProps { alertData?: Omit; - disableAlerts?: boolean; hiddenStatuses?: CaseStatusWithAllStatus[]; isSelectorView?: boolean; onRowClick?: (theCase?: Case | SubCase) => void; @@ -64,7 +63,6 @@ export interface AllCasesListProps { export const AllCasesList = React.memo( ({ alertData, - disableAlerts, hiddenStatuses = [], isSelectorView = false, onRowClick, @@ -168,7 +166,6 @@ export const AllCasesList = React.memo( const showActions = userCanCrud && !isSelectorView; const columns = useCasesColumns({ - disableAlerts, dispatchUpdateCaseProperty, filterStatus: filterOptions.status, handleIsLoading, diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.tsx b/x-pack/plugins/cases/public/components/all_cases/columns.tsx index 67d4409444016..c30ddd199fc49 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/columns.tsx @@ -69,7 +69,6 @@ const renderStringField = (field: string, dataTestSubj: string) => field != null ? {field} : getEmptyTagValue(); export interface GetCasesColumn { - disableAlerts?: boolean; dispatchUpdateCaseProperty: (u: UpdateCase) => void; filterStatus: string; handleIsLoading: (a: boolean) => void; @@ -84,7 +83,6 @@ export interface GetCasesColumn { updateCase?: (newCase: Case) => void; } export const useCasesColumns = ({ - disableAlerts = false, dispatchUpdateCaseProperty, filterStatus, handleIsLoading, @@ -246,19 +244,15 @@ export const useCasesColumns = ({ }, truncateText: true, }, - ...(!disableAlerts - ? [ - { - align: RIGHT_ALIGNMENT, - field: 'totalAlerts', - name: ALERTS, - render: (totalAlerts: Case['totalAlerts']) => - totalAlerts != null - ? renderStringField(`${totalAlerts}`, `case-table-column-alertsCount`) - : getEmptyTagValue(), - }, - ] - : []), + { + align: RIGHT_ALIGNMENT, + field: 'totalAlerts', + name: ALERTS, + render: (totalAlerts: Case['totalAlerts']) => + totalAlerts != null + ? renderStringField(`${totalAlerts}`, `case-table-column-alertsCount`) + : getEmptyTagValue(), + }, { align: RIGHT_ALIGNMENT, field: 'totalComment', diff --git a/x-pack/plugins/cases/public/components/all_cases/index.test.tsx b/x-pack/plugins/cases/public/components/all_cases/index.test.tsx index 8429712a30f74..681bb65870c1e 100644 --- a/x-pack/plugins/cases/public/components/all_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/index.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { waitFor } from '@testing-library/react'; -import { AllCases, AllCasesProps } from '.'; +import { AllCases } from '.'; import { TestProviders } from '../../common/mock'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetReporters } from '../../containers/use_get_reporters'; @@ -31,10 +31,6 @@ jest.mock('../../common/lib/kibana'); jest.mock('../../containers/use_get_cases'); jest.mock('../../containers/use_get_cases_status'); -const defaultAllCasesProps: AllCasesProps = { - disableAlerts: false, -}; - const useKibanaMock = useKibana as jest.Mocked; const useConnectorsMock = useConnectors as jest.Mock; const useGetCasesMock = useGetCases as jest.Mock; @@ -105,7 +101,7 @@ describe('AllCases', () => { const wrapper = mount( - + ); @@ -141,7 +137,7 @@ describe('AllCases', () => { const wrapper = mount( - + ); @@ -173,7 +169,7 @@ describe('AllCases', () => { const wrapper = mount( - + ); @@ -199,7 +195,7 @@ describe('AllCases', () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/cases/public/components/all_cases/index.tsx b/x-pack/plugins/cases/public/components/all_cases/index.tsx index 7f7de11a3435c..e163d9ada4a51 100644 --- a/x-pack/plugins/cases/public/components/all_cases/index.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/index.tsx @@ -14,11 +14,7 @@ import { getActionLicenseError } from '../use_push_to_service/helpers'; import { AllCasesList } from './all_cases_list'; import { CasesTableHeader } from './header'; -export interface AllCasesProps { - disableAlerts?: boolean; -} - -export const AllCases: React.FC = ({ disableAlerts }) => { +export const AllCases: React.FC = () => { const { userCanCrud } = useCasesContext(); const [refresh, setRefresh] = useState(0); useCasesBreadcrumbs(CasesDeepLinkId.cases); @@ -33,7 +29,7 @@ export const AllCases: React.FC = ({ disableAlerts }) => { return ( <> - + ); }; diff --git a/x-pack/plugins/cases/public/components/app/routes.tsx b/x-pack/plugins/cases/public/components/app/routes.tsx index bd4450baaf3fb..06387072c2323 100644 --- a/x-pack/plugins/cases/public/components/app/routes.tsx +++ b/x-pack/plugins/cases/public/components/app/routes.tsx @@ -28,14 +28,12 @@ import * as i18n from './translations'; import { useReadonlyHeader } from './use_readonly_header'; const CasesRoutesComponent: React.FC = ({ - disableAlerts, onComponentInitialized, actionsNavigation, ruleDetailsNavigation, showAlertDetails, useFetchAlertData, refreshRef, - hideSyncAlerts, timelineIntegration, }) => { const { basePath, userCanCrud } = useCasesContext(); @@ -51,7 +49,7 @@ const CasesRoutesComponent: React.FC = ({ return ( - + @@ -59,7 +57,6 @@ const CasesRoutesComponent: React.FC = ({ ) : ( @@ -91,7 +88,6 @@ const CasesRoutesComponent: React.FC = ({ showAlertDetails={showAlertDetails} useFetchAlertData={useFetchAlertData} refreshRef={refreshRef} - hideSyncAlerts={hideSyncAlerts} timelineIntegration={timelineIntegration} /> diff --git a/x-pack/plugins/cases/public/components/app/types.ts b/x-pack/plugins/cases/public/components/app/types.ts index 943cf5fcad96d..9c825ad95618a 100644 --- a/x-pack/plugins/cases/public/components/app/types.ts +++ b/x-pack/plugins/cases/public/components/app/types.ts @@ -11,7 +11,6 @@ import { CasesNavigation } from '../links'; import { CasesTimelineIntegration } from '../timeline_context'; export interface CasesRoutesProps { - disableAlerts?: boolean; onComponentInitialized?: () => void; actionsNavigation?: CasesNavigation; ruleDetailsNavigation?: CasesNavigation; @@ -22,6 +21,5 @@ export interface CasesRoutesProps { * **NOTE**: Do not hold on to the `.current` object, as it could become stale */ refreshRef?: MutableRefObject; - hideSyncAlerts?: boolean; timelineIntegration?: CasesTimelineIntegration; } diff --git a/x-pack/plugins/cases/public/components/case_action_bar/index.test.tsx b/x-pack/plugins/cases/public/components/case_action_bar/index.test.tsx index 371d6dcf3063e..f36ecd5b6b5d1 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/index.test.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/index.test.tsx @@ -118,8 +118,8 @@ describe('CaseActionBar', () => { it('should not show the sync alerts toggle when alerting is disabled', () => { const { queryByText } = render( - - + + ); diff --git a/x-pack/plugins/cases/public/components/case_action_bar/index.tsx b/x-pack/plugins/cases/public/components/case_action_bar/index.tsx index e95410c1967b6..9b326f3216084 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/index.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/index.tsx @@ -25,6 +25,7 @@ import { StatusContextMenu } from './status_context_menu'; import { getStatusDate, getStatusTitle } from './helpers'; import { SyncAlertsSwitch } from '../case_settings/sync_alerts_switch'; import { OnUpdateFields } from '../case_view'; +import { useCasesFeatures } from '../cases_context/use_cases_features'; const MyDescriptionList = styled(EuiDescriptionList)` ${({ theme }) => css` @@ -43,7 +44,6 @@ interface CaseActionBarProps { caseData: Case; currentExternalIncident: CaseService | null; userCanCrud: boolean; - disableAlerting: boolean; isLoading: boolean; onRefresh: () => void; onUpdateField: (args: OnUpdateFields) => void; @@ -51,12 +51,12 @@ interface CaseActionBarProps { const CaseActionBarComponent: React.FC = ({ caseData, currentExternalIncident, - disableAlerting, userCanCrud, isLoading, onRefresh, onUpdateField, }) => { + const { isSyncAlertsEnabled } = useCasesFeatures(); const date = useMemo(() => getStatusDate(caseData), [caseData]); const title = useMemo(() => getStatusTitle(caseData.status), [caseData.status]); const onStatusChanged = useCallback( @@ -114,7 +114,7 @@ const CaseActionBarComponent: React.FC = ({ responsive={false} justifyContent="spaceBetween" > - {userCanCrud && !disableAlerting && ( + {userCanCrud && isSyncAlertsEnabled && ( ; - hideSyncAlerts?: boolean; } export interface CaseViewProps extends Omit { @@ -98,7 +97,6 @@ export const CaseComponent = React.memo( updateCase, useFetchAlertData, refreshRef, - hideSyncAlerts = false, }) => { const { userCanCrud } = useCasesContext(); const { getCaseViewUrl } = useCaseViewNavigation(); @@ -388,7 +386,6 @@ export const CaseComponent = React.memo( caseData={caseData} currentExternalIncident={currentExternalIncident} userCanCrud={userCanCrud} - disableAlerting={ruleDetailsNavigation == null || hideSyncAlerts} isLoading={isLoading && (updateKey === 'status' || updateKey === 'settings')} onRefresh={handleRefresh} onUpdateField={onUpdateField} @@ -506,7 +503,6 @@ export const CaseView = React.memo( timelineIntegration, useFetchAlertData, refreshRef, - hideSyncAlerts, }: CaseViewProps) => { const { spaces: spacesApi } = useKibana().services; const { detailName: caseId, subCaseId } = useCaseViewParams(); @@ -562,7 +558,6 @@ export const CaseView = React.memo( updateCase={updateCase} useFetchAlertData={useFetchAlertData} refreshRef={refreshRef} - hideSyncAlerts={hideSyncAlerts} /> ) diff --git a/x-pack/plugins/cases/public/components/cases_context/index.tsx b/x-pack/plugins/cases/public/components/cases_context/index.tsx index 932ce69372110..588bda245b044 100644 --- a/x-pack/plugins/cases/public/components/cases_context/index.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/index.tsx @@ -6,40 +6,38 @@ */ import React, { useState, useEffect } from 'react'; +import { merge } from 'lodash'; +import { CasesContextValue, DEFAULT_FEATURES } from '../../../common'; import { DEFAULT_BASE_PATH } from '../../common/navigation'; import { useApplication } from './use_application'; -export interface CasesContextValue { - owner: string[]; - appId: string; - appTitle: string; - userCanCrud: boolean; - basePath: string; -} - export const CasesContext = React.createContext(undefined); export interface CasesContextProps - extends Omit { + extends Omit { basePath?: string; + features?: Partial; } -export interface CasesContextStateValue - extends Omit { +export interface CasesContextStateValue extends Omit { appId?: string; appTitle?: string; - userCanCrud?: boolean; } export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({ children, - value: { owner, userCanCrud, basePath = DEFAULT_BASE_PATH }, + value: { owner, userCanCrud, basePath = DEFAULT_BASE_PATH, features = {} }, }) => { const { appId, appTitle } = useApplication(); const [value, setValue] = useState({ owner, userCanCrud, basePath, + /** + * The empty object at the beginning avoids the mutation + * of the DEFAULT_FEATURES object + */ + features: merge({}, DEFAULT_FEATURES, features), }); /** diff --git a/x-pack/plugins/cases/public/components/cases_context/use_cases_features.tsx b/x-pack/plugins/cases/public/components/cases_context/use_cases_features.tsx new file mode 100644 index 0000000000000..93efdcabd8c38 --- /dev/null +++ b/x-pack/plugins/cases/public/components/cases_context/use_cases_features.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { useCasesContext } from './use_cases_context'; + +interface UseCasesFeaturesReturn { + isSyncAlertsEnabled: boolean; +} + +export const useCasesFeatures = (): UseCasesFeaturesReturn => { + const { features } = useCasesContext(); + const memoizedReturnValue = useMemo( + () => ({ isSyncAlertsEnabled: features.alerts.sync }), + [features] + ); + return memoizedReturnValue; +}; diff --git a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx index 5723f78649b5c..e77f72929ecd8 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx @@ -17,7 +17,6 @@ export interface CreateCaseFlyoutProps { afterCaseCreated?: (theCase: Case) => Promise; onClose: () => void; onSuccess: (theCase: Case) => Promise; - disableAlerts?: boolean; } const StyledFlyout = styled(EuiFlyout)` @@ -50,7 +49,7 @@ const StyledEuiFlyoutBody = styled(EuiFlyoutBody)` overflow-y: auto; overflow-x: hidden; } - + && .euiFlyoutBody__overflowContent { display: block; padding: ${theme.eui.paddingSizes.l} ${theme.eui.paddingSizes.l} 70px; @@ -64,7 +63,7 @@ const FormWrapper = styled.div` `; export const CreateCaseFlyout = React.memo( - ({ afterCaseCreated, onClose, onSuccess, disableAlerts }) => ( + ({ afterCaseCreated, onClose, onSuccess }) => ( <> ( onCancel={onClose} onSuccess={onSuccess} withSteps={false} - disableAlerts={disableAlerts} /> diff --git a/x-pack/plugins/cases/public/components/create/form.test.tsx b/x-pack/plugins/cases/public/components/create/form.test.tsx index 06b07ebd396a8..9f1e2e6c6dda3 100644 --- a/x-pack/plugins/cases/public/components/create/form.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form.test.tsx @@ -44,7 +44,10 @@ const casesFormProps: CreateCaseFormProps = { describe('CreateCaseForm', () => { let globalForm: FormHook; - const MockHookWrapperComponent: React.FC = ({ children }) => { + const MockHookWrapperComponent: React.FC<{ testProviderProps?: unknown }> = ({ + children, + testProviderProps = {}, + }) => { const { form } = useForm({ defaultValue: initialCaseValue, options: { stripEmptyFields: false }, @@ -54,7 +57,7 @@ describe('CreateCaseForm', () => { globalForm = form; return ( - +
{children}
); @@ -103,8 +106,8 @@ describe('CreateCaseForm', () => { it('hides the sync alerts toggle', () => { const { queryByText } = render( - - + + ); @@ -118,7 +121,6 @@ describe('CreateCaseForm', () => { diff --git a/x-pack/plugins/cases/public/components/create/form.tsx b/x-pack/plugins/cases/public/components/create/form.tsx index 6105fdbb2aef3..2c775cb5fd86d 100644 --- a/x-pack/plugins/cases/public/components/create/form.tsx +++ b/x-pack/plugins/cases/public/components/create/form.tsx @@ -30,6 +30,7 @@ import { InsertTimeline } from '../insert_timeline'; import { UsePostComment } from '../../containers/use_post_comment'; import { SubmitCaseButton } from './submit_button'; import { FormContext } from './form_context'; +import { useCasesFeatures } from '../cases_context/use_cases_features'; interface ContainerProps { big?: boolean; @@ -51,15 +52,11 @@ const MySpinner = styled(EuiLoadingSpinner)` export interface CreateCaseFormFieldsProps { connectors: ActionConnector[]; isLoadingConnectors: boolean; - disableAlerts: boolean; hideConnectorServiceNowSir: boolean; withSteps: boolean; } export interface CreateCaseFormProps - extends Pick< - Partial, - 'disableAlerts' | 'hideConnectorServiceNowSir' | 'withSteps' - > { + extends Pick, 'hideConnectorServiceNowSir' | 'withSteps'> { onCancel: () => void; onSuccess: (theCase: Case) => Promise; afterCaseCreated?: (theCase: Case, postComment: UsePostComment['postComment']) => Promise; @@ -69,8 +66,10 @@ export interface CreateCaseFormProps const empty: ActionConnector[] = []; export const CreateCaseFormFields: React.FC = React.memo( - ({ connectors, disableAlerts, isLoadingConnectors, hideConnectorServiceNowSir, withSteps }) => { + ({ connectors, isLoadingConnectors, hideConnectorServiceNowSir, withSteps }) => { const { isSubmitting } = useFormContext(); + const { isSyncAlertsEnabled } = useCasesFeatures(); + const firstStep = useMemo( () => ({ title: i18n.STEP_ONE_TITLE, @@ -119,8 +118,8 @@ export const CreateCaseFormFields: React.FC = React.m ); const allSteps = useMemo( - () => [firstStep, ...(!disableAlerts ? [secondStep] : []), thirdStep], - [disableAlerts, firstStep, secondStep, thirdStep] + () => [firstStep, ...(isSyncAlertsEnabled ? [secondStep] : []), thirdStep], + [isSyncAlertsEnabled, firstStep, secondStep, thirdStep] ); return ( @@ -135,7 +134,7 @@ export const CreateCaseFormFields: React.FC = React.m ) : ( <> {firstStep.children} - {!disableAlerts && secondStep.children} + {isSyncAlertsEnabled && secondStep.children} {thirdStep.children} )} @@ -148,7 +147,6 @@ CreateCaseFormFields.displayName = 'CreateCaseFormFields'; export const CreateCaseForm: React.FC = React.memo( ({ - disableAlerts = false, hideConnectorServiceNowSir = false, withSteps = true, afterCaseCreated, @@ -163,12 +161,9 @@ export const CreateCaseForm: React.FC = React.memo( caseType={caseType} hideConnectorServiceNowSir={hideConnectorServiceNowSir} onSuccess={onSuccess} - // if we are disabling alerts, then we should not sync alerts - syncAlertsDefaultValue={!disableAlerts} > { ); }); - it('should set sync alerts to false when the sync setting is passed in as false and alerts are disabled', async () => { + it('should set sync alerts to false when the sync feature setting is false', async () => { useConnectorsMock.mockReturnValue({ ...sampleConnectorData, connectors: connectorsMock, }); const wrapper = mount( - - - + + + diff --git a/x-pack/plugins/cases/public/components/create/form_context.tsx b/x-pack/plugins/cases/public/components/create/form_context.tsx index 10bfa2e78acc4..a513056ba31a5 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.tsx @@ -17,6 +17,7 @@ import { Case } from '../../containers/types'; import { CaseType } from '../../../common'; import { UsePostComment, usePostComment } from '../../containers/use_post_comment'; import { useCasesContext } from '../cases_context/use_cases_context'; +import { useCasesFeatures } from '../cases_context/use_cases_features'; import { getConnectorById } from '../utils'; const initialCaseValue: FormProps = { @@ -34,7 +35,6 @@ interface Props { children?: JSX.Element | JSX.Element[]; hideConnectorServiceNowSir?: boolean; onSuccess?: (theCase: Case) => Promise; - syncAlertsDefaultValue?: boolean; } export const FormContext: React.FC = ({ @@ -43,10 +43,10 @@ export const FormContext: React.FC = ({ children, hideConnectorServiceNowSir, onSuccess, - syncAlertsDefaultValue = true, }) => { const { connectors, loading: isLoadingConnectors } = useConnectors(); const { owner } = useCasesContext(); + const { isSyncAlertsEnabled } = useCasesFeatures(); const { postCase } = usePostCase(); const { postComment } = usePostComment(); const { pushCaseToExternalService } = usePostPushToService(); @@ -56,7 +56,7 @@ export const FormContext: React.FC = ({ { connectorId: dataConnectorId, fields, - syncAlerts = syncAlertsDefaultValue, + syncAlerts = isSyncAlertsEnabled, ...dataWithoutConnectorId }, isValid @@ -93,6 +93,7 @@ export const FormContext: React.FC = ({ } }, [ + isSyncAlertsEnabled, connectors, postCase, caseType, @@ -101,7 +102,6 @@ export const FormContext: React.FC = ({ onSuccess, postComment, pushCaseToExternalService, - syncAlertsDefaultValue, ] ); diff --git a/x-pack/plugins/cases/public/components/create/index.tsx b/x-pack/plugins/cases/public/components/create/index.tsx index 97a1c1d2e8ff3..76c31d6f8c4c4 100644 --- a/x-pack/plugins/cases/public/components/create/index.tsx +++ b/x-pack/plugins/cases/public/components/create/index.tsx @@ -21,7 +21,6 @@ export const CreateCase = React.memo( afterCaseCreated, caseType, hideConnectorServiceNowSir, - disableAlerts, onCancel, onSuccess, timelineIntegration, @@ -40,7 +39,6 @@ export const CreateCase = React.memo( afterCaseCreated={afterCaseCreated} caseType={caseType} hideConnectorServiceNowSir={hideConnectorServiceNowSir} - disableAlerts={disableAlerts} onCancel={onCancel} onSuccess={onSuccess} timelineIntegration={timelineIntegration} diff --git a/x-pack/plugins/cases/public/methods/get_cases.tsx b/x-pack/plugins/cases/public/methods/get_cases.tsx index 432c2d9fc5b2b..94e7d321840a8 100644 --- a/x-pack/plugins/cases/public/methods/get_cases.tsx +++ b/x-pack/plugins/cases/public/methods/get_cases.tsx @@ -17,27 +17,24 @@ export const getCasesLazy = ({ owner, userCanCrud, basePath, - disableAlerts, onComponentInitialized, actionsNavigation, ruleDetailsNavigation, showAlertDetails, useFetchAlertData, refreshRef, - hideSyncAlerts, timelineIntegration, + features, }: GetCasesProps) => ( - + }> diff --git a/x-pack/plugins/cases/public/methods/get_create_case_flyout.tsx b/x-pack/plugins/cases/public/methods/get_create_case_flyout.tsx index 2128063ea9084..1e2143888bf1e 100644 --- a/x-pack/plugins/cases/public/methods/get_create_case_flyout.tsx +++ b/x-pack/plugins/cases/public/methods/get_create_case_flyout.tsx @@ -18,18 +18,17 @@ const CreateCaseFlyoutLazy: React.FC = lazy( export const getCreateCaseFlyoutLazy = ({ owner, userCanCrud, + features, afterCaseCreated, onClose, onSuccess, - disableAlerts, }: GetCreateCaseFlyoutProps) => ( - + }> diff --git a/x-pack/plugins/observability/public/pages/cases/cases.tsx b/x-pack/plugins/observability/public/pages/cases/cases.tsx index d947f9aa86489..19eb16a3bd52b 100644 --- a/x-pack/plugins/observability/public/pages/cases/cases.tsx +++ b/x-pack/plugins/observability/public/pages/cases/cases.tsx @@ -43,9 +43,9 @@ export const Cases = React.memo(({ userCanCrud }) => { )} {casesUi.getCases({ basePath: CASES_PATH, - disableAlerts: true, userCanCrud, owner: [CASES_OWNER], + features: { alerts: { sync: false } }, useFetchAlertData, showAlertDetails: (alertId: string) => { setSelectedAlertId(alertId); @@ -65,7 +65,6 @@ export const Cases = React.memo(({ userCanCrud }) => { }); }, }, - hideSyncAlerts: true, })} ); diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.tsx index 05b18da3293bd..c734442d5a77b 100644 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.tsx +++ b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.tsx @@ -7,7 +7,7 @@ import React, { memo, useMemo, useCallback } from 'react'; import { useDispatch } from 'react-redux'; -import { CaseStatuses, StatusAll } from '../../../../../../cases/common'; +import { CaseStatuses, StatusAll, CasesContextValue } from '../../../../../../cases/common'; import { TimelineItem } from '../../../../../common/'; import { useAddToCase, normalizedEventFields } from '../../../../hooks/use_add_to_case'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; @@ -24,7 +24,7 @@ export interface AddToCaseActionProps { appId: string; owner: string; onClose?: Function; - disableAlerts?: boolean; + casesFeatures?: CasesContextValue['features']; } const AddToCaseActionComponent: React.FC = ({ @@ -34,7 +34,7 @@ const AddToCaseActionComponent: React.FC = ({ appId, owner, onClose, - disableAlerts, + casesFeatures, }) => { const eventId = event?.ecs._id ?? ''; const eventIndex = event?.ecs._index ?? ''; @@ -94,8 +94,8 @@ const AddToCaseActionComponent: React.FC = ({ onSuccess: onCaseSuccess, useInsertTimeline, owner: [owner], - disableAlerts, userCanCrud: casePermissions?.crud ?? false, + features: casesFeatures, }; }, [ attachAlertToCase, @@ -103,8 +103,8 @@ const AddToCaseActionComponent: React.FC = ({ onCaseSuccess, useInsertTimeline, owner, - disableAlerts, casePermissions, + casesFeatures, ]); return ( diff --git a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx index a95683e7de4aa..18009d9e176a8 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx @@ -75,6 +75,8 @@ const ScrollableFlexItem = styled(EuiFlexItem)` overflow: auto; `; +const casesFeatures = { alerts: { sync: false } }; + export interface TGridStandaloneProps { appId: string; casesOwner: string; @@ -416,7 +418,7 @@ const TGridStandaloneComponent: React.FC = ({ ) : null} - + ); From 9e12416d32a86a5b9cc136d66528096a3e642b76 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Thu, 2 Dec 2021 19:27:15 +0200 Subject: [PATCH 08/65] Normalize apm transaction names (#119740) * update task manager apm transactions * normalize reporting apm transaction names * update core transaction names * fix tests * typo + test * task manager types * consts * reporting transaction type * dummy * dummy * remove unused consts * Update src/core/server/server.ts Co-authored-by: Mikhail Shustov * Update src/core/server/server.ts Co-authored-by: Mikhail Shustov * Update src/core/server/server.ts Co-authored-by: Mikhail Shustov * new line * Update x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts Co-authored-by: ymao1 * Update x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts Co-authored-by: ymao1 * alerting code review * ok Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Mikhail Shustov Co-authored-by: ymao1 --- dev_docs/contributing/standards.mdx | 8 ++++++++ src/core/public/apm_system.ts | 2 +- src/core/server/server.ts | 6 +++--- x-pack/plugins/reporting/common/constants.ts | 2 ++ .../export_types/common/generate_png.ts | 11 ++++++----- .../export_types/png/execute_job/index.ts | 8 ++++---- .../server/export_types/png_v2/execute_job.ts | 8 ++++---- .../printable_pdf/execute_job/index.ts | 8 ++++---- .../export_types/printable_pdf/lib/tracker.ts | 17 +++++++++-------- .../printable_pdf_v2/execute_job.ts | 8 ++++---- .../printable_pdf_v2/lib/tracker.ts | 17 +++++++++-------- .../server/lib/screenshots/observable.ts | 5 +++-- .../lib/screenshots/observable_handler.ts | 2 +- .../server/queries/task_claiming.test.ts | 16 +++++++++++----- .../server/queries/task_claiming.ts | 8 ++++++-- .../task_running/ephemeral_task_runner.ts | 13 +++++++++++-- .../server/task_running/task_runner.test.ts | 19 +++++++++++++------ .../server/task_running/task_runner.ts | 12 ++++++++++-- 18 files changed, 109 insertions(+), 61 deletions(-) diff --git a/dev_docs/contributing/standards.mdx b/dev_docs/contributing/standards.mdx index 172a83935b966..d2f31f3a4faa2 100644 --- a/dev_docs/contributing/standards.mdx +++ b/dev_docs/contributing/standards.mdx @@ -67,6 +67,14 @@ Every public API should have a release tag specified at the top of it’s docume Every team should be collecting telemetry metrics on it’s public API usage. This will be important for knowing when it’s safe to make breaking changes. The Core team will be looking into ways to make this easier and an automatic part of registration (see [#112291](https://github.com/elastic/kibana/issues/112291)). +### APM + +Kibana server and client are instrumented with APM node and APM RUM clients respectively, tracking serveral types of transactions by default, such as `page-load`, `request`, etc. +You may introduce custom transactions. Please refer to the [APM documentation](https://www.elastic.co/guide/en/apm/get-started/current/index.html) and follow these guidelines when doing so: + +- Use dashed syntax for transaction types and names: `my-transaction-type` and `my-transaction-name` +- [Refrain from adding too many custom labels](https://www.elastic.co/guide/en/apm/get-started/current/metadata.html) + ### Documentation Every public API should be documented inside the [docs/api](https://github.com/elastic/kibana/tree/main/docs/api) folder in asciidoc (this content will eventually be migrated to mdx to support the new docs system). If a public REST API is undocumented, you should either document it, or make it internal. diff --git a/src/core/public/apm_system.ts b/src/core/public/apm_system.ts index 5201b7005c66e..f15a317f9f934 100644 --- a/src/core/public/apm_system.ts +++ b/src/core/public/apm_system.ts @@ -71,7 +71,7 @@ export class ApmSystem { start.application.currentAppId$.subscribe((appId) => { if (appId && this.apm) { this.closePageLoadTransaction(); - this.apm.startTransaction(`/app/${appId}`, 'route-change', { + this.apm.startTransaction(appId, 'app-change', { managed: true, canReuse: true, }); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index e8c7ce6abb029..c35fde302578a 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -128,7 +128,7 @@ export class Server { public async preboot() { this.log.debug('prebooting server'); - const prebootTransaction = apm.startTransaction('server_preboot', 'kibana_platform'); + const prebootTransaction = apm.startTransaction('server-preboot', 'kibana-platform'); const environmentPreboot = await this.environment.preboot(); @@ -184,7 +184,7 @@ export class Server { public async setup() { this.log.debug('setting up server'); - const setupTransaction = apm.startTransaction('server_setup', 'kibana_platform'); + const setupTransaction = apm.startTransaction('server-setup', 'kibana-platform'); const environmentSetup = this.environment.setup(); @@ -291,7 +291,7 @@ export class Server { public async start() { this.log.debug('starting server'); - const startTransaction = apm.startTransaction('server_start', 'kibana_platform'); + const startTransaction = apm.startTransaction('server-start', 'kibana-platform'); const executionContextStart = this.executionContext.start(); const elasticsearchStart = await this.elasticsearch.start(); diff --git a/x-pack/plugins/reporting/common/constants.ts b/x-pack/plugins/reporting/common/constants.ts index 1eef032945e69..65d196b6e068a 100644 --- a/x-pack/plugins/reporting/common/constants.ts +++ b/x-pack/plugins/reporting/common/constants.ts @@ -7,6 +7,8 @@ export const PLUGIN_ID = 'reporting'; +export const REPORTING_TRANSACTION_TYPE = PLUGIN_ID; + export const REPORTING_SYSTEM_INDEX = '.reporting'; export const JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY = diff --git a/x-pack/plugins/reporting/server/export_types/common/generate_png.ts b/x-pack/plugins/reporting/server/export_types/common/generate_png.ts index 5ad39a3f91303..c5e70a6c93eff 100644 --- a/x-pack/plugins/reporting/server/export_types/common/generate_png.ts +++ b/x-pack/plugins/reporting/server/export_types/common/generate_png.ts @@ -8,6 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { finalize, map, tap } from 'rxjs/operators'; +import { REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; import { ReportingCore } from '../../'; import { UrlOrUrlLocatorTuple } from '../../../common/types'; import { LevelLogger } from '../../lib'; @@ -27,8 +28,8 @@ export async function generatePngObservableFactory(reporting: ReportingCore) { conditionalHeaders: ConditionalHeaders, layoutParams: LayoutParams & { selectors?: Partial } ): Rx.Observable<{ buffer: Buffer; warnings: string[] }> { - const apmTrans = apm.startTransaction('reporting generate_png', 'reporting'); - const apmLayout = apmTrans?.startSpan('create_layout', 'setup'); + const apmTrans = apm.startTransaction('generate-png', REPORTING_TRANSACTION_TYPE); + const apmLayout = apmTrans?.startSpan('create-layout', 'setup'); if (!layoutParams || !layoutParams.dimensions) { throw new Error(`LayoutParams.Dimensions is undefined.`); } @@ -36,7 +37,7 @@ export async function generatePngObservableFactory(reporting: ReportingCore) { if (apmLayout) apmLayout.end(); - const apmScreenshots = apmTrans?.startSpan('screenshots_pipeline', 'setup'); + const apmScreenshots = apmTrans?.startSpan('screenshots-pipeline', 'setup'); let apmBuffer: typeof apm.currentSpan; const screenshots$ = getScreenshots$(captureConfig, browserDriverFactory, { logger, @@ -47,7 +48,7 @@ export async function generatePngObservableFactory(reporting: ReportingCore) { }).pipe( tap(() => { apmScreenshots?.end(); - apmBuffer = apmTrans?.startSpan('get_buffer', 'output') ?? null; + apmBuffer = apmTrans?.startSpan('get-buffer', 'output') ?? null; }), map((results: ScreenshotResults[]) => ({ buffer: results[0].screenshots[0].data, @@ -63,7 +64,7 @@ export async function generatePngObservableFactory(reporting: ReportingCore) { })), tap(({ buffer }) => { logger.debug(`PNG buffer byte length: ${buffer.byteLength}`); - apmTrans?.setLabel('byte_length', buffer.byteLength, false); + apmTrans?.setLabel('byte-length', buffer.byteLength, false); }), finalize(() => { apmBuffer?.end(); diff --git a/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts index fba6ea7b491c6..2446e7a7d1c51 100644 --- a/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts @@ -8,7 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, finalize, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; -import { PNG_JOB_TYPE } from '../../../../common/constants'; +import { PNG_JOB_TYPE, REPORTING_TRANSACTION_TYPE } from '../../../../common/constants'; import { TaskRunResult } from '../../../lib/tasks'; import { RunTaskFn, RunTaskFnFactory } from '../../../types'; import { @@ -26,8 +26,8 @@ export const runTaskFnFactory: RunTaskFnFactory> = const encryptionKey = config.get('encryptionKey'); return async function runTask(jobId, job, cancellationToken, stream) { - const apmTrans = apm.startTransaction('reporting execute_job png', 'reporting'); - const apmGetAssets = apmTrans?.startSpan('get_assets', 'setup'); + const apmTrans = apm.startTransaction('execute-job-png', REPORTING_TRANSACTION_TYPE); + const apmGetAssets = apmTrans?.startSpan('get-assets', 'setup'); let apmGeneratePng: { end: () => void } | null | undefined; const generatePngObservable = await generatePngObservableFactory(reporting); @@ -41,7 +41,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = const hashUrl = urls[0]; if (apmGetAssets) apmGetAssets.end(); - apmGeneratePng = apmTrans?.startSpan('generate_png_pipeline', 'execute'); + apmGeneratePng = apmTrans?.startSpan('generate-png-pipeline', 'execute'); return generatePngObservable( jobLogger, hashUrl, diff --git a/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.ts b/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.ts index a7478de1cc96e..00652309b88c1 100644 --- a/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.ts @@ -8,7 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, finalize, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; -import { PNG_JOB_TYPE_V2 } from '../../../common/constants'; +import { PNG_JOB_TYPE_V2, REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; import { TaskRunResult } from '../../lib/tasks'; import { RunTaskFn, RunTaskFnFactory } from '../../types'; import { @@ -26,8 +26,8 @@ export const runTaskFnFactory: RunTaskFnFactory> = const encryptionKey = config.get('encryptionKey'); return async function runTask(jobId, job, cancellationToken, stream) { - const apmTrans = apm.startTransaction('reporting execute_job pngV2', 'reporting'); - const apmGetAssets = apmTrans?.startSpan('get_assets', 'setup'); + const apmTrans = apm.startTransaction('execute-job-png-v2', REPORTING_TRANSACTION_TYPE); + const apmGetAssets = apmTrans?.startSpan('get-assets', 'setup'); let apmGeneratePng: { end: () => void } | null | undefined; const generatePngObservable = await generatePngObservableFactory(reporting); @@ -42,7 +42,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = apmGetAssets?.end(); - apmGeneratePng = apmTrans?.startSpan('generate_png_pipeline', 'execute'); + apmGeneratePng = apmTrans?.startSpan('generate-png-pipeline', 'execute'); return generatePngObservable( jobLogger, [url, locatorParams], diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts index f4b95e0e20e51..2358333bbe7ef 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts @@ -8,7 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; -import { PDF_JOB_TYPE } from '../../../../common/constants'; +import { PDF_JOB_TYPE, REPORTING_TRANSACTION_TYPE } from '../../../../common/constants'; import { TaskRunResult } from '../../../lib/tasks'; import { RunTaskFn, RunTaskFnFactory } from '../../../types'; import { @@ -28,8 +28,8 @@ export const runTaskFnFactory: RunTaskFnFactory> = return async function runTask(jobId, job, cancellationToken, stream) { const jobLogger = parentLogger.clone([PDF_JOB_TYPE, 'execute-job', jobId]); - const apmTrans = apm.startTransaction('reporting execute_job pdf', 'reporting'); - const apmGetAssets = apmTrans?.startSpan('get_assets', 'setup'); + const apmTrans = apm.startTransaction('execute-job-pdf', REPORTING_TRANSACTION_TYPE); + const apmGetAssets = apmTrans?.startSpan('get-assets', 'setup'); let apmGeneratePdf: { end: () => void } | null | undefined; const generatePdfObservable = await generatePdfObservableFactory(reporting); @@ -47,7 +47,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = const { browserTimezone, layout, title } = job; apmGetAssets?.end(); - apmGeneratePdf = apmTrans?.startSpan('generate_pdf_pipeline', 'execute'); + apmGeneratePdf = apmTrans?.startSpan('generate-pdf-pipeline', 'execute'); return generatePdfObservable( jobLogger, title, diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/tracker.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/tracker.ts index 4b5a0a7bdade7..3d720ccade546 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/tracker.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/tracker.ts @@ -6,6 +6,7 @@ */ import apm from 'elastic-apm-node'; +import { REPORTING_TRANSACTION_TYPE } from '../../../../common/constants'; interface PdfTracker { setByteLength: (byteLength: number) => void; @@ -32,7 +33,7 @@ interface ApmSpan { } export function getTracker(): PdfTracker { - const apmTrans = apm.startTransaction('reporting generate_pdf', 'reporting'); + const apmTrans = apm.startTransaction('generate-pdf', REPORTING_TRANSACTION_TYPE); let apmLayout: ApmSpan | null = null; let apmScreenshots: ApmSpan | null = null; @@ -43,43 +44,43 @@ export function getTracker(): PdfTracker { return { startLayout() { - apmLayout = apmTrans?.startSpan('create_layout', SPANTYPE_SETUP) || null; + apmLayout = apmTrans?.startSpan('create-layout', SPANTYPE_SETUP) || null; }, endLayout() { if (apmLayout) apmLayout.end(); }, startScreenshots() { - apmScreenshots = apmTrans?.startSpan('screenshots_pipeline', SPANTYPE_SETUP) || null; + apmScreenshots = apmTrans?.startSpan('screenshots-pipeline', SPANTYPE_SETUP) || null; }, endScreenshots() { if (apmScreenshots) apmScreenshots.end(); }, startSetup() { - apmSetup = apmTrans?.startSpan('setup_pdf', SPANTYPE_SETUP) || null; + apmSetup = apmTrans?.startSpan('setup-pdf', SPANTYPE_SETUP) || null; }, endSetup() { if (apmSetup) apmSetup.end(); }, startAddImage() { - apmAddImage = apmTrans?.startSpan('add_pdf_image', SPANTYPE_OUTPUT) || null; + apmAddImage = apmTrans?.startSpan('add-pdf-image', SPANTYPE_OUTPUT) || null; }, endAddImage() { if (apmAddImage) apmAddImage.end(); }, startCompile() { - apmCompilePdf = apmTrans?.startSpan('compile_pdf', SPANTYPE_OUTPUT) || null; + apmCompilePdf = apmTrans?.startSpan('compile-pdf', SPANTYPE_OUTPUT) || null; }, endCompile() { if (apmCompilePdf) apmCompilePdf.end(); }, startGetBuffer() { - apmGetBuffer = apmTrans?.startSpan('get_buffer', SPANTYPE_OUTPUT) || null; + apmGetBuffer = apmTrans?.startSpan('get-buffer', SPANTYPE_OUTPUT) || null; }, endGetBuffer() { if (apmGetBuffer) apmGetBuffer.end(); }, setByteLength(byteLength: number) { - apmTrans?.setLabel('byte_length', byteLength, false); + apmTrans?.setLabel('byte-length', byteLength, false); }, end() { if (apmTrans) apmTrans.end(); diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.ts index 2c553295aa840..b1b6f3f79aee3 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.ts @@ -8,7 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; -import { PDF_JOB_TYPE_V2 } from '../../../common/constants'; +import { PDF_JOB_TYPE_V2, REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; import { TaskRunResult } from '../../lib/tasks'; import { RunTaskFn, RunTaskFnFactory } from '../../types'; import { @@ -27,8 +27,8 @@ export const runTaskFnFactory: RunTaskFnFactory> = return async function runTask(jobId, job, cancellationToken, stream) { const jobLogger = parentLogger.clone([PDF_JOB_TYPE_V2, 'execute-job', jobId]); - const apmTrans = apm.startTransaction('reporting execute_job pdf_v2', 'reporting'); - const apmGetAssets = apmTrans?.startSpan('get_assets', 'setup'); + const apmTrans = apm.startTransaction('execute-job-pdf-v2', REPORTING_TRANSACTION_TYPE); + const apmGetAssets = apmTrans?.startSpan('get-assets', 'setup'); let apmGeneratePdf: { end: () => void } | null | undefined; const generatePdfObservable = await generatePdfObservableFactory(reporting); @@ -44,7 +44,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = const { browserTimezone, layout, title, locatorParams } = job; apmGetAssets?.end(); - apmGeneratePdf = apmTrans?.startSpan('generate_pdf_pipeline', 'execute'); + apmGeneratePdf = apmTrans?.startSpan('generate-pdf-pipeline', 'execute'); return generatePdfObservable( jobLogger, job, diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/tracker.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/tracker.ts index 4b5a0a7bdade7..3d720ccade546 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/tracker.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/tracker.ts @@ -6,6 +6,7 @@ */ import apm from 'elastic-apm-node'; +import { REPORTING_TRANSACTION_TYPE } from '../../../../common/constants'; interface PdfTracker { setByteLength: (byteLength: number) => void; @@ -32,7 +33,7 @@ interface ApmSpan { } export function getTracker(): PdfTracker { - const apmTrans = apm.startTransaction('reporting generate_pdf', 'reporting'); + const apmTrans = apm.startTransaction('generate-pdf', REPORTING_TRANSACTION_TYPE); let apmLayout: ApmSpan | null = null; let apmScreenshots: ApmSpan | null = null; @@ -43,43 +44,43 @@ export function getTracker(): PdfTracker { return { startLayout() { - apmLayout = apmTrans?.startSpan('create_layout', SPANTYPE_SETUP) || null; + apmLayout = apmTrans?.startSpan('create-layout', SPANTYPE_SETUP) || null; }, endLayout() { if (apmLayout) apmLayout.end(); }, startScreenshots() { - apmScreenshots = apmTrans?.startSpan('screenshots_pipeline', SPANTYPE_SETUP) || null; + apmScreenshots = apmTrans?.startSpan('screenshots-pipeline', SPANTYPE_SETUP) || null; }, endScreenshots() { if (apmScreenshots) apmScreenshots.end(); }, startSetup() { - apmSetup = apmTrans?.startSpan('setup_pdf', SPANTYPE_SETUP) || null; + apmSetup = apmTrans?.startSpan('setup-pdf', SPANTYPE_SETUP) || null; }, endSetup() { if (apmSetup) apmSetup.end(); }, startAddImage() { - apmAddImage = apmTrans?.startSpan('add_pdf_image', SPANTYPE_OUTPUT) || null; + apmAddImage = apmTrans?.startSpan('add-pdf-image', SPANTYPE_OUTPUT) || null; }, endAddImage() { if (apmAddImage) apmAddImage.end(); }, startCompile() { - apmCompilePdf = apmTrans?.startSpan('compile_pdf', SPANTYPE_OUTPUT) || null; + apmCompilePdf = apmTrans?.startSpan('compile-pdf', SPANTYPE_OUTPUT) || null; }, endCompile() { if (apmCompilePdf) apmCompilePdf.end(); }, startGetBuffer() { - apmGetBuffer = apmTrans?.startSpan('get_buffer', SPANTYPE_OUTPUT) || null; + apmGetBuffer = apmTrans?.startSpan('get-buffer', SPANTYPE_OUTPUT) || null; }, endGetBuffer() { if (apmGetBuffer) apmGetBuffer.end(); }, setByteLength(byteLength: number) { - apmTrans?.setLabel('byte_length', byteLength, false); + apmTrans?.setLabel('byte-length', byteLength, false); }, end() { if (apmTrans) apmTrans.end(); diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable.ts index d400c423c5e04..8ba2a125a5504 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable.ts @@ -9,6 +9,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, concatMap, first, mergeMap, take, takeUntil, toArray } from 'rxjs/operators'; import { durationToNumber } from '../../../common/schema_utils'; +import { REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; import { HeadlessChromiumDriverFactory } from '../../browsers'; import { CaptureConfig } from '../../types'; import { @@ -46,8 +47,8 @@ export function getScreenshots$( browserDriverFactory: HeadlessChromiumDriverFactory, opts: ScreenshotObservableOpts ): Rx.Observable { - const apmTrans = apm.startTransaction(`reporting screenshot pipeline`, 'reporting'); - const apmCreatePage = apmTrans?.startSpan('create_page', 'wait'); + const apmTrans = apm.startTransaction('screenshot-pipeline', REPORTING_TRANSACTION_TYPE); + const apmCreatePage = apmTrans?.startSpan('create-page', 'wait'); const { browserTimezone, logger } = opts; return browserDriverFactory.createPage({ browserTimezone }, logger).pipe( diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts index cdbddb8d89c89..c241a529818fa 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts @@ -110,7 +110,7 @@ export class ScreenshotObservableHandler { // allows for them to be displayed properly in many cases await injectCustomCss(driver, layout, logger); - const apmPositionElements = apmTrans?.startSpan('position_elements', 'correction'); + const apmPositionElements = apmTrans?.startSpan('position-elements', 'correction'); // position panel elements for print layout await layout.positionElements?.(driver, logger); apmPositionElements?.end(); diff --git a/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts b/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts index eed8858dc95d8..ed656b5144956 100644 --- a/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts +++ b/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts @@ -17,10 +17,16 @@ import { asOk, asErr } from '../lib/result_type'; import { TaskTypeDictionary } from '../task_type_dictionary'; import type { MustNotCondition } from '../queries/query_clauses'; import { mockLogger } from '../test_utils'; -import { TaskClaiming, OwnershipClaimingOpts, TaskClaimingOpts } from './task_claiming'; +import { + TaskClaiming, + OwnershipClaimingOpts, + TaskClaimingOpts, + TASK_MANAGER_MARK_AS_CLAIMED, +} from './task_claiming'; import { Observable } from 'rxjs'; import { taskStoreMock } from '../task_store.mock'; import apm from 'elastic-apm-node'; +import { TASK_MANAGER_TRANSACTION_TYPE } from '../task_running'; const taskManagerLogger = mockLogger(); @@ -190,8 +196,8 @@ describe('TaskClaiming', () => { const results = await getAllAsPromise(taskClaiming.claimAvailableTasks(claimingOpts)); expect(apm.startTransaction).toHaveBeenCalledWith( - 'markAvailableTasksAsClaimed', - 'taskManager markAvailableTasksAsClaimed' + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE ); expect(mockApmTrans.end).toHaveBeenCalledWith('success'); @@ -250,8 +256,8 @@ describe('TaskClaiming', () => { ).rejects.toMatchInlineSnapshot(`[Error: Oh no]`); expect(apm.startTransaction).toHaveBeenCalledWith( - 'markAvailableTasksAsClaimed', - 'taskManager markAvailableTasksAsClaimed' + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE ); expect(mockApmTrans.end).toHaveBeenCalledWith('failure'); }); diff --git a/x-pack/plugins/task_manager/server/queries/task_claiming.ts b/x-pack/plugins/task_manager/server/queries/task_claiming.ts index fb0b92e87a424..b45591a233e19 100644 --- a/x-pack/plugins/task_manager/server/queries/task_claiming.ts +++ b/x-pack/plugins/task_manager/server/queries/task_claiming.ts @@ -52,6 +52,7 @@ import { SearchOpts, } from '../task_store'; import { FillPoolResult } from '../lib/fill_pool'; +import { TASK_MANAGER_TRANSACTION_TYPE } from '../task_running'; export interface TaskClaimingOpts { logger: Logger; @@ -106,6 +107,8 @@ interface TaskClaimingBatch { type UnlimitedBatch = TaskClaimingBatch>; type LimitedBatch = TaskClaimingBatch; +export const TASK_MANAGER_MARK_AS_CLAIMED = 'mark-available-tasks-as-claimed'; + export class TaskClaiming { public readonly errors$ = new Subject(); public readonly maxAttempts: number; @@ -412,9 +415,10 @@ export class TaskClaiming { ); const apmTrans = apm.startTransaction( - 'markAvailableTasksAsClaimed', - `taskManager markAvailableTasksAsClaimed` + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE ); + try { const result = await this.taskStore.updateByQuery( { diff --git a/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts b/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts index 0695ed149c9a4..0085329cd66e6 100644 --- a/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts @@ -48,6 +48,9 @@ import { TaskRunner, TaskRunningInstance, TaskRunResult, + TASK_MANAGER_RUN_TRANSACTION_TYPE, + TASK_MANAGER_TRANSACTION_TYPE, + TASK_MANAGER_TRANSACTION_TYPE_MARK_AS_RUNNING, } from './task_runner'; type Opts = { @@ -206,9 +209,11 @@ export class EphemeralTaskManagerRunner implements TaskRunner { ); } this.logger.debug(`Running ephemeral task ${this}`); - const apmTrans = apm.startTransaction(this.taskType, 'taskManager ephemeral run', { + const apmTrans = apm.startTransaction(this.taskType, TASK_MANAGER_RUN_TRANSACTION_TYPE, { childOf: this.instance.task.traceparent, }); + apmTrans?.addLabels({ ephemeral: true }); + const modifiedContext = await this.beforeRun({ taskInstance: asConcreteInstance(this.instance.task), }); @@ -261,7 +266,11 @@ export class EphemeralTaskManagerRunner implements TaskRunner { ); } - const apmTrans = apm.startTransaction('taskManager', 'taskManager markTaskAsRunning'); + const apmTrans = apm.startTransaction( + TASK_MANAGER_TRANSACTION_TYPE_MARK_AS_RUNNING, + TASK_MANAGER_TRANSACTION_TYPE + ); + apmTrans?.addLabels({ entityId: this.taskType }); const now = new Date(); try { diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts index 02be86c3db0c2..1408fadc102e4 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts @@ -26,6 +26,11 @@ import { throwUnrecoverableError } from './errors'; import { taskStoreMock } from '../task_store.mock'; import apm from 'elastic-apm-node'; import { executionContextServiceMock } from '../../../../../src/core/server/mocks'; +import { + TASK_MANAGER_RUN_TRANSACTION_TYPE, + TASK_MANAGER_TRANSACTION_TYPE, + TASK_MANAGER_TRANSACTION_TYPE_MARK_AS_RUNNING, +} from './task_runner'; const executionContext = executionContextServiceMock.createSetupContract(); const minutesFromNow = (mins: number): Date => secondsFromNow(mins * 60); @@ -47,6 +52,8 @@ describe('TaskManagerRunner', () => { const readyToRunStageSetup = (opts: TestOpts) => testOpts(TaskRunningStage.READY_TO_RUN, opts); const mockApmTrans = { end: jest.fn(), + addLabels: jest.fn(), + setLabel: jest.fn(), }; test('execution ID', async () => { @@ -88,8 +95,8 @@ describe('TaskManagerRunner', () => { }); await runner.markTaskAsRunning(); expect(apm.startTransaction).toHaveBeenCalledWith( - 'taskManager', - 'taskManager markTaskAsRunning' + TASK_MANAGER_TRANSACTION_TYPE_MARK_AS_RUNNING, + TASK_MANAGER_TRANSACTION_TYPE ); expect(mockApmTrans.end).toHaveBeenCalledWith('success'); }); @@ -117,8 +124,8 @@ describe('TaskManagerRunner', () => { ); // await runner.markTaskAsRunning(); expect(apm.startTransaction).toHaveBeenCalledWith( - 'taskManager', - 'taskManager markTaskAsRunning' + TASK_MANAGER_TRANSACTION_TYPE_MARK_AS_RUNNING, + TASK_MANAGER_TRANSACTION_TYPE ); expect(mockApmTrans.end).toHaveBeenCalledWith('failure'); }); @@ -706,7 +713,7 @@ describe('TaskManagerRunner', () => { }, }); await runner.run(); - expect(apm.startTransaction).toHaveBeenCalledWith('bar', 'taskManager run', { + expect(apm.startTransaction).toHaveBeenCalledWith('bar', TASK_MANAGER_RUN_TRANSACTION_TYPE, { childOf: 'apmTraceparent', }); expect(mockApmTrans.end).toHaveBeenCalledWith('success'); @@ -729,7 +736,7 @@ describe('TaskManagerRunner', () => { }, }); await runner.run(); - expect(apm.startTransaction).toHaveBeenCalledWith('bar', 'taskManager run', { + expect(apm.startTransaction).toHaveBeenCalledWith('bar', TASK_MANAGER_RUN_TRANSACTION_TYPE, { childOf: 'apmTraceparent', }); expect(mockApmTrans.end).toHaveBeenCalledWith('failure'); diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.ts index cd07b4db728c4..cf5ee80d2a82a 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.ts @@ -60,6 +60,10 @@ import { isUnrecoverableError } from './errors'; const defaultBackoffPerFailure = 5 * 60 * 1000; export const EMPTY_RUN_RESULT: SuccessfulRunResult = { state: {} }; +export const TASK_MANAGER_RUN_TRANSACTION_TYPE = 'task-run'; +export const TASK_MANAGER_TRANSACTION_TYPE = 'task-manager'; +export const TASK_MANAGER_TRANSACTION_TYPE_MARK_AS_RUNNING = 'mark-task-as-running'; + export interface TaskRunner { isExpired: boolean; expiration: Date; @@ -276,7 +280,7 @@ export class TaskManagerRunner implements TaskRunner { } this.logger.debug(`Running task ${this}`); - const apmTrans = apm.startTransaction(this.taskType, 'taskManager run', { + const apmTrans = apm.startTransaction(this.taskType, TASK_MANAGER_RUN_TRANSACTION_TYPE, { childOf: this.instance.task.traceparent, }); @@ -333,7 +337,11 @@ export class TaskManagerRunner implements TaskRunner { ); } - const apmTrans = apm.startTransaction('taskManager', 'taskManager markTaskAsRunning'); + const apmTrans = apm.startTransaction( + TASK_MANAGER_TRANSACTION_TYPE_MARK_AS_RUNNING, + TASK_MANAGER_TRANSACTION_TYPE + ); + apmTrans?.addLabels({ entityId: this.taskType }); const now = new Date(); try { From 41cc382269afecee2de4da60238c1959cddbbeb7 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Thu, 2 Dec 2021 11:42:14 -0600 Subject: [PATCH 09/65] [Enterprise Search] Remove `external_id` from UI code (#120143) * [Enterprise Search] Remove `external_id` from UI code Ports https://github.com/elastic/ent-search/pull/5441 to Kibana. `external_id` was an alias for regular `id` was removed in the Enterprise Search API. * Add path to logic file Not really related to this PR, but needed for debugging in the Redux dev tools * Rename missed argument name Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/schema/reindex_job/reindex_job_logic.test.ts | 4 ++-- .../shared/schema/errors_accordion/index.test.tsx | 4 ++-- .../applications/shared/schema/errors_accordion/index.tsx | 4 ++-- .../public/applications/shared/schema/types.ts | 2 +- .../workplace_search/__mocks__/content_sources.mock.ts | 2 +- .../public/applications/workplace_search/types.ts | 2 +- .../components/display_settings/display_settings_logic.ts | 1 + .../content_sources/components/schema/schema_logic.test.ts | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/schema/reindex_job/reindex_job_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/schema/reindex_job/reindex_job_logic.test.ts index 706dc3549badc..bb8156a8b55fa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/schema/reindex_job/reindex_job_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/schema/reindex_job/reindex_job_logic.test.ts @@ -25,13 +25,13 @@ describe('ReindexJobLogic', () => { fieldCoercionErrors: { some_erroring_field: [ { - external_id: 'document-1', + id: 'document-1', error: "Value 'some text' cannot be parsed as a number", }, ], another_erroring_field: [ { - external_id: 'document-2', + id: 'document-2', error: "Value '123' cannot be parsed as a date", }, ], diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.test.tsx index 5413da9d56161..19d04ea3de17e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.test.tsx @@ -22,11 +22,11 @@ describe('SchemaErrorsAccordion', () => { fieldCoercionErrors: { id: [ { - external_id: 'foo', + id: 'foo', error: 'this is an error', }, { - external_id: 'bar', + id: 'bar', error: 'this is another error', }, ], diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.tsx index 35675d246a15e..99f3d30c6caae 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.tsx @@ -36,7 +36,7 @@ import './schema_errors_accordion.scss'; interface Props { fieldCoercionErrors: FieldCoercionErrors; schema: Schema; - generateViewPath?(externalId: string): string; + generateViewPath?(id: string): string; } export const SchemaErrorsAccordion: React.FC = ({ @@ -89,7 +89,7 @@ export const SchemaErrorsAccordion: React.FC = ({ {errors.map((error) => { - const { external_id: id, error: errorMessage } = error; + const { id, error: errorMessage } = error; return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/schema/types.ts index 5a423ad510af1..58ad584fd5b60 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/types.ts @@ -58,7 +58,7 @@ export interface IndexJob extends IIndexingStatus { } export interface FieldCoercionError { - external_id: string; + id: string; error: string; } export type FieldCoercionErrors = Record; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts index 48cbf4ba00d87..85ffde0acfea3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts @@ -370,7 +370,7 @@ export const exampleResult = { myLink: 'http://foo', otherTitle: 'foo', content_source_id: '60e85e7ea2564c265a88a4f0', - external_id: 'doc-60e85eb7a2564c937a88a4f3', + id: 'doc-60e85eb7a2564c937a88a4f3', last_updated: '2021-07-09T14:35:35+00:00', updated_at: '2021-07-09T14:35:35+00:00', source: 'custom', diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts index 2a982c2be1cfc..0fa8c00409d1a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts @@ -257,7 +257,7 @@ export type CustomAPIFieldValue = export interface Result { content_source_id: string; last_updated: string; - external_id: string; + id: string; updated_at: string; source: string; [key: string]: CustomAPIFieldValue; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts index d62bd6252f130..f7076b4d02afb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts @@ -123,6 +123,7 @@ export const defaultSearchResultConfig = { export const DisplaySettingsLogic = kea< MakeLogicType >({ + path: ['enterprise_search', 'workplace_search', 'display_settings_logic'], actions: { onInitializeDisplaySettings: (displaySettingsProps: DisplaySettingsInitialData) => displaySettingsProps, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.test.ts index d284f5c741eb3..748e5068833cf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.test.ts @@ -67,7 +67,7 @@ describe('SchemaLogic', () => { const fieldCoercionErrors = [ { - external_id: '123', + id: '123', error: 'error', }, ] as any; From 392325ceae5de27739b7967e706a98866b259d93 Mon Sep 17 00:00:00 2001 From: Oleksiy Kovyrin Date: Thu, 2 Dec 2021 13:29:31 -0500 Subject: [PATCH 10/65] Remove links to docs about different auth methods in Enterprise Search (#120127) * Remove links to docs about different auth methods in Enterprise Search --- ...-plugin-core-public.doclinksstart.links.md | 4 - ...kibana-plugin-core-public.doclinksstart.md | 2 +- .../public/doc_links/doc_links_service.ts | 8 -- src/core/public/public.api.md | 4 - .../components/setup_guide/setup_guide.tsx | 8 +- .../public/applications/app_search/routes.ts | 2 - .../components/setup_guide/setup_guide.tsx | 3 - .../shared/doc_links/doc_links.ts | 12 --- .../shared/setup_guide/instructions.test.tsx | 17 +--- .../shared/setup_guide/instructions.tsx | 80 +------------------ .../shared/setup_guide/setup_guide.tsx | 16 +--- .../applications/workplace_search/routes.ts | 2 - .../views/setup_guide/setup_guide.tsx | 8 +- .../translations/translations/ja-JP.json | 5 -- .../translations/translations/zh-CN.json | 5 -- 15 files changed, 7 insertions(+), 169 deletions(-) diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 7669b9b644916..403d8594999a7 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -72,13 +72,11 @@ readonly links: { readonly indexingDocumentsSchema: string; readonly logSettings: string; readonly metaEngines: string; - readonly nativeAuth: string; readonly precisionTuning: string; readonly relevanceTuning: string; readonly resultSettings: string; readonly searchUI: string; readonly security: string; - readonly standardAuth: string; readonly synonyms: string; readonly webCrawler: string; readonly webCrawlerEventLogs: string; @@ -105,7 +103,6 @@ readonly links: { readonly indexingSchedule: string; readonly jiraCloud: string; readonly jiraServer: string; - readonly nativeAuth: string; readonly oneDrive: string; readonly permissions: string; readonly salesforce: string; @@ -113,7 +110,6 @@ readonly links: { readonly serviceNow: string; readonly sharePoint: string; readonly slack: string; - readonly standardAuth: string; readonly synch: string; readonly zendesk: string; }; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 6aa528d4f04d1..131d4452c980c 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | { readonly settings: string; readonly elasticStackGetStarted: string; readonly upgrade: { readonly upgradingElasticStack: string; }; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; readonly customLinks: string; readonly droppedTransactionSpans: string; readonly upgrading: string; readonly metaData: string; }; readonly canvas: { readonly guide: string; }; readonly cloud: { readonly indexManagement: string; }; readonly dashboard: { readonly guide: string; readonly drilldowns: string; readonly drilldownsTriggerPicker: string; readonly urlDrilldownTemplateSyntax: string; readonly urlDrilldownVariables: string; }; readonly discover: Record<string, string>; readonly filebeat: { readonly base: string; readonly installation: string; readonly configuration: string; readonly elasticsearchOutput: string; readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; readonly suricataModule: string; readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; readonly auditdModule: string; readonly systemModule: string; }; readonly metricbeat: { readonly base: string; readonly configure: string; readonly httpEndpoint: string; readonly install: string; readonly start: string; }; readonly appSearch: { readonly apiRef: string; readonly apiClients: string; readonly apiKeys: string; readonly authentication: string; readonly crawlRules: string; readonly curations: string; readonly duplicateDocuments: string; readonly entryPoints: string; readonly guide: string; readonly indexingDocuments: string; readonly indexingDocumentsSchema: string; readonly logSettings: string; readonly metaEngines: string; readonly nativeAuth: string; readonly precisionTuning: string; readonly relevanceTuning: string; readonly resultSettings: string; readonly searchUI: string; readonly security: string; readonly standardAuth: string; readonly synonyms: string; readonly webCrawler: string; readonly webCrawlerEventLogs: string; }; readonly enterpriseSearch: { readonly configuration: string; readonly licenseManagement: string; readonly mailService: string; readonly usersAccess: string; }; readonly workplaceSearch: { readonly box: string; readonly confluenceCloud: string; readonly confluenceServer: string; readonly customSources: string; readonly customSourcePermissions: string; readonly documentPermissions: string; readonly dropbox: string; readonly externalIdentities: string; readonly gitHub: string; readonly gettingStarted: string; readonly gmail: string; readonly googleDrive: string; readonly indexingSchedule: string; readonly jiraCloud: string; readonly jiraServer: string; readonly nativeAuth: string; readonly oneDrive: string; readonly permissions: string; readonly salesforce: string; readonly security: string; readonly serviceNow: string; readonly sharePoint: string; readonly slack: string; readonly standardAuth: string; readonly synch: string; readonly zendesk: string; }; readonly heartbeat: { readonly base: string; }; readonly libbeat: { readonly getStarted: string; }; readonly logstash: { readonly base: string; }; readonly functionbeat: { readonly base: string; }; readonly winlogbeat: { readonly base: string; }; readonly aggs: { readonly composite: string; readonly composite\_missing\_bucket: string; readonly date\_histogram: string; readonly date\_range: string; readonly date\_format\_pattern: string; readonly filter: string; readonly filters: string; readonly geohash\_grid: string; readonly histogram: string; readonly ip\_range: string; readonly range: string; readonly significant\_terms: string; readonly terms: string; readonly terms\_doc\_count\_error: string; readonly avg: string; readonly avg\_bucket: string; readonly max\_bucket: string; readonly min\_bucket: string; readonly sum\_bucket: string; readonly cardinality: string; readonly count: string; readonly cumulative\_sum: string; readonly derivative: string; readonly geo\_bounds: string; readonly geo\_centroid: string; readonly max: string; readonly median: string; readonly min: string; readonly moving\_avg: string; readonly percentile\_ranks: string; readonly serial\_diff: string; readonly std\_dev: string; readonly sum: string; readonly top\_hits: string; }; readonly runtimeFields: { readonly overview: string; readonly mapping: string; }; readonly scriptedFields: { readonly scriptFields: string; readonly scriptAggs: string; readonly painless: string; readonly painlessApi: string; readonly painlessLangSpec: string; readonly painlessSyntax: string; readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; readonly search: { readonly sessions: string; readonly sessionLimits: string; }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; readonly upgradeAssistant: { readonly overview: string; readonly batchReindex: string; readonly remoteReindex: string; }; readonly rollupJobs: string; readonly elasticsearch: Record<string, string>; readonly siem: { readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; readonly troubleshootGaps: string; }; readonly securitySolution: { readonly trustedApps: string; }; readonly query: { readonly eql: string; readonly kueryQuerySyntax: string; readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; }; readonly date: { readonly dateMath: string; readonly dateMathIndexNames: string; }; readonly management: Record<string, string>; readonly ml: Record<string, string>; readonly transforms: Record<string, string>; readonly visualize: Record<string, string>; readonly apis: Readonly<{ bulkIndexAlias: string; byteSizeUnits: string; createAutoFollowPattern: string; createFollower: string; createIndex: string; createSnapshotLifecyclePolicy: string; createRoleMapping: string; createRoleMappingTemplates: string; createRollupJobsRequest: string; createApiKey: string; createPipeline: string; createTransformRequest: string; cronExpressions: string; executeWatchActionModes: string; indexExists: string; openIndex: string; putComponentTemplate: string; painlessExecute: string; painlessExecuteAPIContexts: string; putComponentTemplateMetadata: string; putSnapshotLifecyclePolicy: string; putIndexTemplateV1: string; putWatch: string; simulatePipeline: string; timeUnits: string; updateTransform: string; }>; readonly observability: Readonly<{ guide: string; infrastructureThreshold: string; logsThreshold: string; metricsThreshold: string; monitorStatus: string; monitorUptime: string; tlsCertificate: string; uptimeDurationAnomaly: string; }>; readonly alerting: Record<string, string>; readonly maps: Readonly<{ guide: string; importGeospatialPrivileges: string; gdalTutorial: string; }>; readonly monitoring: Record<string, string>; readonly security: Readonly<{ apiKeyServiceSettings: string; clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; elasticsearchEnableApiKeys: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; mappingRoles: string; mappingRolesFieldRules: string; runAsPrivilege: string; }>; readonly spaces: Readonly<{ kibanaLegacyUrlAliases: string; kibanaDisableLegacyUrlAliasesApi: string; }>; readonly watcher: Record<string, string>; readonly ccs: Record<string, string>; readonly plugins: Record<string, string>; readonly snapshotRestore: Record<string, string>; readonly ingest: Record<string, string>; readonly fleet: Readonly<{ beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; settings: string; settingsFleetServerHostSettings: string; settingsFleetServerProxySettings: string; troubleshooting: string; elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; installElasticAgent: string; installElasticAgentStandalone: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; apiKeysLearnMore: string; onPremRegistry: string; }>; readonly ecs: { readonly guide: string; }; readonly clients: { readonly guide: string; readonly goOverview: string; readonly javaIndex: string; readonly jsIntro: string; readonly netGuide: string; readonly perlGuide: string; readonly phpGuide: string; readonly pythonGuide: string; readonly rubyOverview: string; readonly rustGuide: string; }; readonly endpoints: { readonly troubleshooting: string; }; } | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | { readonly settings: string; readonly elasticStackGetStarted: string; readonly upgrade: { readonly upgradingElasticStack: string; }; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; readonly customLinks: string; readonly droppedTransactionSpans: string; readonly upgrading: string; readonly metaData: string; }; readonly canvas: { readonly guide: string; }; readonly cloud: { readonly indexManagement: string; }; readonly dashboard: { readonly guide: string; readonly drilldowns: string; readonly drilldownsTriggerPicker: string; readonly urlDrilldownTemplateSyntax: string; readonly urlDrilldownVariables: string; }; readonly discover: Record<string, string>; readonly filebeat: { readonly base: string; readonly installation: string; readonly configuration: string; readonly elasticsearchOutput: string; readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; readonly suricataModule: string; readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; readonly auditdModule: string; readonly systemModule: string; }; readonly metricbeat: { readonly base: string; readonly configure: string; readonly httpEndpoint: string; readonly install: string; readonly start: string; }; readonly appSearch: { readonly apiRef: string; readonly apiClients: string; readonly apiKeys: string; readonly authentication: string; readonly crawlRules: string; readonly curations: string; readonly duplicateDocuments: string; readonly entryPoints: string; readonly guide: string; readonly indexingDocuments: string; readonly indexingDocumentsSchema: string; readonly logSettings: string; readonly metaEngines: string; readonly precisionTuning: string; readonly relevanceTuning: string; readonly resultSettings: string; readonly searchUI: string; readonly security: string; readonly synonyms: string; readonly webCrawler: string; readonly webCrawlerEventLogs: string; }; readonly enterpriseSearch: { readonly configuration: string; readonly licenseManagement: string; readonly mailService: string; readonly usersAccess: string; }; readonly workplaceSearch: { readonly box: string; readonly confluenceCloud: string; readonly confluenceServer: string; readonly customSources: string; readonly customSourcePermissions: string; readonly documentPermissions: string; readonly dropbox: string; readonly externalIdentities: string; readonly gitHub: string; readonly gettingStarted: string; readonly gmail: string; readonly googleDrive: string; readonly indexingSchedule: string; readonly jiraCloud: string; readonly jiraServer: string; readonly oneDrive: string; readonly permissions: string; readonly salesforce: string; readonly security: string; readonly serviceNow: string; readonly sharePoint: string; readonly slack: string; readonly synch: string; readonly zendesk: string; }; readonly heartbeat: { readonly base: string; }; readonly libbeat: { readonly getStarted: string; }; readonly logstash: { readonly base: string; }; readonly functionbeat: { readonly base: string; }; readonly winlogbeat: { readonly base: string; }; readonly aggs: { readonly composite: string; readonly composite\_missing\_bucket: string; readonly date\_histogram: string; readonly date\_range: string; readonly date\_format\_pattern: string; readonly filter: string; readonly filters: string; readonly geohash\_grid: string; readonly histogram: string; readonly ip\_range: string; readonly range: string; readonly significant\_terms: string; readonly terms: string; readonly terms\_doc\_count\_error: string; readonly avg: string; readonly avg\_bucket: string; readonly max\_bucket: string; readonly min\_bucket: string; readonly sum\_bucket: string; readonly cardinality: string; readonly count: string; readonly cumulative\_sum: string; readonly derivative: string; readonly geo\_bounds: string; readonly geo\_centroid: string; readonly max: string; readonly median: string; readonly min: string; readonly moving\_avg: string; readonly percentile\_ranks: string; readonly serial\_diff: string; readonly std\_dev: string; readonly sum: string; readonly top\_hits: string; }; readonly runtimeFields: { readonly overview: string; readonly mapping: string; }; readonly scriptedFields: { readonly scriptFields: string; readonly scriptAggs: string; readonly painless: string; readonly painlessApi: string; readonly painlessLangSpec: string; readonly painlessSyntax: string; readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; readonly search: { readonly sessions: string; readonly sessionLimits: string; }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; readonly upgradeAssistant: { readonly overview: string; readonly batchReindex: string; readonly remoteReindex: string; }; readonly rollupJobs: string; readonly elasticsearch: Record<string, string>; readonly siem: { readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; readonly troubleshootGaps: string; }; readonly securitySolution: { readonly trustedApps: string; }; readonly query: { readonly eql: string; readonly kueryQuerySyntax: string; readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; }; readonly date: { readonly dateMath: string; readonly dateMathIndexNames: string; }; readonly management: Record<string, string>; readonly ml: Record<string, string>; readonly transforms: Record<string, string>; readonly visualize: Record<string, string>; readonly apis: Readonly<{ bulkIndexAlias: string; byteSizeUnits: string; createAutoFollowPattern: string; createFollower: string; createIndex: string; createSnapshotLifecyclePolicy: string; createRoleMapping: string; createRoleMappingTemplates: string; createRollupJobsRequest: string; createApiKey: string; createPipeline: string; createTransformRequest: string; cronExpressions: string; executeWatchActionModes: string; indexExists: string; openIndex: string; putComponentTemplate: string; painlessExecute: string; painlessExecuteAPIContexts: string; putComponentTemplateMetadata: string; putSnapshotLifecyclePolicy: string; putIndexTemplateV1: string; putWatch: string; simulatePipeline: string; timeUnits: string; updateTransform: string; }>; readonly observability: Readonly<{ guide: string; infrastructureThreshold: string; logsThreshold: string; metricsThreshold: string; monitorStatus: string; monitorUptime: string; tlsCertificate: string; uptimeDurationAnomaly: string; }>; readonly alerting: Record<string, string>; readonly maps: Readonly<{ guide: string; importGeospatialPrivileges: string; gdalTutorial: string; }>; readonly monitoring: Record<string, string>; readonly security: Readonly<{ apiKeyServiceSettings: string; clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; elasticsearchEnableApiKeys: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; mappingRoles: string; mappingRolesFieldRules: string; runAsPrivilege: string; }>; readonly spaces: Readonly<{ kibanaLegacyUrlAliases: string; kibanaDisableLegacyUrlAliasesApi: string; }>; readonly watcher: Record<string, string>; readonly ccs: Record<string, string>; readonly plugins: Record<string, string>; readonly snapshotRestore: Record<string, string>; readonly ingest: Record<string, string>; readonly fleet: Readonly<{ beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; settings: string; settingsFleetServerHostSettings: string; settingsFleetServerProxySettings: string; troubleshooting: string; elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; installElasticAgent: string; installElasticAgentStandalone: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; apiKeysLearnMore: string; onPremRegistry: string; }>; readonly ecs: { readonly guide: string; }; readonly clients: { readonly guide: string; readonly goOverview: string; readonly javaIndex: string; readonly jsIntro: string; readonly netGuide: string; readonly perlGuide: string; readonly phpGuide: string; readonly pythonGuide: string; readonly rubyOverview: string; readonly rustGuide: string; }; readonly endpoints: { readonly troubleshooting: string; }; } | | diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 7a599e7c84acf..f99f621a52c83 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -97,13 +97,11 @@ export class DocLinksService { indexingDocumentsSchema: `${APP_SEARCH_DOCS}indexing-documents-guide.html#indexing-documents-guide-schema`, logSettings: `${APP_SEARCH_DOCS}logs.html`, metaEngines: `${APP_SEARCH_DOCS}meta-engines-guide.html`, - nativeAuth: `${APP_SEARCH_DOCS}security-and-users.html#app-search-self-managed-security-and-user-management-elasticsearch-native-realm`, precisionTuning: `${APP_SEARCH_DOCS}precision-tuning.html`, relevanceTuning: `${APP_SEARCH_DOCS}relevance-tuning-guide.html`, resultSettings: `${APP_SEARCH_DOCS}result-settings-guide.html`, searchUI: `${APP_SEARCH_DOCS}reference-ui-guide.html`, security: `${APP_SEARCH_DOCS}security-and-users.html`, - standardAuth: `${APP_SEARCH_DOCS}security-and-users.html#app-search-self-managed-security-and-user-management-standard`, synonyms: `${APP_SEARCH_DOCS}synonyms-guide.html`, webCrawler: `${APP_SEARCH_DOCS}web-crawler.html`, webCrawlerEventLogs: `${APP_SEARCH_DOCS}view-web-crawler-events-logs.html`, @@ -130,7 +128,6 @@ export class DocLinksService { indexingSchedule: `${WORKPLACE_SEARCH_DOCS}workplace-search-customizing-indexing-rules.html#_indexing_schedule`, jiraCloud: `${WORKPLACE_SEARCH_DOCS}workplace-search-jira-cloud-connector.html`, jiraServer: `${WORKPLACE_SEARCH_DOCS}workplace-search-jira-server-connector.html`, - nativeAuth: `${WORKPLACE_SEARCH_DOCS}workplace-search-security.html#elasticsearch-native-realm`, oneDrive: `${WORKPLACE_SEARCH_DOCS}workplace-search-onedrive-connector.html`, permissions: `${WORKPLACE_SEARCH_DOCS}workplace-search-permissions.html#organizational-sources-private-sources`, salesforce: `${WORKPLACE_SEARCH_DOCS}workplace-search-salesforce-connector.html`, @@ -138,7 +135,6 @@ export class DocLinksService { serviceNow: `${WORKPLACE_SEARCH_DOCS}workplace-search-servicenow-connector.html`, sharePoint: `${WORKPLACE_SEARCH_DOCS}workplace-search-sharepoint-online-connector.html`, slack: `${WORKPLACE_SEARCH_DOCS}workplace-search-slack-connector.html`, - standardAuth: `${WORKPLACE_SEARCH_DOCS}workplace-search-security.html#standard`, synch: `${WORKPLACE_SEARCH_DOCS}workplace-search-customizing-indexing-rules.html`, zendesk: `${WORKPLACE_SEARCH_DOCS}workplace-search-zendesk-connector.html`, }, @@ -659,13 +655,11 @@ export interface DocLinksStart { readonly indexingDocumentsSchema: string; readonly logSettings: string; readonly metaEngines: string; - readonly nativeAuth: string; readonly precisionTuning: string; readonly relevanceTuning: string; readonly resultSettings: string; readonly searchUI: string; readonly security: string; - readonly standardAuth: string; readonly synonyms: string; readonly webCrawler: string; readonly webCrawlerEventLogs: string; @@ -692,7 +686,6 @@ export interface DocLinksStart { readonly indexingSchedule: string; readonly jiraCloud: string; readonly jiraServer: string; - readonly nativeAuth: string; readonly oneDrive: string; readonly permissions: string; readonly salesforce: string; @@ -700,7 +693,6 @@ export interface DocLinksStart { readonly serviceNow: string; readonly sharePoint: string; readonly slack: string; - readonly standardAuth: string; readonly synch: string; readonly zendesk: string; }; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index cec80af843c4c..9bb65a9dd0b57 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -554,13 +554,11 @@ export interface DocLinksStart { readonly indexingDocumentsSchema: string; readonly logSettings: string; readonly metaEngines: string; - readonly nativeAuth: string; readonly precisionTuning: string; readonly relevanceTuning: string; readonly resultSettings: string; readonly searchUI: string; readonly security: string; - readonly standardAuth: string; readonly synonyms: string; readonly webCrawler: string; readonly webCrawlerEventLogs: string; @@ -587,7 +585,6 @@ export interface DocLinksStart { readonly indexingSchedule: string; readonly jiraCloud: string; readonly jiraServer: string; - readonly nativeAuth: string; readonly oneDrive: string; readonly permissions: string; readonly salesforce: string; @@ -595,7 +592,6 @@ export interface DocLinksStart { readonly serviceNow: string; readonly sharePoint: string; readonly slack: string; - readonly standardAuth: string; readonly synch: string; readonly zendesk: string; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx index f1d9beaca5136..1cf1cf8a0d5bd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx @@ -15,17 +15,11 @@ import { APP_SEARCH_PLUGIN } from '../../../../../common/constants'; import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { SetupGuideLayout, SETUP_GUIDE_TITLE } from '../../../shared/setup_guide'; import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; -import { NATIVE_AUTH_DOCS_URL, STANDARD_AUTH_DOCS_URL } from '../../routes'; import GettingStarted from './assets/getting_started.png'; export const SetupGuide: React.FC = () => ( - + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts index 1f2e7c883e1cb..1a41004c882e3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts @@ -20,13 +20,11 @@ export const INDEXING_DOCS_URL = docLinks.appSearchIndexingDocs; export const INDEXING_SCHEMA_DOCS_URL = docLinks.appSearchIndexingDocsSchema; export const LOG_SETTINGS_DOCS_URL = docLinks.appSearchLogSettings; export const META_ENGINES_DOCS_URL = docLinks.appSearchMetaEngines; -export const NATIVE_AUTH_DOCS_URL = docLinks.appSearchNativeAuth; export const PRECISION_DOCS_URL = docLinks.appSearchPrecision; export const RELEVANCE_DOCS_URL = docLinks.appSearchRelevance; export const RESULT_SETTINGS_DOCS_URL = docLinks.appSearchResultSettings; export const SEARCH_UI_DOCS_URL = docLinks.appSearchSearchUI; export const SECURITY_DOCS_URL = docLinks.appSearchSecurity; -export const STANDARD_AUTH_DOCS_URL = docLinks.appSearchStandardAuth; export const SYNONYMS_DOCS_URL = docLinks.appSearchSynonyms; export const WEB_CRAWLER_DOCS_URL = docLinks.appSearchWebCrawler; export const WEB_CRAWLER_LOG_DOCS_URL = docLinks.appSearchWebCrawlerEventLogs; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx index c7c85fdd49359..1a25d1a7a8d1e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx @@ -12,7 +12,6 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { ENTERPRISE_SEARCH_PLUGIN } from '../../../../../common/constants'; -import { NATIVE_AUTH_DOCS_URL, STANDARD_AUTH_DOCS_URL } from '../../../app_search/routes'; import { SetEnterpriseSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { SetupGuideLayout, SETUP_GUIDE_TITLE } from '../../../shared/setup_guide'; import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; @@ -23,8 +22,6 @@ export const SetupGuide: React.FC = () => ( diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts index 93bead4d31f4c..8cd5b86314a2c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts @@ -21,13 +21,11 @@ class DocLinks { public appSearchIndexingDocsSchema: string; public appSearchLogSettings: string; public appSearchMetaEngines: string; - public appSearchNativeAuth: string; public appSearchPrecision: string; public appSearchRelevance: string; public appSearchResultSettings: string; public appSearchSearchUI: string; public appSearchSecurity: string; - public appSearchStandardAuth: string; public appSearchSynonyms: string; public appSearchWebCrawler: string; public appSearchWebCrawlerEventLogs: string; @@ -51,7 +49,6 @@ class DocLinks { public workplaceSearchIndexingSchedule: string; public workplaceSearchJiraCloud: string; public workplaceSearchJiraServer: string; - public workplaceSearchNativeAuth: string; public workplaceSearchOneDrive: string; public workplaceSearchPermissions: string; public workplaceSearchSalesforce: string; @@ -59,7 +56,6 @@ class DocLinks { public workplaceSearchServiceNow: string; public workplaceSearchSharePoint: string; public workplaceSearchSlack: string; - public workplaceSearchStandardAuth: string; public workplaceSearchSynch: string; public workplaceSearchZendesk: string; @@ -77,13 +73,11 @@ class DocLinks { this.appSearchIndexingDocsSchema = ''; this.appSearchLogSettings = ''; this.appSearchMetaEngines = ''; - this.appSearchNativeAuth = ''; this.appSearchPrecision = ''; this.appSearchRelevance = ''; this.appSearchResultSettings = ''; this.appSearchSearchUI = ''; this.appSearchSecurity = ''; - this.appSearchStandardAuth = ''; this.appSearchSynonyms = ''; this.appSearchWebCrawler = ''; this.appSearchWebCrawlerEventLogs = ''; @@ -107,7 +101,6 @@ class DocLinks { this.workplaceSearchIndexingSchedule = ''; this.workplaceSearchJiraCloud = ''; this.workplaceSearchJiraServer = ''; - this.workplaceSearchNativeAuth = ''; this.workplaceSearchOneDrive = ''; this.workplaceSearchPermissions = ''; this.workplaceSearchSalesforce = ''; @@ -115,7 +108,6 @@ class DocLinks { this.workplaceSearchServiceNow = ''; this.workplaceSearchSharePoint = ''; this.workplaceSearchSlack = ''; - this.workplaceSearchStandardAuth = ''; this.workplaceSearchSynch = ''; this.workplaceSearchZendesk = ''; } @@ -134,13 +126,11 @@ class DocLinks { this.appSearchIndexingDocsSchema = docLinks.links.appSearch.indexingDocumentsSchema; this.appSearchLogSettings = docLinks.links.appSearch.logSettings; this.appSearchMetaEngines = docLinks.links.appSearch.metaEngines; - this.appSearchNativeAuth = docLinks.links.appSearch.nativeAuth; this.appSearchPrecision = docLinks.links.appSearch.precisionTuning; this.appSearchRelevance = docLinks.links.appSearch.relevanceTuning; this.appSearchResultSettings = docLinks.links.appSearch.resultSettings; this.appSearchSearchUI = docLinks.links.appSearch.searchUI; this.appSearchSecurity = docLinks.links.appSearch.security; - this.appSearchStandardAuth = docLinks.links.appSearch.standardAuth; this.appSearchSynonyms = docLinks.links.appSearch.synonyms; this.appSearchWebCrawler = docLinks.links.appSearch.webCrawler; this.appSearchWebCrawlerEventLogs = docLinks.links.appSearch.webCrawlerEventLogs; @@ -165,7 +155,6 @@ class DocLinks { this.workplaceSearchIndexingSchedule = docLinks.links.workplaceSearch.indexingSchedule; this.workplaceSearchJiraCloud = docLinks.links.workplaceSearch.jiraCloud; this.workplaceSearchJiraServer = docLinks.links.workplaceSearch.jiraServer; - this.workplaceSearchNativeAuth = docLinks.links.workplaceSearch.nativeAuth; this.workplaceSearchOneDrive = docLinks.links.workplaceSearch.oneDrive; this.workplaceSearchPermissions = docLinks.links.workplaceSearch.permissions; this.workplaceSearchSalesforce = docLinks.links.workplaceSearch.salesforce; @@ -173,7 +162,6 @@ class DocLinks { this.workplaceSearchServiceNow = docLinks.links.workplaceSearch.serviceNow; this.workplaceSearchSharePoint = docLinks.links.workplaceSearch.sharePoint; this.workplaceSearchSlack = docLinks.links.workplaceSearch.slack; - this.workplaceSearchStandardAuth = docLinks.links.workplaceSearch.standardAuth; this.workplaceSearchSynch = docLinks.links.workplaceSearch.synch; this.workplaceSearchZendesk = docLinks.links.workplaceSearch.zendesk; } diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.test.tsx index 9f65e6b599a98..e162b126c0679 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.test.tsx @@ -9,9 +9,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiSteps, EuiLink } from '@elastic/eui'; - -import { mountWithIntl } from '../../test_helpers'; +import { EuiSteps } from '@elastic/eui'; import { SetupInstructions } from './instructions'; @@ -20,17 +18,4 @@ describe('SetupInstructions', () => { const wrapper = shallow(); expect(wrapper.find(EuiSteps)).toHaveLength(1); }); - - it('renders with auth links', () => { - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(EuiLink).first().prop('href')).toEqual('http://bar.com'); - expect(wrapper.find(EuiLink).last().prop('href')).toEqual('http://foo.com'); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.tsx index aa8bbd6970f86..4fc12f62305fa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.tsx @@ -9,28 +9,20 @@ import React from 'react'; import { EuiPageContent, - EuiSpacer, EuiText, EuiSteps, EuiCode, EuiCodeBlock, EuiAccordion, - EuiLink, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; interface Props { productName: string; - standardAuthLink?: string; - elasticsearchNativeAuthLink?: string; } -export const SetupInstructions: React.FC = ({ - productName, - standardAuthLink, - elasticsearchNativeAuthLink, -}) => ( +export const SetupInstructions: React.FC = ({ productName }) => ( = ({ defaultMessage="Restart Kibana to pick up the configuration changes from the previous step." />

-

- - Elasticsearch Native Auth - - ) : ( - 'Elasticsearch Native Auth' - ), - }} - /> -

), }, @@ -118,60 +94,6 @@ export const SetupInstructions: React.FC = ({

- - - -

- -

-
-
- - - -

- - Standard Auth - - ) : ( - 'Standard Auth' - ), - }} - /> -

-
-
), }, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.tsx index d041f1b294e7a..4314dc1465f94 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.tsx @@ -37,17 +37,9 @@ interface Props { children: React.ReactNode; productName: string; productEuiIcon: 'logoAppSearch' | 'logoWorkplaceSearch' | 'logoEnterpriseSearch'; - standardAuthLink?: string; - elasticsearchNativeAuthLink?: string; } -export const SetupGuideLayout: React.FC = ({ - children, - productName, - productEuiIcon, - standardAuthLink, - elasticsearchNativeAuthLink, -}) => { +export const SetupGuideLayout: React.FC = ({ children, productName, productEuiIcon }) => { const { cloud } = useValues(KibanaLogic); const isCloudEnabled = Boolean(cloud.isCloudEnabled); const cloudDeploymentLink = cloud.deploymentUrl || ''; @@ -81,11 +73,7 @@ export const SetupGuideLayout: React.FC = ({ cloudDeploymentLink={cloudDeploymentLink} /> ) : ( - + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts index b28343f37ea25..5f3c79f9432e7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts @@ -35,7 +35,6 @@ export const GMAIL_DOCS_URL = docLinks.workplaceSearchGmail; export const GOOGLE_DRIVE_DOCS_URL = docLinks.workplaceSearchGoogleDrive; export const JIRA_DOCS_URL = docLinks.workplaceSearchJiraCloud; export const JIRA_SERVER_DOCS_URL = docLinks.workplaceSearchJiraServer; -export const NATIVE_AUTH_DOCS_URL = docLinks.workplaceSearchNativeAuth; export const OBJECTS_AND_ASSETS_DOCS_URL = docLinks.workplaceSearchSynch; export const ONEDRIVE_DOCS_URL = docLinks.workplaceSearchOneDrive; export const PRIVATE_SOURCES_DOCS_URL = docLinks.workplaceSearchPermissions; @@ -44,7 +43,6 @@ export const SECURITY_DOCS_URL = docLinks.workplaceSearchSecurity; export const SERVICENOW_DOCS_URL = docLinks.workplaceSearchServiceNow; export const SHAREPOINT_DOCS_URL = docLinks.workplaceSearchSharePoint; export const SLACK_DOCS_URL = docLinks.workplaceSearchSlack; -export const STANDARD_AUTH_DOCS_URL = docLinks.workplaceSearchStandardAuth; export const SYNCHRONIZATION_DOCS_URL = docLinks.workplaceSearchSynch; export const ZENDESK_DOCS_URL = docLinks.workplaceSearchZendesk; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/setup_guide/setup_guide.tsx index e52a174850c4c..009dbffafebd8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/setup_guide/setup_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/setup_guide/setup_guide.tsx @@ -15,11 +15,7 @@ import { WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants'; import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { SetupGuideLayout, SETUP_GUIDE_TITLE } from '../../../shared/setup_guide'; import { SendWorkplaceSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; -import { - GETTING_STARTED_DOCS_URL, - NATIVE_AUTH_DOCS_URL, - STANDARD_AUTH_DOCS_URL, -} from '../../routes'; +import { GETTING_STARTED_DOCS_URL } from '../../routes'; import GettingStarted from './assets/getting_started.png'; @@ -30,8 +26,6 @@ export const SetupGuide: React.FC = () => { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 409caee7d4b4b..76d3f07facf05 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9354,19 +9354,14 @@ "xpack.enterpriseSearch.setupGuide.step1.instruction1": "{configFile} ファイルで、{configSetting} を {productName} インスタンスの URL に設定します。例:", "xpack.enterpriseSearch.setupGuide.step1.title": "{productName}ホストURLをKibana構成に追加", "xpack.enterpriseSearch.setupGuide.step2.instruction1": "Kibanaを再起動して、前のステップから構成変更を取得します。", - "xpack.enterpriseSearch.setupGuide.step2.instruction2": "{productName}で{elasticsearchNativeAuthLink}を使用している場合は、すべて設定済みです。ユーザーは、現在の{productName}アクセスおよび権限を使用して、Kibanaで{productName}にアクセスできます。", "xpack.enterpriseSearch.setupGuide.step2.title": "Kibanaインスタンスの再読み込み", "xpack.enterpriseSearch.setupGuide.step3.title": "トラブルシューティングのヒント", "xpack.enterpriseSearch.setupGuide.title": "セットアップガイド", "xpack.enterpriseSearch.shared.flashMessages.defaultErrorMessage": "予期しないエラーが発生しました", "xpack.enterpriseSearch.shared.unsavedChangesMessage": "変更は保存されていません。終了してよろしいですか?", "xpack.enterpriseSearch.trialCalloutLink": "Elastic Stackライセンスの詳細を参照してください。", - "xpack.enterpriseSearch.troubleshooting.differentAuth.description": "このプラグインは現在、異なる認証方法で運用されている{productName}およびKibanaをサポートしています。たとえば、Kibana以外のSAMLプロバイダーを使用している{productName}はサポートされません。", - "xpack.enterpriseSearch.troubleshooting.differentAuth.title": "{productName}とKibanaは別の認証方法を使用しています", "xpack.enterpriseSearch.troubleshooting.differentEsClusters.description": "このプラグインは現在、異なるクラスターで実行されている{productName}とKibanaをサポートしていません。", "xpack.enterpriseSearch.troubleshooting.differentEsClusters.title": "{productName}とKibanaは別のElasticsearchクラスターにあります", - "xpack.enterpriseSearch.troubleshooting.standardAuth.description": "このプラグインは、{standardAuthLink}の{productName}を完全にはサポートしていません。{productName}で作成されたユーザーはKibanaアクセス権が必要です。Kibanaで作成されたユーザーは、ナビゲーションメニューに{productName}が表示されません。", - "xpack.enterpriseSearch.troubleshooting.standardAuth.title": "標準認証の{productName}はサポートされていません", "xpack.enterpriseSearch.units.allDaysLabel": "すべての日", "xpack.enterpriseSearch.units.daysLabel": "日", "xpack.enterpriseSearch.units.daysOfWeekLabel.friday": "金曜日", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index cf24a857c9723..01997e32f243e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9440,7 +9440,6 @@ "xpack.enterpriseSearch.setupGuide.step1.instruction1": "在 {configFile} 文件中,将 {configSetting} 设置为 {productName} 实例的 URL。例如:", "xpack.enterpriseSearch.setupGuide.step1.title": "将 {productName} 主机 URL 添加到 Kibana 配置", "xpack.enterpriseSearch.setupGuide.step2.instruction1": "重新启动 Kibana 以应用上一步骤中的配置更改。", - "xpack.enterpriseSearch.setupGuide.step2.instruction2": "如果正在 {productName} 中使用 {elasticsearchNativeAuthLink},则全部就绪。您的用户现在可以使用自己当前的 {productName} 访问权限在 Kibana 中访问 {productName}。", "xpack.enterpriseSearch.setupGuide.step2.title": "重新加载 Kibana 实例", "xpack.enterpriseSearch.setupGuide.step3.title": "解决问题", "xpack.enterpriseSearch.setupGuide.title": "设置指南", @@ -9448,12 +9447,8 @@ "xpack.enterpriseSearch.shared.unsavedChangesMessage": "您的更改尚未更改。是否确定要离开?", "xpack.enterpriseSearch.trialCalloutLink": "详细了解 Elastic Stack 许可证。", "xpack.enterpriseSearch.trialCalloutTitle": "您的可启用白金级功能的 Elastic Stack 试用版许可证将 {days, plural, other {# 天}}后过期。", - "xpack.enterpriseSearch.troubleshooting.differentAuth.description": "此插件当前不支持使用不同身份验证方法的 {productName} 和 Kibana,例如 {productName} 使用与 Kibana 不同的 SAML 提供程序。", - "xpack.enterpriseSearch.troubleshooting.differentAuth.title": "{productName} 和 Kibana 使用不同的身份验证方法", "xpack.enterpriseSearch.troubleshooting.differentEsClusters.description": "此插件当前不支持在不同集群中运行的 {productName} 和 Kibana。", "xpack.enterpriseSearch.troubleshooting.differentEsClusters.title": "{productName} 和 Kibana 在不同的 Elasticsearch 集群中", - "xpack.enterpriseSearch.troubleshooting.standardAuth.description": "此插件不完全支持使用 {standardAuthLink} 的 {productName}。{productName} 中创建的用户必须具有 Kibana 访问权限。Kibana 中创建的用户在导航菜单中将看不到 {productName}。", - "xpack.enterpriseSearch.troubleshooting.standardAuth.title": "不支持使用标准身份验证的 {productName}", "xpack.enterpriseSearch.units.allDaysLabel": "所有日期", "xpack.enterpriseSearch.units.daysLabel": "天", "xpack.enterpriseSearch.units.daysOfWeekLabel.friday": "星期五", From 3360b6a53c836d138d588ef4f96012a6cb9b7171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:55:50 -0500 Subject: [PATCH 11/65] [APM] Service maps: Add sparklines to the detail popover (#120021) * adding error rate and latency timeseries * adding sparklines * fixing ui * fixing spaces * adjusting error color * fixing api tests * deleting unnecessary test * changing loading spinner * addressing pr comments * fixing ci --- x-pack/plugins/apm/common/service_map.ts | 28 +- .../service_map/Popover/backend_contents.tsx | 3 +- .../app/service_map/Popover/index.tsx | 2 +- .../service_map/Popover/service_contents.tsx | 8 +- .../app/service_map/Popover/stats_list.tsx | 196 ++++---- .../app/service_map/cytoscape_options.ts | 2 +- .../server/lib/helpers/transactions/index.ts | 2 +- .../lib/transaction_groups/get_error_rate.ts | 3 + .../get_transaction_group_stats.ts | 10 +- .../chart_preview/get_transaction_duration.ts | 4 +- ...egister_transaction_duration_alert_type.ts | 4 +- .../get_service_map_backend_node_info.ts | 91 +++- .../get_service_map_service_node_info.test.ts | 96 ---- .../get_service_map_service_node_info.ts | 161 +++++-- ...ervice_instances_transaction_statistics.ts | 6 +- ...e_transaction_group_detailed_statistics.ts | 6 +- .../get_service_transaction_groups.ts | 6 +- .../get_service_transaction_stats.ts | 6 +- ...service_transaction_detailed_statistics.ts | 6 +- .../transactions/get_latency_charts/index.ts | 4 +- .../tests/service_maps/service_maps.spec.ts | 439 ++++++++++-------- 21 files changed, 615 insertions(+), 468 deletions(-) delete mode 100644 x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.test.ts diff --git a/x-pack/plugins/apm/common/service_map.ts b/x-pack/plugins/apm/common/service_map.ts index b8e6922414ebf..d10785d614cd3 100644 --- a/x-pack/plugins/apm/common/service_map.ts +++ b/x-pack/plugins/apm/common/service_map.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import cytoscape from 'cytoscape'; +import { Coordinate } from '../typings/timeseries'; import { ServiceAnomalyStats } from './anomaly_detection'; // These should be imported, but until TypeScript 4.2 we're inlining them here. @@ -59,13 +60,28 @@ export interface Connection { } export interface NodeStats { - avgMemoryUsage?: number | null; - avgCpuUsage?: number | null; - transactionStats: { - avgTransactionDuration: number | null; - avgRequestsPerMinute: number | null; + transactionStats?: { + latency?: { + value: number | null; + timeseries?: Coordinate[]; + }; + throughput?: { + value: number | null; + timeseries?: Coordinate[]; + }; + }; + failedTransactionsRate?: { + value: number | null; + timeseries?: Coordinate[]; + }; + cpuUsage?: { + value?: number | null; + timeseries?: Coordinate[]; + }; + memoryUsage?: { + value?: number | null; + timeseries?: Coordinate[]; }; - avgErrorRate: number | null; } export const invalidLicenseMessage = i18n.translate( diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx index 6b9954465f39d..b61b225c2fe94 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiButton, EuiFlexItem } from '@elastic/eui'; +import { EuiButton, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { TypeOf } from '@kbn/typed-react-router-config'; import { METRIC_TYPE } from '@kbn/analytics'; @@ -73,6 +73,7 @@ export function BackendContents({ + {/* eslint-disable-next-line @elastic/eui/href-or-on-click*/} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx index 5c41a1b4db7e6..10d558e648376 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx @@ -7,7 +7,12 @@ /* eslint-disable @elastic/eui/href-or-on-click */ -import { EuiButton, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; +import { + EuiButton, + EuiFlexItem, + EuiHorizontalRule, + EuiSpacer, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useApmParams } from '../../../../hooks/use_apm_params'; @@ -89,6 +94,7 @@ export function ServiceContents({ )} + {i18n.translate('xpack.apm.serviceMap.serviceDetailsButtonText', { diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/stats_list.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/stats_list.tsx index b46b7a0986179..002c480503454 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/stats_list.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/stats_list.tsx @@ -5,30 +5,23 @@ * 2.0. */ -import { EuiFlexGroup, EuiLoadingSpinner, EuiText } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiLoadingChart, + EuiText, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isNumber } from 'lodash'; -import React from 'react'; -import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import React, { useMemo } from 'react'; import { NodeStats } from '../../../../../common/service_map'; import { asDuration, asPercent, asTransactionRate, } from '../../../../../common/utils/formatters'; - -export const ItemRow = euiStyled.tr` - line-height: 2; -`; - -export const ItemTitle = euiStyled.td` - color: ${({ theme }) => theme.eui.euiTextSubduedColor}; - padding-right: 1rem; -`; - -export const ItemDescription = euiStyled.td` - text-align: right; -`; +import { Coordinate } from '../../../../../typings/timeseries'; +import { SparkPlot, Color } from '../../../shared/charts/spark_plot'; function LoadingSpinner() { return ( @@ -37,7 +30,7 @@ function LoadingSpinner() { justifyContent="spaceAround" style={{ height: 170 }} > - +
); } @@ -57,22 +50,82 @@ interface StatsListProps { data: NodeStats; } +interface Item { + title: string; + valueLabel: string | null; + timeseries?: Coordinate[]; + color: Color; +} + export function StatsList({ data, isLoading }: StatsListProps) { - const { - avgCpuUsage, - avgErrorRate, - avgMemoryUsage, - transactionStats: { avgRequestsPerMinute, avgTransactionDuration }, - } = data; + const { cpuUsage, failedTransactionsRate, memoryUsage, transactionStats } = + data; const hasData = [ - avgCpuUsage, - avgErrorRate, - avgMemoryUsage, - avgRequestsPerMinute, - avgTransactionDuration, + cpuUsage?.value, + failedTransactionsRate?.value, + memoryUsage?.value, + transactionStats?.throughput?.value, + transactionStats?.latency?.value, ].some((stat) => isNumber(stat)); + const items: Item[] = useMemo( + () => [ + { + title: i18n.translate( + 'xpack.apm.serviceMap.avgTransDurationPopoverStat', + { + defaultMessage: 'Latency (avg.)', + } + ), + valueLabel: isNumber(transactionStats?.latency?.value) + ? asDuration(transactionStats?.latency?.value) + : null, + timeseries: transactionStats?.latency?.timeseries, + color: 'euiColorVis1', + }, + { + title: i18n.translate( + 'xpack.apm.serviceMap.avgReqPerMinutePopoverMetric', + { + defaultMessage: 'Throughput (avg.)', + } + ), + valueLabel: asTransactionRate(transactionStats?.throughput?.value), + timeseries: transactionStats?.throughput?.timeseries, + color: 'euiColorVis0', + }, + { + title: i18n.translate('xpack.apm.serviceMap.errorRatePopoverStat', { + defaultMessage: 'Failed transaction rate (avg.)', + }), + valueLabel: asPercent(failedTransactionsRate?.value, 1, ''), + timeseries: failedTransactionsRate?.timeseries, + color: 'euiColorVis7', + }, + { + title: i18n.translate('xpack.apm.serviceMap.avgCpuUsagePopoverStat', { + defaultMessage: 'CPU usage (avg.)', + }), + valueLabel: asPercent(cpuUsage?.value, 1, ''), + timeseries: cpuUsage?.timeseries, + color: 'euiColorVis3', + }, + { + title: i18n.translate( + 'xpack.apm.serviceMap.avgMemoryUsagePopoverStat', + { + defaultMessage: 'Memory usage (avg.)', + } + ), + valueLabel: asPercent(memoryUsage?.value, 1, ''), + timeseries: memoryUsage?.timeseries, + color: 'euiColorVis8', + }, + ], + [cpuUsage, failedTransactionsRate, memoryUsage, transactionStats] + ); + if (isLoading) { return ; } @@ -81,59 +134,40 @@ export function StatsList({ data, isLoading }: StatsListProps) { return ; } - const items = [ - { - title: i18n.translate( - 'xpack.apm.serviceMap.avgTransDurationPopoverStat', - { - defaultMessage: 'Latency (avg.)', - } - ), - description: isNumber(avgTransactionDuration) - ? asDuration(avgTransactionDuration) - : null, - }, - { - title: i18n.translate( - 'xpack.apm.serviceMap.avgReqPerMinutePopoverMetric', - { - defaultMessage: 'Throughput (avg.)', - } - ), - description: asTransactionRate(avgRequestsPerMinute), - }, - { - title: i18n.translate('xpack.apm.serviceMap.errorRatePopoverStat', { - defaultMessage: 'Failed transaction rate (avg.)', - }), - description: asPercent(avgErrorRate, 1, ''), - }, - { - title: i18n.translate('xpack.apm.serviceMap.avgCpuUsagePopoverStat', { - defaultMessage: 'CPU usage (avg.)', - }), - description: asPercent(avgCpuUsage, 1, ''), - }, - { - title: i18n.translate('xpack.apm.serviceMap.avgMemoryUsagePopoverStat', { - defaultMessage: 'Memory usage (avg.)', - }), - description: asPercent(avgMemoryUsage, 1, ''), - }, - ]; - return ( - - - {items.map(({ title, description }) => { - return description ? ( - - {title} - {description} - - ) : null; - })} - -
+ + {items.map(({ title, valueLabel, timeseries, color }) => { + if (!valueLabel) { + return null; + } + return ( + + + + + {title} + + + + {timeseries ? ( + + ) : ( +
{valueLabel}
+ )} +
+
+
+ ); + })} +
); } diff --git a/x-pack/plugins/apm/public/components/app/service_map/cytoscape_options.ts b/x-pack/plugins/apm/public/components/app/service_map/cytoscape_options.ts index 1e2386c8743fe..e137f2dbb0f78 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/cytoscape_options.ts +++ b/x-pack/plugins/apm/public/components/app/service_map/cytoscape_options.ts @@ -20,7 +20,7 @@ import { import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { iconForNode } from './icons'; -export const popoverWidth = 280; +export const popoverWidth = 350; function getServiceAnomalyStats(el: cytoscape.NodeSingular) { const serviceAnomalyStats: ServiceAnomalyStats | undefined = el.data( diff --git a/x-pack/plugins/apm/server/lib/helpers/transactions/index.ts b/x-pack/plugins/apm/server/lib/helpers/transactions/index.ts index edaae8cadc1d5..577a7544d93ea 100644 --- a/x-pack/plugins/apm/server/lib/helpers/transactions/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/transactions/index.ts @@ -82,7 +82,7 @@ export async function getSearchAggregatedTransactions({ } } -export function getTransactionDurationFieldForTransactions( +export function getDurationFieldForTransactions( searchAggregatedTransactions: boolean ) { return searchAggregatedTransactions diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts index 328d2da0f6df0..e1dde61bfc3ff 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts @@ -42,6 +42,7 @@ export async function getErrorRate({ searchAggregatedTransactions, start, end, + numBuckets, }: { environment: string; kuery: string; @@ -52,6 +53,7 @@ export async function getErrorRate({ searchAggregatedTransactions: boolean; start: number; end: number; + numBuckets?: number; }): Promise<{ timeseries: Coordinate[]; average: number | null; @@ -91,6 +93,7 @@ export async function getErrorRate({ start, end, searchAggregatedTransactions, + numBuckets, }).intervalString, min_doc_count: 0, extended_bounds: { min: start, max: end }, diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts index fd638a6731c63..04cee211c78db 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts @@ -13,7 +13,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { arrayUnionToCallable } from '../../../common/utils/array_union_to_callable'; import { TransactionGroupRequestBase, TransactionGroupSetup } from './fetcher'; -import { getTransactionDurationFieldForTransactions } from '../helpers/transactions'; +import { getDurationFieldForTransactions } from '../helpers/transactions'; import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; interface MetricParams { request: TransactionGroupRequestBase; @@ -49,9 +49,7 @@ export async function getAverages({ const params = mergeRequestWithAggs(request, { avg: { avg: { - field: getTransactionDurationFieldForTransactions( - searchAggregatedTransactions - ), + field: getDurationFieldForTransactions(searchAggregatedTransactions), }, }, }); @@ -119,9 +117,7 @@ export async function getSums({ const params = mergeRequestWithAggs(request, { sum: { sum: { - field: getTransactionDurationFieldForTransactions( - searchAggregatedTransactions - ), + field: getDurationFieldForTransactions(searchAggregatedTransactions), }, }, }); diff --git a/x-pack/plugins/apm/server/routes/alerts/chart_preview/get_transaction_duration.ts b/x-pack/plugins/apm/server/routes/alerts/chart_preview/get_transaction_duration.ts index 0338f78a0a892..6b5fb0c5fb1b4 100644 --- a/x-pack/plugins/apm/server/routes/alerts/chart_preview/get_transaction_duration.ts +++ b/x-pack/plugins/apm/server/routes/alerts/chart_preview/get_transaction_duration.ts @@ -16,7 +16,7 @@ import { AlertParams } from '../route'; import { getSearchAggregatedTransactions, getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../../lib/helpers/transactions'; import { Setup } from '../../../lib/helpers/setup_request'; @@ -55,7 +55,7 @@ export async function getTransactionDurationChartPreview({ }, }; - const transactionDurationField = getTransactionDurationFieldForTransactions( + const transactionDurationField = getDurationFieldForTransactions( searchAggregatedTransactions ); diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts index 1e39b02655ada..7ad78cdff9545 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts @@ -36,7 +36,7 @@ import { environmentQuery } from '../../../common/utils/environment_query'; import { getDurationFormatter } from '../../../common/utils/formatters'; import { getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, } from '../../lib/helpers/transactions'; import { getApmIndices } from '../../routes/settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; @@ -110,7 +110,7 @@ export function registerTransactionDurationAlertType({ ? indices.metric : indices.transaction; - const field = getTransactionDurationFieldForTransactions( + const field = getDurationFieldForTransactions( searchAggregatedTransactions ); diff --git a/x-pack/plugins/apm/server/routes/service_map/get_service_map_backend_node_info.ts b/x-pack/plugins/apm/server/routes/service_map/get_service_map_backend_node_info.ts index 3866bac88ef44..6922fc04f2e71 100644 --- a/x-pack/plugins/apm/server/routes/service_map/get_service_map_backend_node_info.ts +++ b/x-pack/plugins/apm/server/routes/service_map/get_service_map_backend_node_info.ts @@ -16,8 +16,11 @@ import { EventOutcome } from '../../../common/event_outcome'; import { ProcessorEvent } from '../../../common/processor_event'; import { environmentQuery } from '../../../common/utils/environment_query'; import { withApmSpan } from '../../utils/with_apm_span'; -import { calculateThroughput } from '../../lib/helpers/calculate_throughput'; +import { calculateThroughputWithRange } from '../../lib/helpers/calculate_throughput'; import { Setup } from '../../lib/helpers/setup_request'; +import { getBucketSize } from '../../lib/helpers/get_bucket_size'; +import { getFailedTransactionRateTimeSeries } from '../../lib/helpers/transaction_error_rate'; +import { NodeStats } from '../../../common/service_map'; interface Options { setup: Setup; @@ -33,10 +36,24 @@ export function getServiceMapBackendNodeInfo({ setup, start, end, -}: Options) { +}: Options): Promise { return withApmSpan('get_service_map_backend_node_stats', async () => { const { apmEventClient } = setup; + const { intervalString } = getBucketSize({ start, end, numBuckets: 20 }); + + const subAggs = { + latency_sum: { + sum: { field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM }, + }, + count: { + sum: { field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT }, + }, + outcomes: { + terms: { field: EVENT_OUTCOME, include: [EventOutcome.failure] }, + }, + }; + const response = await apmEventClient.search( 'get_service_map_backend_node_stats', { @@ -55,18 +72,15 @@ export function getServiceMapBackendNodeInfo({ }, }, aggs: { - latency_sum: { - sum: { - field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM, + ...subAggs, + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: intervalString, + min_doc_count: 0, + extended_bounds: { min: start, max: end }, }, - }, - count: { - sum: { - field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT, - }, - }, - [EVENT_OUTCOME]: { - terms: { field: EVENT_OUTCOME, include: [EventOutcome.failure] }, + aggs: subAggs, }, }, }, @@ -74,13 +88,13 @@ export function getServiceMapBackendNodeInfo({ ); const count = response.aggregations?.count.value ?? 0; - const errorCount = - response.aggregations?.[EVENT_OUTCOME].buckets[0]?.doc_count ?? 0; + const failedTransactionsRateCount = + response.aggregations?.outcomes.buckets[0]?.doc_count ?? 0; const latencySum = response.aggregations?.latency_sum.value ?? 0; - const avgErrorRate = errorCount / count; - const avgTransactionDuration = latencySum / count; - const avgRequestsPerMinute = calculateThroughput({ + const avgFailedTransactionsRate = failedTransactionsRateCount / count; + const latency = latencySum / count; + const throughput = calculateThroughputWithRange({ start, end, value: count, @@ -88,19 +102,48 @@ export function getServiceMapBackendNodeInfo({ if (count === 0) { return { - avgErrorRate: null, + failedTransactionsRate: undefined, transactionStats: { - avgRequestsPerMinute: null, - avgTransactionDuration: null, + throughput: undefined, + latency: undefined, }, }; } return { - avgErrorRate, + failedTransactionsRate: { + value: avgFailedTransactionsRate, + timeseries: response.aggregations?.timeseries + ? getFailedTransactionRateTimeSeries( + response.aggregations.timeseries.buckets + ) + : undefined, + }, transactionStats: { - avgRequestsPerMinute, - avgTransactionDuration, + throughput: { + value: throughput, + timeseries: response.aggregations?.timeseries.buckets.map( + (bucket) => { + return { + x: bucket.key, + y: calculateThroughputWithRange({ + start, + end, + value: bucket.doc_count ?? 0, + }), + }; + } + ), + }, + latency: { + value: latency, + timeseries: response.aggregations?.timeseries.buckets.map( + (bucket) => ({ + x: bucket.key, + y: bucket.latency_sum.value, + }) + ), + }, }, }; }); diff --git a/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.test.ts b/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.test.ts deleted file mode 100644 index 9f65481d07489..0000000000000 --- a/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.test.ts +++ /dev/null @@ -1,96 +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 { getServiceMapServiceNodeInfo } from './get_service_map_service_node_info'; -import { Setup } from '../../lib/helpers/setup_request'; -import * as getErrorRateModule from '../../lib/transaction_groups/get_error_rate'; -import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; - -describe('getServiceMapServiceNodeInfo', () => { - describe('with no results', () => { - it('returns null data', async () => { - const setup = { - apmEventClient: { - search: () => - Promise.resolve({ - hits: { total: { value: 0 } }, - }), - }, - indices: {}, - uiFilters: {}, - } as unknown as Setup; - const serviceName = 'test service name'; - const result = await getServiceMapServiceNodeInfo({ - environment: 'test environment', - setup, - serviceName, - searchAggregatedTransactions: false, - start: 1528113600000, - end: 1528977600000, - }); - - expect(result).toEqual({ - avgCpuUsage: null, - avgErrorRate: null, - avgMemoryUsage: null, - transactionStats: { - avgRequestsPerMinute: null, - avgTransactionDuration: null, - }, - }); - }); - }); - - describe('with some results', () => { - it('returns data', async () => { - jest.spyOn(getErrorRateModule, 'getErrorRate').mockResolvedValueOnce({ - average: 0.5, - timeseries: [{ x: 1634808240000, y: 0 }], - }); - - const setup = { - apmEventClient: { - search: () => - Promise.resolve({ - hits: { - total: { value: 1 }, - }, - aggregations: { - duration: { value: null }, - avgCpuUsage: { value: null }, - avgMemoryUsage: { value: null }, - }, - }), - }, - indices: {}, - start: 1593460053026000, - end: 1593497863217000, - config: { metricsInterval: 30 }, - uiFilters: { environment: 'test environment' }, - } as unknown as Setup; - const serviceName = 'test service name'; - const result = await getServiceMapServiceNodeInfo({ - setup, - serviceName, - searchAggregatedTransactions: false, - environment: ENVIRONMENT_ALL.value, - start: 1593460053026000, - end: 1593497863217000, - }); - - expect(result).toEqual({ - avgCpuUsage: null, - avgErrorRate: 0.5, - avgMemoryUsage: null, - transactionStats: { - avgRequestsPerMinute: 0.000001586873761097901, - avgTransactionDuration: null, - }, - }); - }); - }); -}); diff --git a/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts index d6eb7729effaf..ad2ab74098c22 100644 --- a/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts +++ b/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts @@ -6,6 +6,7 @@ */ import { ESFilter } from '../../../../../../src/core/types/elasticsearch'; +import { rangeQuery } from '../../../../observability/server'; import { METRIC_CGROUP_MEMORY_USAGE_BYTES, METRIC_SYSTEM_CPU_PERCENT, @@ -15,24 +16,25 @@ import { TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; +import { NodeStats } from '../../../common/service_map'; import { TRANSACTION_PAGE_LOAD, TRANSACTION_REQUEST, } from '../../../common/transaction_types'; -import { rangeQuery } from '../../../../observability/server'; import { environmentQuery } from '../../../common/utils/environment_query'; -import { withApmSpan } from '../../utils/with_apm_span'; +import { getBucketSizeForAggregatedTransactions } from '../../lib/helpers/get_bucket_size_for_aggregated_transactions'; +import { Setup } from '../../lib/helpers/setup_request'; import { getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../lib/helpers/transactions'; -import { Setup } from '../../lib/helpers/setup_request'; +import { getErrorRate } from '../../lib/transaction_groups/get_error_rate'; +import { withApmSpan } from '../../utils/with_apm_span'; import { percentCgroupMemoryUsedScript, percentSystemMemoryUsedScript, } from '../metrics/by_agent/shared/memory'; -import { getErrorRate } from '../../lib/transaction_groups/get_error_rate'; interface Options { setup: Setup; @@ -48,8 +50,13 @@ interface TaskParameters { filter: ESFilter[]; searchAggregatedTransactions: boolean; minutes: number; - serviceName?: string; + serviceName: string; setup: Setup; + start: number; + end: number; + intervalString: string; + bucketSize: number; + numBuckets: number; } export function getServiceMapServiceNodeInfo({ @@ -59,7 +66,7 @@ export function getServiceMapServiceNodeInfo({ searchAggregatedTransactions, start, end, -}: Options) { +}: Options): Promise { return withApmSpan('get_service_map_node_stats', async () => { const filter: ESFilter[] = [ { term: { [SERVICE_NAME]: serviceName } }, @@ -68,6 +75,14 @@ export function getServiceMapServiceNodeInfo({ ]; const minutes = Math.abs((end - start) / (1000 * 60)); + const numBuckets = 20; + const { intervalString, bucketSize } = + getBucketSizeForAggregatedTransactions({ + start, + end, + searchAggregatedTransactions, + numBuckets, + }); const taskParams = { environment, filter, @@ -77,34 +92,38 @@ export function getServiceMapServiceNodeInfo({ setup, start, end, + intervalString, + bucketSize, + numBuckets, }; - const [errorStats, transactionStats, cpuStats, memoryStats] = + const [failedTransactionsRate, transactionStats, cpuUsage, memoryUsage] = await Promise.all([ - getErrorStats(taskParams), + getFailedTransactionsRateStats(taskParams), getTransactionStats(taskParams), getCpuStats(taskParams), getMemoryStats(taskParams), ]); return { - ...errorStats, + failedTransactionsRate, transactionStats, - ...cpuStats, - ...memoryStats, + cpuUsage, + memoryUsage, }; }); } -async function getErrorStats({ +async function getFailedTransactionsRateStats({ setup, serviceName, environment, searchAggregatedTransactions, start, end, -}: Options) { + numBuckets, +}: TaskParameters): Promise { return withApmSpan('get_error_rate_for_service_map_node', async () => { - const { average } = await getErrorRate({ + const { average, timeseries } = await getErrorRate({ environment, setup, serviceName, @@ -112,8 +131,9 @@ async function getErrorStats({ start, end, kuery: '', + numBuckets, }); - return { avgErrorRate: average }; + return { value: average, timeseries }; }); } @@ -122,12 +142,16 @@ async function getTransactionStats({ filter, minutes, searchAggregatedTransactions, -}: TaskParameters): Promise<{ - avgTransactionDuration: number | null; - avgRequestsPerMinute: number | null; -}> { + start, + end, + intervalString, +}: TaskParameters): Promise { const { apmEventClient } = setup; + const durationField = getDurationFieldForTransactions( + searchAggregatedTransactions + ); + const params = { apm: { events: [getProcessorEventForTransactions(searchAggregatedTransactions)], @@ -154,11 +178,16 @@ async function getTransactionStats({ }, track_total_hits: true, aggs: { - duration: { - avg: { - field: getTransactionDurationFieldForTransactions( - searchAggregatedTransactions - ), + duration: { avg: { field: durationField } }, + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: intervalString, + min_doc_count: 0, + extended_bounds: { min: start, max: end }, + }, + aggs: { + latency: { avg: { field: durationField } }, }, }, }, @@ -172,15 +201,32 @@ async function getTransactionStats({ const totalRequests = response.hits.total.value; return { - avgTransactionDuration: response.aggregations?.duration.value ?? null, - avgRequestsPerMinute: totalRequests > 0 ? totalRequests / minutes : null, + latency: { + value: response.aggregations?.duration.value ?? null, + timeseries: response.aggregations?.timeseries.buckets.map((bucket) => ({ + x: bucket.key, + y: bucket.latency.value, + })), + }, + throughput: { + value: totalRequests > 0 ? totalRequests / minutes : null, + timeseries: response.aggregations?.timeseries.buckets.map((bucket) => { + return { + x: bucket.key, + y: bucket.doc_count ?? 0, + }; + }), + }, }; } async function getCpuStats({ setup, filter, -}: TaskParameters): Promise<{ avgCpuUsage: number | null }> { + intervalString, + start, + end, +}: TaskParameters): Promise { const { apmEventClient } = setup; const response = await apmEventClient.search( @@ -199,22 +245,44 @@ async function getCpuStats({ ], }, }, - aggs: { avgCpuUsage: { avg: { field: METRIC_SYSTEM_CPU_PERCENT } } }, + aggs: { + avgCpuUsage: { avg: { field: METRIC_SYSTEM_CPU_PERCENT } }, + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: intervalString, + min_doc_count: 0, + extended_bounds: { min: start, max: end }, + }, + aggs: { + cpuAvg: { avg: { field: METRIC_SYSTEM_CPU_PERCENT } }, + }, + }, + }, }, } ); - return { avgCpuUsage: response.aggregations?.avgCpuUsage.value ?? null }; + return { + value: response.aggregations?.avgCpuUsage.value ?? null, + timeseries: response.aggregations?.timeseries.buckets.map((bucket) => ({ + x: bucket.key, + y: bucket.cpuAvg.value, + })), + }; } function getMemoryStats({ setup, filter, -}: TaskParameters): Promise<{ avgMemoryUsage: number | null }> { + intervalString, + start, + end, +}: TaskParameters) { return withApmSpan('get_memory_stats_for_service_map_node', async () => { const { apmEventClient } = setup; - const getAvgMemoryUsage = async ({ + const getMemoryUsage = async ({ additionalFilters, script, }: { @@ -222,7 +290,7 @@ function getMemoryStats({ script: | typeof percentCgroupMemoryUsedScript | typeof percentSystemMemoryUsedScript; - }) => { + }): Promise => { const response = await apmEventClient.search( 'get_avg_memory_for_service_map_node', { @@ -238,22 +306,39 @@ function getMemoryStats({ }, aggs: { avgMemoryUsage: { avg: { script } }, + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: intervalString, + min_doc_count: 0, + extended_bounds: { min: start, max: end }, + }, + aggs: { + memoryAvg: { avg: { script } }, + }, + }, }, }, } ); - return response.aggregations?.avgMemoryUsage.value ?? null; + return { + value: response.aggregations?.avgMemoryUsage.value ?? null, + timeseries: response.aggregations?.timeseries.buckets.map((bucket) => ({ + x: bucket.key, + y: bucket.memoryAvg.value, + })), + }; }; - let avgMemoryUsage = await getAvgMemoryUsage({ + let memoryUsage = await getMemoryUsage({ additionalFilters: [ { exists: { field: METRIC_CGROUP_MEMORY_USAGE_BYTES } }, ], script: percentCgroupMemoryUsedScript, }); - if (!avgMemoryUsage) { - avgMemoryUsage = await getAvgMemoryUsage({ + if (!memoryUsage) { + memoryUsage = await getMemoryUsage({ additionalFilters: [ { exists: { field: METRIC_SYSTEM_FREE_MEMORY } }, { exists: { field: METRIC_SYSTEM_TOTAL_MEMORY } }, @@ -262,6 +347,6 @@ function getMemoryStats({ }); } - return { avgMemoryUsage }; + return memoryUsage; }); } diff --git a/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts b/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts index 166e8d61142ea..ec081916f455d 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts @@ -18,7 +18,7 @@ import { kqlQuery, rangeQuery } from '../../../../../observability/server'; import { environmentQuery } from '../../../../common/utils/environment_query'; import { getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../../lib/helpers/transactions'; import { calculateThroughput } from '../../../lib/helpers/calculate_throughput'; @@ -89,9 +89,7 @@ export async function getServiceInstancesTransactionStatistics< } ); - const field = getTransactionDurationFieldForTransactions( - searchAggregatedTransactions - ); + const field = getDurationFieldForTransactions(searchAggregatedTransactions); const subAggs = { ...getLatencyAggregation(latencyAggregationType, field), diff --git a/x-pack/plugins/apm/server/routes/services/get_service_transaction_group_detailed_statistics.ts b/x-pack/plugins/apm/server/routes/services/get_service_transaction_group_detailed_statistics.ts index 70f77c26fdbf9..b14329985db90 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_transaction_group_detailed_statistics.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_transaction_group_detailed_statistics.ts @@ -20,7 +20,7 @@ import { environmentQuery } from '../../../common/utils/environment_query'; import { Coordinate } from '../../../typings/timeseries'; import { getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../lib/helpers/transactions'; import { getBucketSizeForAggregatedTransactions } from '../../lib/helpers/get_bucket_size_for_aggregated_transactions'; @@ -72,9 +72,7 @@ export async function getServiceTransactionGroupDetailedStatistics({ searchAggregatedTransactions, }); - const field = getTransactionDurationFieldForTransactions( - searchAggregatedTransactions - ); + const field = getDurationFieldForTransactions(searchAggregatedTransactions); const response = await apmEventClient.search( 'get_service_transaction_group_detailed_statistics', diff --git a/x-pack/plugins/apm/server/routes/services/get_service_transaction_groups.ts b/x-pack/plugins/apm/server/routes/services/get_service_transaction_groups.ts index 711d6964221bd..979d79c84578a 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_transaction_groups.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_transaction_groups.ts @@ -17,7 +17,7 @@ import { rangeQuery, kqlQuery } from '../../../../observability/server'; import { environmentQuery } from '../../../common/utils/environment_query'; import { getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../lib/helpers/transactions'; import { calculateThroughput } from '../../lib/helpers/calculate_throughput'; @@ -59,9 +59,7 @@ export async function getServiceTransactionGroups({ const { apmEventClient, config } = setup; const bucketSize = config.ui.transactionGroupBucketSize; - const field = getTransactionDurationFieldForTransactions( - searchAggregatedTransactions - ); + const field = getDurationFieldForTransactions(searchAggregatedTransactions); const response = await apmEventClient.search( 'get_service_transaction_groups', 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 3eaa8053b6709..9576c018c1c27 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 @@ -20,7 +20,7 @@ import { environmentQuery } from '../../../../common/utils/environment_query'; import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; import { getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../../lib/helpers/transactions'; import { calculateThroughput } from '../../../lib/helpers/calculate_throughput'; @@ -56,9 +56,7 @@ export async function getServiceTransactionStats({ const metrics = { avg_duration: { avg: { - field: getTransactionDurationFieldForTransactions( - searchAggregatedTransactions - ), + field: getDurationFieldForTransactions(searchAggregatedTransactions), }, }, outcomes, 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 95f2c6f400de9..744cd70e9eeb2 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 @@ -19,7 +19,7 @@ import { environmentQuery } from '../../../../common/utils/environment_query'; import { getOffsetInMs } from '../../../../common/utils/get_offset_in_ms'; import { getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../../lib/helpers/transactions'; import { calculateThroughput } from '../../../lib/helpers/calculate_throughput'; @@ -61,9 +61,7 @@ export async function getServiceTransactionDetailedStatistics({ const metrics = { avg_duration: { avg: { - field: getTransactionDurationFieldForTransactions( - searchAggregatedTransactions - ), + field: getDurationFieldForTransactions(searchAggregatedTransactions), }, }, outcomes, diff --git a/x-pack/plugins/apm/server/routes/transactions/get_latency_charts/index.ts b/x-pack/plugins/apm/server/routes/transactions/get_latency_charts/index.ts index 5375da3606f18..5c9e4892866cb 100644 --- a/x-pack/plugins/apm/server/routes/transactions/get_latency_charts/index.ts +++ b/x-pack/plugins/apm/server/routes/transactions/get_latency_charts/index.ts @@ -21,7 +21,7 @@ import { import { environmentQuery } from '../../../../common/utils/environment_query'; import { getDocumentTypeFilterForTransactions, - getTransactionDurationFieldForTransactions, + getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../../lib/helpers/transactions'; import { Setup } from '../../../lib/helpers/setup_request'; @@ -64,7 +64,7 @@ function searchLatency({ searchAggregatedTransactions, }); - const transactionDurationField = getTransactionDurationFieldForTransactions( + const transactionDurationField = getDurationFieldForTransactions( searchAggregatedTransactions ); diff --git a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.spec.ts b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.spec.ts index bf607030c07d3..d4f0e350071bf 100644 --- a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.spec.ts @@ -5,46 +5,63 @@ * 2.0. */ -import querystring from 'querystring'; -import url from 'url'; import expect from '@kbn/expect'; import { isEmpty, orderBy, uniq } from 'lodash'; +import { ServiceConnectionNode } from '../../../../plugins/apm/common/service_map'; +import { ApmApiError, SupertestReturnType } from '../../common/apm_api_supertest'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; -import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; +type BackendResponse = SupertestReturnType<'GET /internal/apm/service-map/backend'>; +type ServiceNodeResponse = + SupertestReturnType<'GET /internal/apm/service-map/service/{serviceName}'>; +type ServiceMapResponse = SupertestReturnType<'GET /internal/apm/service-map'>; + export default function serviceMapsApiTests({ getService }: FtrProviderContext) { + const apmApiClient = getService('apmApiClient'); const registry = getService('registry'); - const supertest = getService('legacySupertestAsApmReadUser'); - const supertestAsApmReadUserWithoutMlAccess = getService( - 'legacySupertestAsApmReadUserWithoutMlAccess' - ); const archiveName = 'apm_8.0.0'; const metadata = archives_metadata[archiveName]; - const start = encodeURIComponent(metadata.start); - const end = encodeURIComponent(metadata.end); registry.when('Service map with a basic license', { config: 'basic', archives: [] }, () => { it('is only be available to users with Platinum license (or higher)', async () => { - const response = await supertest.get( - `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` - ); - - expect(response.status).to.be(403); + try { + await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map`, + params: { + query: { + start: metadata.start, + end: metadata.end, + environment: 'ENVIRONMENT_ALL', + }, + }, + }); - expectSnapshot(response.body.message).toMatchInline( - `"In order to access Service Maps, you must be subscribed to an Elastic Platinum license. With it, you'll have the ability to visualize your entire application stack along with your APM data."` - ); + expect(true).to.be(false); + } catch (e) { + const err = e as ApmApiError; + expect(err.res.status).to.be(403); + expectSnapshot(err.res.body.message).toMatchInline( + `"In order to access Service Maps, you must be subscribed to an Elastic Platinum license. With it, you'll have the ability to visualize your entire application stack along with your APM data."` + ); + } }); }); registry.when('Service map without data', { config: 'trial', archives: [] }, () => { describe('/internal/apm/service-map', () => { it('returns an empty list', async () => { - const response = await supertest.get( - `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` - ); + const response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map`, + params: { + query: { + start: metadata.start, + end: metadata.end, + environment: 'ENVIRONMENT_ALL', + }, + }, + }); expect(response.status).to.be(200); expect(response.body.elements.length).to.be(0); @@ -52,63 +69,78 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); describe('/internal/apm/service-map/service/{serviceName}', () => { - it('returns an object with nulls', async () => { - const q = querystring.stringify({ - start: metadata.start, - end: metadata.end, - environment: 'ENVIRONMENT_ALL', + let response: ServiceNodeResponse; + before(async () => { + response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map/service/{serviceName}`, + params: { + path: { serviceName: 'opbeans-node' }, + query: { + start: metadata.start, + end: metadata.end, + environment: 'ENVIRONMENT_ALL', + }, + }, }); - const response = await supertest.get(`/internal/apm/service-map/service/opbeans-node?${q}`); + }); + it('retuns status code 200', () => { expect(response.status).to.be(200); + }); - expectSnapshot(response.body).toMatchInline(` - Object { - "avgCpuUsage": null, - "avgErrorRate": null, - "avgMemoryUsage": null, - "transactionStats": Object { - "avgRequestsPerMinute": null, - "avgTransactionDuration": null, - }, - } - `); + it('returns an object with nulls', async () => { + [ + response.body.failedTransactionsRate?.value, + response.body.memoryUsage?.value, + response.body.cpuUsage?.value, + response.body.transactionStats?.latency?.value, + response.body.transactionStats?.throughput?.value, + ].forEach((value) => { + expect(value).to.be.eql(null); + }); }); }); describe('/internal/apm/service-map/backend', () => { - it('returns an object with nulls', async () => { - const q = querystring.stringify({ - backendName: 'postgres', - start: metadata.start, - end: metadata.end, - environment: 'ENVIRONMENT_ALL', + let response: BackendResponse; + before(async () => { + response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map/backend`, + params: { + query: { + backendName: 'postgres', + start: metadata.start, + end: metadata.end, + environment: 'ENVIRONMENT_ALL', + }, + }, }); - const response = await supertest.get(`/internal/apm/service-map/backend?${q}`); + }); + it('retuns status code 200', () => { expect(response.status).to.be(200); + }); - expectSnapshot(response.body).toMatchInline(` - Object { - "avgErrorRate": null, - "transactionStats": Object { - "avgRequestsPerMinute": null, - "avgTransactionDuration": null, - }, - } - `); + it('returns undefined values', () => { + expect(response.body).to.eql({ transactionStats: {} }); }); }); }); registry.when('Service Map with data', { config: 'trial', archives: ['apm_8.0.0'] }, () => { describe('/internal/apm/service-map', () => { - let response: PromiseReturnType; - + let response: ServiceMapResponse; before(async () => { - response = await supertest.get( - `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` - ); + response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map`, + params: { + query: { + environment: 'ENVIRONMENT_ALL', + start: metadata.start, + end: metadata.end, + }, + }, + }); }); it('returns service map elements', () => { @@ -126,17 +158,17 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) ).sort(); expectSnapshot(serviceNames).toMatchInline(` - Array [ - "auditbeat", - "opbeans-dotnet", - "opbeans-go", - "opbeans-java", - "opbeans-node", - "opbeans-python", - "opbeans-ruby", - "opbeans-rum", - ] - `); + Array [ + "auditbeat", + "opbeans-dotnet", + "opbeans-go", + "opbeans-java", + "opbeans-node", + "opbeans-python", + "opbeans-ruby", + "opbeans-rum", + ] + `); const externalDestinations = uniq( elements @@ -145,115 +177,119 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) ).sort(); expectSnapshot(externalDestinations).toMatchInline(` - Array [ - ">elasticsearch", - ">postgresql", - ">redis", - ">sqlite", - ] - `); + Array [ + ">elasticsearch", + ">postgresql", + ">redis", + ">sqlite", + ] + `); }); describe('with ML data', () => { describe('with the default apm user', () => { before(async () => { - response = await supertest.get( - `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` - ); + response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map`, + params: { + query: { + environment: 'ENVIRONMENT_ALL', + start: metadata.start, + end: metadata.end, + }, + }, + }); }); - it('returns service map elements with anomaly stats', () => { expect(response.status).to.be(200); const dataWithAnomalies = response.body.elements.filter( - (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) + (el) => !isEmpty((el.data as ServiceConnectionNode).serviceAnomalyStats) ); - expect(dataWithAnomalies).not.to.be.empty(); - dataWithAnomalies.forEach(({ data }: any) => { expect( Object.values(data.serviceAnomalyStats).filter((value) => isEmpty(value)) ).to.not.empty(); }); }); - it('returns the correct anomaly stats', () => { const dataWithAnomalies = response.body.elements.filter( - (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) + (el) => !isEmpty((el.data as ServiceConnectionNode).serviceAnomalyStats) ); - expect(dataWithAnomalies).not.to.be.empty(); - expectSnapshot(dataWithAnomalies.length).toMatchInline(`7`); expectSnapshot(orderBy(dataWithAnomalies, 'data.id').slice(0, 3)).toMatchInline(` - Array [ - Object { - "data": Object { - "agent.name": "dotnet", - "id": "opbeans-dotnet", - "service.environment": "production", - "service.name": "opbeans-dotnet", - "serviceAnomalyStats": Object { - "actualValue": 868025.86875, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-6117-high_mean_transaction_duration", - "serviceName": "opbeans-dotnet", - "transactionType": "request", - }, - }, - }, - Object { - "data": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - "serviceAnomalyStats": Object { - "actualValue": 102786.319148936, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-testing-41e5-high_mean_transaction_duration", - "serviceName": "opbeans-go", - "transactionType": "request", - }, - }, - }, - Object { - "data": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 175568.855769231, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-6117-high_mean_transaction_duration", - "serviceName": "opbeans-java", - "transactionType": "request", - }, - }, - }, - ] - `); + Array [ + Object { + "data": Object { + "agent.name": "dotnet", + "id": "opbeans-dotnet", + "service.environment": "production", + "service.name": "opbeans-dotnet", + "serviceAnomalyStats": Object { + "actualValue": 868025.86875, + "anomalyScore": 0, + "healthStatus": "healthy", + "jobId": "apm-production-6117-high_mean_transaction_duration", + "serviceName": "opbeans-dotnet", + "transactionType": "request", + }, + }, + }, + Object { + "data": Object { + "agent.name": "go", + "id": "opbeans-go", + "service.environment": "testing", + "service.name": "opbeans-go", + "serviceAnomalyStats": Object { + "actualValue": 102786.319148936, + "anomalyScore": 0, + "healthStatus": "healthy", + "jobId": "apm-testing-41e5-high_mean_transaction_duration", + "serviceName": "opbeans-go", + "transactionType": "request", + }, + }, + }, + Object { + "data": Object { + "agent.name": "java", + "id": "opbeans-java", + "service.environment": "production", + "service.name": "opbeans-java", + "serviceAnomalyStats": Object { + "actualValue": 175568.855769231, + "anomalyScore": 0, + "healthStatus": "healthy", + "jobId": "apm-production-6117-high_mean_transaction_duration", + "serviceName": "opbeans-java", + "transactionType": "request", + }, + }, + }, + ] + `); }); }); - describe('with a user that does not have access to ML', () => { before(async () => { - response = await supertestAsApmReadUserWithoutMlAccess.get( - `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` - ); + response = await apmApiClient.noMlAccessUser({ + endpoint: `GET /internal/apm/service-map`, + params: { + query: { + environment: 'ENVIRONMENT_ALL', + start: metadata.start, + end: metadata.end, + }, + }, + }); }); - it('returns service map elements without anomaly stats', () => { expect(response.status).to.be(200); - const dataWithAnomalies = response.body.elements.filter( - (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) + (el) => !isEmpty((el.data as ServiceConnectionNode).serviceAnomalyStats) ); - expect(dataWithAnomalies).to.be.empty(); }); }); @@ -261,20 +297,25 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) describe('with a single service', () => { describe('when ENVIRONMENT_ALL is selected', () => { - it('returns service map elements', async () => { - response = await supertest.get( - url.format({ - pathname: '/internal/apm/service-map', + before(async () => { + response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map`, + params: { query: { environment: 'ENVIRONMENT_ALL', start: metadata.start, end: metadata.end, serviceName: 'opbeans-java', }, - }) - ); + }, + }); + }); + it('retuns status code 200', () => { expect(response.status).to.be(200); + }); + + it('returns some elements', () => { expect(response.body.elements.length).to.be.greaterThan(1); }); }); @@ -282,51 +323,79 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); describe('/internal/apm/service-map/service/{serviceName}', () => { - it('returns an object with data', async () => { - const q = querystring.stringify({ - start: metadata.start, - end: metadata.end, - environment: 'ENVIRONMENT_ALL', + let response: ServiceNodeResponse; + before(async () => { + response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map/service/{serviceName}`, + params: { + path: { serviceName: 'opbeans-node' }, + query: { + start: metadata.start, + end: metadata.end, + environment: 'ENVIRONMENT_ALL', + }, + }, }); - const response = await supertest.get(`/internal/apm/service-map/service/opbeans-node?${q}`); + }); + it('retuns status code 200', () => { expect(response.status).to.be(200); + }); - expectSnapshot(response.body).toMatchInline(` - Object { - "avgCpuUsage": 0.240216666666667, - "avgErrorRate": 0, - "avgMemoryUsage": 0.202572668763642, - "transactionStats": Object { - "avgRequestsPerMinute": 5.2, - "avgTransactionDuration": 53906.6603773585, - }, - } - `); + it('returns some error rate', () => { + expect(response.body.failedTransactionsRate?.value).to.eql(0); + expect(response.body.failedTransactionsRate?.timeseries?.length).to.be.greaterThan(0); + }); + + it('returns some latency', () => { + expect(response.body.transactionStats?.latency?.value).to.be.greaterThan(0); + expect(response.body.transactionStats?.latency?.timeseries?.length).to.be.greaterThan(0); + }); + + it('returns some throughput', () => { + expect(response.body.transactionStats?.throughput?.value).to.be.greaterThan(0); + expect(response.body.transactionStats?.throughput?.timeseries?.length).to.be.greaterThan(0); + }); + + it('returns some cpu usage', () => { + expect(response.body.cpuUsage?.value).to.be.greaterThan(0); + expect(response.body.cpuUsage?.timeseries?.length).to.be.greaterThan(0); }); }); describe('/internal/apm/service-map/backend', () => { - it('returns an object with data', async () => { - const q = querystring.stringify({ - backendName: 'postgresql', - start: metadata.start, - end: metadata.end, - environment: 'ENVIRONMENT_ALL', + let response: BackendResponse; + before(async () => { + response = await apmApiClient.readUser({ + endpoint: `GET /internal/apm/service-map/backend`, + params: { + query: { + backendName: 'postgresql', + start: metadata.start, + end: metadata.end, + environment: 'ENVIRONMENT_ALL', + }, + }, }); - const response = await supertest.get(`/internal/apm/service-map/backend?${q}`); + }); + it('retuns status code 200', () => { expect(response.status).to.be(200); + }); - expectSnapshot(response.body).toMatchInline(` - Object { - "avgErrorRate": 0, - "transactionStats": Object { - "avgRequestsPerMinute": 82.9666666666667, - "avgTransactionDuration": 18307.583366814, - }, - } - `); + it('returns some error rate', () => { + expect(response.body.failedTransactionsRate?.value).to.eql(0); + expect(response.body.failedTransactionsRate?.timeseries?.length).to.be.greaterThan(0); + }); + + it('returns some latency', () => { + expect(response.body.transactionStats?.latency?.value).to.be.greaterThan(0); + expect(response.body.transactionStats?.latency?.timeseries?.length).to.be.greaterThan(0); + }); + + it('returns some throughput', () => { + expect(response.body.transactionStats?.throughput?.value).to.be.greaterThan(0); + expect(response.body.transactionStats?.throughput?.timeseries?.length).to.be.greaterThan(0); }); }); }); From 44f3f92682515b71b65311a8b59d0b585bb14e20 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Thu, 2 Dec 2021 12:58:02 -0600 Subject: [PATCH 12/65] Replace `EuiThemeProvider` with `KibanaThemeProvider` (#120244) --- .../enterprise_search/public/applications/index.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index f8db22563ec5a..de112dd60863f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -16,8 +16,10 @@ import { Store } from 'redux'; import { I18nProvider } from '@kbn/i18n-react'; import { AppMountParameters, CoreStart } from '../../../../../src/core/public'; -import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; -import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + KibanaThemeProvider, +} from '../../../../../src/plugins/kibana_react/public'; import { InitialAppData } from '../../common/types'; import { PluginsStart, ClientConfigType, ClientData } from '../plugin'; @@ -70,7 +72,7 @@ export const renderApp = ( ReactDOM.render( - + @@ -79,7 +81,7 @@ export const renderApp = ( - + , params.element ); From 553928c4e18eefdaa2d2d5e94e236a8999093d11 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 Dec 2021 12:04:55 -0700 Subject: [PATCH 13/65] [Maps] remove getIndexPatternsService from server.kibana_server_services (#120156) * [Maps] remove getIndexPatternsService from server.kibana_server_services * eslint --- .../maps/server/kibana_server_services.ts | 40 +++++++------------ .../maps_telemetry/maps_telemetry.test.js | 30 +++++++++----- .../server/maps_telemetry/maps_telemetry.ts | 17 +++++++- x-pack/plugins/maps/server/plugin.ts | 11 +---- 4 files changed, 52 insertions(+), 46 deletions(-) diff --git a/x-pack/plugins/maps/server/kibana_server_services.ts b/x-pack/plugins/maps/server/kibana_server_services.ts index f5bd4dad085d8..28c7caf3c50e3 100644 --- a/x-pack/plugins/maps/server/kibana_server_services.ts +++ b/x-pack/plugins/maps/server/kibana_server_services.ts @@ -5,32 +5,20 @@ * 2.0. */ -import { ElasticsearchClient, ISavedObjectsRepository } from 'kibana/server'; -import { SavedObjectsClient } from '../../../../src/core/server'; -import { - IndexPatternsCommonService, - IndexPatternsServiceStart, -} from '../../../../src/plugins/data/server'; +import { CoreStart } from '../../../../src/core/server'; +import { StartDeps } from './plugin'; -let internalRepository: ISavedObjectsRepository; -export const setInternalRepository = ( - createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository -) => { - internalRepository = createInternalRepository(); -}; -export const getInternalRepository = () => internalRepository; +let coreStart: CoreStart; +let pluginsStart: StartDeps; +export function setStartServices(core: CoreStart, plugins: StartDeps) { + coreStart = core; + pluginsStart = plugins; +} -let esClient: ElasticsearchClient; -let indexPatternsService: IndexPatternsCommonService; -export const setIndexPatternsService = async ( - indexPatternsServiceFactory: IndexPatternsServiceStart['indexPatternsServiceFactory'], - elasticsearchClient: ElasticsearchClient -) => { - esClient = elasticsearchClient; - indexPatternsService = await indexPatternsServiceFactory( - new SavedObjectsClient(getInternalRepository()), - elasticsearchClient - ); +export const getSavedObjectClient = (extraTypes?: string[]) => { + return coreStart.savedObjects.createInternalRepository(extraTypes); }; -export const getIndexPatternsService = () => indexPatternsService; -export const getESClient = () => esClient; + +export const getIndexPatternsServiceFactory = () => + pluginsStart.data.indexPatterns.indexPatternsServiceFactory; +export const getElasticsearch = () => coreStart.elasticsearch; diff --git a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.test.js b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.test.js index c9720063290b0..a415d181900d7 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.test.js +++ b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.test.js @@ -54,19 +54,31 @@ jest.mock('../kibana_server_services', () => { }, }; return { - getIndexPatternsService() { + getSavedObjectClient: () => { + return {}; + }, + getElasticsearch: () => { return { - async get(x) { - return x === testAggIndexPatternId ? testAggIndexPattern : testIndexPatterns[x]; - }, - async getIds() { - return Object.values(testIndexPatterns).map((x) => x.id); - }, - async getFieldsForIndexPattern(x) { - return x.fields; + client: { + asInternalUser: {}, }, }; }, + getIndexPatternsServiceFactory() { + return function () { + return { + async get(x) { + return x === testAggIndexPatternId ? testAggIndexPattern : testIndexPatterns[x]; + }, + async getIds() { + return Object.values(testIndexPatterns).map((x) => x.id); + }, + async getFieldsForIndexPattern(x) { + return x.fields; + }, + }; + }; + }, }; }); diff --git a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts index 5041cb997ff58..93a9a118d23bc 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts @@ -22,7 +22,11 @@ import { LayerDescriptor, } from '../../common/descriptor_types'; import { MapSavedObject, MapSavedObjectAttributes } from '../../common/map_saved_object_type'; -import { getIndexPatternsService, getInternalRepository } from '../kibana_server_services'; +import { + getElasticsearch, + getIndexPatternsServiceFactory, + getSavedObjectClient, +} from '../kibana_server_services'; import { injectReferences } from '././../../common/migrations/references'; import { getBaseMapsPerCluster, @@ -36,6 +40,15 @@ import { TELEMETRY_SCALING_OPTION_COUNTS_PER_CLUSTER, TELEMETRY_TERM_JOIN_COUNTS_PER_CLUSTER, } from './util'; +import { SavedObjectsClient } from '../../../../../src/core/server'; + +async function getIndexPatternsService() { + const factory = getIndexPatternsServiceFactory(); + return factory( + new SavedObjectsClient(getSavedObjectClient()), + getElasticsearch().client.asInternalUser + ); +} interface IStats { [key: string]: { @@ -302,7 +315,7 @@ export async function execTransformOverMultipleSavedObjectPages( savedObjectType: string, transform: (savedObjects: Array>) => void ) { - const savedObjectsClient = getInternalRepository(); + const savedObjectsClient = getSavedObjectClient(); let currentPage = 1; // Seed values diff --git a/x-pack/plugins/maps/server/plugin.ts b/x-pack/plugins/maps/server/plugin.ts index b35f680df457c..ec9b3652fddbd 100644 --- a/x-pack/plugins/maps/server/plugin.ts +++ b/x-pack/plugins/maps/server/plugin.ts @@ -25,8 +25,7 @@ import { registerMapsUsageCollector } from './maps_telemetry/collectors/register import { APP_ID, APP_ICON, MAP_SAVED_OBJECT_TYPE, getFullPath } from '../common/constants'; import { mapSavedObjects, mapsTelemetrySavedObjects } from './saved_objects'; import { MapsXPackConfig } from '../config'; -// @ts-ignore -import { setIndexPatternsService, setInternalRepository } from './kibana_server_services'; +import { setStartServices } from './kibana_server_services'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; import { emsBoundariesSpecProvider } from './tutorials/ems'; // @ts-ignore @@ -160,7 +159,6 @@ export class MapsPlugin implements Plugin { ); } - // @ts-ignore setup(core: CoreSetup, plugins: SetupDeps) { const { usageCollection, home, licensing, features, mapsEms, customIntegrations } = plugins; const mapsEmsConfig = mapsEms.config; @@ -230,12 +228,7 @@ export class MapsPlugin implements Plugin { }; } - // @ts-ignore start(core: CoreStart, plugins: StartDeps) { - setInternalRepository(core.savedObjects.createInternalRepository); - setIndexPatternsService( - plugins.data.indexPatterns.indexPatternsServiceFactory, - core.elasticsearch.client.asInternalUser - ); + setStartServices(core, plugins); } } From 915206531bc35d127fec88e8b2e44811ace47596 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Thu, 2 Dec 2021 12:10:48 -0700 Subject: [PATCH 14/65] [Security Solutions] Removes tech debt of exporting all from linter rule for security_solution plugin (#120188) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary See: https://github.com/elastic/kibana/issues/110903 This removes the top level API `export *` spots from: * `security_solution` plugin by removing _all_ the exports from `security_solution/common/index.ts` since non of those were shared outside this plugin. Look at the metrics from the build below and you will see _huge_ drops off numbers across the board for required API documentation to the page load size. In the file `security_solution/common/index.ts` I now put the advice of: ``` // Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase. // If you're using functions/types/etc... internally it's best to import directly from their paths than expose the functions/types/etc... here. // You should _only_ expose functions/types/etc... that need to be shared with other plugins here. ``` But really I doubt we will have to share anything from `security_solutions` plugin to another plugin or expose it for anyone else. So I think this is 👍 the way forward to not expose anything directly from `security_solution/common/index.ts` anymore. --- x-pack/plugins/lists/common/index.ts | 4 ++++ x-pack/plugins/security_solution/common/index.ts | 9 +++------ .../components/event_details/alert_summary_view.test.tsx | 2 +- .../components/event_details/alert_summary_view.tsx | 3 ++- .../public/common/components/event_details/columns.tsx | 3 ++- .../event_details/cti_details/enrichment_summary.tsx | 6 +++++- .../event_details/cti_details/threat_summary_view.tsx | 6 +++++- .../public/common/components/event_details/helpers.tsx | 2 +- .../event_details/investigation_guide_view.tsx | 2 +- .../public/common/components/event_details/reason.tsx | 2 +- .../common/components/events_viewer/default_headers.tsx | 2 +- .../public/common/containers/sourcerer/index.tsx | 2 +- .../security_solution/public/common/mock/header.ts | 2 +- .../alerts_kpis/alerts_count_panel/alerts_count.tsx | 2 +- .../components/alerts_kpis/alerts_count_panel/types.ts | 2 +- .../timeline_actions/alert_context_menu.test.tsx | 2 +- .../alerts_table/timeline_actions/alert_context_menu.tsx | 2 +- .../timeline_actions/investigate_in_resolver.tsx | 2 +- .../timeline_actions/use_add_to_case_actions.tsx | 3 ++- .../detections/components/host_isolation/helpers.ts | 2 +- .../host_isolation/use_host_isolation_action.tsx | 2 +- .../detections/components/rules/query_preview/index.tsx | 2 +- .../rules/rule_preview/use_preview_histogram.tsx | 2 +- .../components/take_action_dropdown/index.test.tsx | 3 ++- .../detections/components/take_action_dropdown/index.tsx | 2 +- .../examples/observablity_alerts/columns.ts | 2 +- .../observablity_alerts/render_cell_value.test.tsx | 2 +- .../examples/security_solution_rac/columns.ts | 2 +- .../security_solution_rac/render_cell_value.test.tsx | 2 +- .../security_solution_detections/columns.ts | 2 +- .../render_cell_value.test.tsx | 2 +- .../risky_hosts_enabled_module.tsx | 2 +- .../overview_risky_host_links/use_hosts_risk_score.ts | 2 +- .../use_hosts_risk_score_complete.ts | 2 +- .../components/create_field_button/index.test.tsx | 2 +- .../timelines/components/fields_browser/field_items.tsx | 2 +- .../timelines/components/fields_browser/field_name.tsx | 2 +- .../timelines/components/formatted_ip/index.test.tsx | 2 +- .../components/side_panel/event_details/footer.tsx | 3 ++- .../components/side_panel/event_details/index.tsx | 2 +- .../timeline/body/column_headers/actions/index.tsx | 2 +- .../timeline/body/column_headers/default_headers.ts | 2 +- .../timeline/body/column_headers/filter/index.tsx | 2 +- .../timeline/body/column_headers/header/index.tsx | 2 +- .../column_headers/header_tooltip_content/index.test.tsx | 2 +- .../body/column_headers/header_tooltip_content/index.tsx | 2 +- .../components/timeline/body/column_headers/helpers.ts | 2 +- .../timeline/body/events/stateful_row_renderer/index.tsx | 2 +- .../body/renderers/auditd/generic_row_renderer.test.tsx | 2 +- .../timeline/body/renderers/column_renderer.ts | 2 +- .../timeline/body/renderers/cti/threat_match_rows.tsx | 2 +- .../timeline/body/renderers/empty_column_renderer.tsx | 2 +- .../timeline/body/renderers/get_row_renderer.ts | 2 +- .../timeline/body/renderers/host_name.test.tsx | 2 +- .../components/timeline/body/renderers/index.ts | 2 +- .../timeline/body/renderers/plain_column_renderer.tsx | 2 +- .../body/renderers/reason_column_renderer.test.tsx | 9 +++------ .../timeline/body/renderers/reason_column_renderer.tsx | 3 ++- .../body/renderers/system/generic_row_renderer.test.tsx | 2 +- .../public/ueba/components/host_rules_table/columns.tsx | 2 +- .../public/ueba/components/host_rules_table/index.tsx | 7 +++---- .../ueba/components/host_tactics_table/columns.tsx | 2 +- .../public/ueba/components/host_tactics_table/index.tsx | 4 ++-- .../public/ueba/components/risk_score_table/index.tsx | 7 +++---- .../public/ueba/containers/host_rules/index.tsx | 2 +- .../public/ueba/containers/host_tactics/index.tsx | 2 +- .../public/ueba/containers/risk_score/index.tsx | 2 +- .../public/ueba/containers/user_rules/index.tsx | 2 +- .../ueba/pages/navigation/user_rules_query_tab_body.tsx | 2 +- .../plugins/security_solution/public/ueba/pages/ueba.tsx | 2 +- .../source_fields_merging/strategies/get_strategy.ts | 2 +- .../routes/timelines/create_timelines/helpers.test.ts | 2 +- .../security_solution/factory/hosts/risk_score/index.ts | 2 +- .../factory/hosts/risk_score/query.hosts_risk.dsl.ts | 2 +- .../factory/matrix_histogram/alerts/index.ts | 2 +- .../factory/matrix_histogram/anomalies/index.ts | 2 +- .../factory/matrix_histogram/authentications/index.ts | 2 +- .../factory/matrix_histogram/dns/index.ts | 2 +- .../factory/matrix_histogram/events/index.ts | 2 +- .../factory/matrix_histogram/preview/__mocks__/index.ts | 2 +- .../factory/matrix_histogram/preview/index.ts | 2 +- .../security_solution/factory/ueba/host_rules/helpers.ts | 6 +++++- .../security_solution/factory/ueba/host_rules/index.ts | 2 +- .../factory/ueba/host_tactics/helpers.ts | 2 +- .../security_solution/factory/ueba/host_tactics/index.ts | 4 ++-- .../security_solution/factory/ueba/risk_score/helpers.ts | 2 +- .../security_solution/factory/ueba/risk_score/index.ts | 4 ++-- .../security_solution/factory/ueba/user_rules/helpers.ts | 6 +++++- .../security_solution/factory/ueba/user_rules/index.ts | 2 +- .../services/security_solution/roles_users_utils.ts | 2 +- 90 files changed, 126 insertions(+), 108 deletions(-) diff --git a/x-pack/plugins/lists/common/index.ts b/x-pack/plugins/lists/common/index.ts index 1fec1c76430eb..a9aa96c2d9c60 100644 --- a/x-pack/plugins/lists/common/index.ts +++ b/x-pack/plugins/lists/common/index.ts @@ -4,3 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + +// Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase. +// If you're using functions/types/etc... internally it's best to import directly from their paths than expose the functions/types/etc... here. +// You should _only_ expose functions/types/etc... that need to be shared with other plugins here. diff --git a/x-pack/plugins/security_solution/common/index.ts b/x-pack/plugins/security_solution/common/index.ts index 19ce0d45e2485..a9aa96c2d9c60 100644 --- a/x-pack/plugins/security_solution/common/index.ts +++ b/x-pack/plugins/security_solution/common/index.ts @@ -5,9 +5,6 @@ * 2.0. */ -// TODO: https://github.com/elastic/kibana/issues/110904 -/* eslint-disable @kbn/eslint/no_export_all */ - -export * from './types'; -export * from './search_strategy'; -export * from './utility_types'; +// Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase. +// If you're using functions/types/etc... internally it's best to import directly from their paths than expose the functions/types/etc... here. +// You should _only_ expose functions/types/etc... that need to be shared with other plugins here. diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx index f7522cb4dd585..7e1e71a01642f 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx @@ -14,7 +14,7 @@ import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { useRuleWithFallback } from '../../../detections/containers/detection_engine/rules/use_rule_with_fallback'; import { TestProviders, TestProvidersComponent } from '../../mock'; -import { TimelineId } from '../../../../common'; +import { TimelineId } from '../../../../common/types'; import { mockBrowserFields } from '../../containers/source/mock'; jest.mock('../../lib/kibana'); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx index 82259aa2312ae..b42a0425355cc 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx @@ -14,7 +14,8 @@ import { AlertSummaryRow, getSummaryColumns, SummaryRow } from './helpers'; import { ActionCell } from './table/action_cell'; import { FieldValueCell } from './table/field_value_cell'; -import { TimelineEventsDetailsItem, TimelineId } from '../../../../common'; +import { TimelineId } from '../../../../common/types'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { getSummaryRows } from './get_alert_summary_rows'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx index dbb52ade2652c..811c43fdfc5b9 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx @@ -14,7 +14,8 @@ import { BrowserFields } from '../../containers/source'; import { OnUpdateColumns } from '../../../timelines/components/timeline/events'; import * as i18n from './translations'; import { EventFieldsData } from './types'; -import { BrowserField, ColumnHeaderOptions } from '../../../../common'; +import { ColumnHeaderOptions } from '../../../../common/types'; +import { BrowserField } from '../../../../common/search_strategy'; import { FieldValueCell } from './table/field_value_cell'; import { FieldNameCell } from './table/field_name_cell'; import { ActionCell } from './table/action_cell'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/enrichment_summary.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/enrichment_summary.tsx index 37fbab924afa3..c2254c18f4364 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/enrichment_summary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/enrichment_summary.tsx @@ -16,7 +16,11 @@ import { getEnrichmentIdentifiers, isInvestigationTimeEnrichment } from './helpe import { FieldsData } from '../types'; import { ActionCell } from '../table/action_cell'; -import { BrowserField, BrowserFields, TimelineEventsDetailsItem } from '../../../../../common'; +import { + BrowserField, + BrowserFields, + TimelineEventsDetailsItem, +} from '../../../../../common/search_strategy'; import { FormattedFieldValue } from '../../../../timelines/components/timeline/body/renderers/formatted_field'; import { EnrichedDataRow, ThreatSummaryPanelHeader } from './threat_summary_view'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx index bdd342934eeb6..c4d7902e151b4 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx @@ -23,7 +23,11 @@ import { CtiEnrichment } from '../../../../../common/search_strategy/security_so import { FieldsData } from '../types'; -import { BrowserField, BrowserFields, TimelineEventsDetailsItem } from '../../../../../common'; +import { + BrowserField, + BrowserFields, + TimelineEventsDetailsItem, +} from '../../../../../common/search_strategy'; import { HostRisk } from '../../../../overview/containers/overview_risky_host_links/use_hosts_risk_score'; import { HostRiskSummary } from './host_risk_summary'; import { EnrichmentSummary } from './enrichment_summary'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx index 47d0ccf5ba3b2..648bc96b5c9e7 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx @@ -25,7 +25,7 @@ import { import { FieldsData } from './types'; import * as i18n from './translations'; -import { ColumnHeaderOptions } from '../../../../common'; +import { ColumnHeaderOptions } from '../../../../common/types'; /** * Defines the behavior of the search input that appears above the table of data diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/investigation_guide_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/investigation_guide_view.tsx index f845e8ecba6b6..b4abd253b4a96 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/investigation_guide_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/investigation_guide_view.tsx @@ -15,7 +15,7 @@ import * as i18n from './translations'; import { useRuleWithFallback } from '../../../detections/containers/detection_engine/rules/use_rule_with_fallback'; import { MarkdownRenderer } from '../markdown_editor'; import { LineClamp } from '../line_clamp'; -import { TimelineEventsDetailsItem } from '../../../../common'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; export const Indent = styled.div` padding: 0 8px; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/reason.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/reason.tsx index cf69e4ba02c3e..d06f4d3ea105b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/reason.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/reason.tsx @@ -13,7 +13,7 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; import { getRuleDetailsUrl, useFormatUrl } from '../link_to'; import * as i18n from './translations'; -import { TimelineEventsDetailsItem } from '../../../../common'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { LinkAnchor } from '../links'; import { useKibana } from '../../lib/kibana'; import { APP_UI_ID, SecurityPageName } from '../../../../common/constants'; diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/default_headers.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/default_headers.tsx index a511af16bbf71..1578c77f283fb 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/default_headers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/default_headers.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { ColumnHeaderOptions } from '../../../../common'; +import { ColumnHeaderOptions } from '../../../../common/types'; import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers'; import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx index 7aea1e881bb52..3311207eb1420 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx @@ -22,7 +22,7 @@ import { RULES_PATH, UEBA_PATH, } from '../../../../common/constants'; -import { TimelineId } from '../../../../common'; +import { TimelineId } from '../../../../common/types'; import { useDeepEqualSelector } from '../../hooks/use_selector'; import { checkIfIndicesExist, getScopePatternListSelection } from '../../store/sourcerer/helpers'; import { useAppToasts } from '../../hooks/use_app_toasts'; diff --git a/x-pack/plugins/security_solution/public/common/mock/header.ts b/x-pack/plugins/security_solution/public/common/mock/header.ts index 029ddb00d1832..66bfda1a02619 100644 --- a/x-pack/plugins/security_solution/public/common/mock/header.ts +++ b/x-pack/plugins/security_solution/public/common/mock/header.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ColumnHeaderOptions } from '../../../common'; +import { ColumnHeaderOptions } from '../../../common/types'; import { defaultColumnHeaderType } from '../../timelines/components/timeline/body/column_headers/default_headers'; import { DEFAULT_COLUMN_MIN_WIDTH, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx index d6b1afea98592..764a13c17ce84 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx @@ -13,7 +13,7 @@ import { useUiSetting$ } from '../../../../common/lib/kibana'; import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; import * as i18n from './translations'; import { DefaultDraggable } from '../../../../common/components/draggables'; -import type { GenericBuckets } from '../../../../../common'; +import type { GenericBuckets } from '../../../../../common/search_strategy'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; import type { AlertsCountAggregation } from './types'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/types.ts index 06cdee581d3fd..b541c7234f08e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { GenericBuckets } from '../../../../../common'; +import type { GenericBuckets } from '../../../../../common/search_strategy'; export interface AlertsCountAggregation { alertsByGroupingCount: { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx index 9568f9c894e24..a1684d6564a0a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx @@ -7,7 +7,7 @@ import { mount } from 'enzyme'; import { AlertContextMenu } from './alert_context_menu'; -import { TimelineId } from '../../../../../common'; +import { TimelineId } from '../../../../../common/types'; import { TestProviders } from '../../../../common/mock'; import React from 'react'; import { Ecs } from '../../../../../common/ecs'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx index 7a6ff229fbcd9..c95006866194b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx @@ -23,7 +23,7 @@ import { } from '../../../../common/components/exceptions/add_exception_modal'; import * as i18n from '../translations'; import { inputsModel, inputsSelectors, State } from '../../../../common/store'; -import { TimelineId } from '../../../../../common'; +import { TimelineId } from '../../../../../common/types'; import { AlertData, EcsHit } from '../../../../common/components/exceptions/types'; import { useQueryAlerts } from '../../../containers/detection_engine/alerts/use_query'; import { useSignalIndex } from '../../../containers/detection_engine/alerts/use_signal_index'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx index 21d0e132599fb..db7ba367d0ace 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx @@ -17,7 +17,7 @@ import { useGlobalFullScreen, useTimelineFullScreen, } from '../../../../common/containers/use_full_screen'; -import { TimelineId, TimelineTabs } from '../../../../../common'; +import { TimelineId, TimelineTabs } from '../../../../../common/types'; import { ACTION_INVESTIGATE_IN_RESOLVER } from '../../../../timelines/components/timeline/body/translations'; import { Ecs } from '../../../../../common/ecs'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx index 708c3c396d0d1..cc0ef8d4e8b79 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx @@ -7,7 +7,8 @@ import { useMemo } from 'react'; import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana'; -import { TimelineId, TimelineNonEcsData } from '../../../../../common'; +import type { TimelineNonEcsData } from '../../../../../common/search_strategy'; +import { TimelineId } from '../../../../../common/types'; import { APP_ID, APP_UI_ID } from '../../../../../common/constants'; import { useInsertTimeline } from '../../../../cases/components/use_insert_timeline'; import { Ecs } from '../../../../../common/ecs'; diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/host_isolation/helpers.ts index d0951172c21a9..32fa616831dfb 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/helpers.ts @@ -6,7 +6,7 @@ */ import { find } from 'lodash/fp'; -import type { TimelineEventsDetailsItem } from '../../../../common'; +import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; export const getFieldValues = ( { diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx index 4282a584ea9f3..a7c3cb900d7c9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx @@ -6,7 +6,7 @@ */ import React, { useCallback, useMemo } from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; -import type { TimelineEventsDetailsItem } from '../../../../common'; +import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { isIsolationSupported } from '../../../../common/endpoint/service/host_isolation/utils'; import { HostStatus } from '../../../../common/endpoint/types'; import { useIsolationPrivileges } from '../../../common/hooks/endpoint/use_isolate_privileges'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx index ca4fba4a908ff..e7cc34ef49bef 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx @@ -23,7 +23,7 @@ import { debounce } from 'lodash/fp'; import { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import * as i18n from '../rule_preview/translations'; import { useMatrixHistogram } from '../../../../common/containers/matrix_histogram'; -import { MatrixHistogramType } from '../../../../../common'; +import { MatrixHistogramType } from '../../../../../common/search_strategy'; import { FieldValueQueryBar } from '../query_bar'; import { PreviewEqlQueryHistogram } from './eql_histogram'; import { useEqlPreview } from '../../../../common/hooks/eql/'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_histogram.tsx index b7c415615f97a..5d8a787929acf 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_histogram.tsx @@ -6,7 +6,7 @@ */ import { useMemo } from 'react'; import { useMatrixHistogram } from '../../../../common/containers/matrix_histogram'; -import { MatrixHistogramType } from '../../../../../common'; +import { MatrixHistogramType } from '../../../../../common/search_strategy'; import { convertToBuildEsQuery } from '../../../../common/lib/keury'; import { getEsQueryConfig } from '../../../../../../../../src/plugins/data/common'; import { useKibana } from '../../../../common/lib/kibana'; diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx index a2f2ba331d959..ab9d4a6344aec 100644 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx @@ -11,7 +11,8 @@ import { waitFor } from '@testing-library/react'; import { TakeActionDropdown, TakeActionDropdownProps } from '.'; import { mockAlertDetailsData } from '../../../common/components/event_details/__mocks__'; import { mockEcsDataWithAlert } from '../../../common/mock/mock_detection_alerts'; -import { TimelineEventsDetailsItem, TimelineId } from '../../../../common'; +import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; +import { TimelineId } from '../../../../common/types'; import { TestProviders } from '../../../common/mock'; import { mockTimelines } from '../../../common/mock/mock_timelines_plugin'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.tsx b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.tsx index 84cca38014fde..3a388104d926e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.tsx @@ -9,7 +9,7 @@ import React, { useState, useCallback, useMemo } from 'react'; import { EuiContextMenuPanel, EuiButton, EuiPopover } from '@elastic/eui'; import type { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types'; import { isEmpty } from 'lodash/fp'; -import { TimelineEventsDetailsItem } from '../../../../common'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { TAKE_ACTION } from '../alerts_table/alerts_utility_bar/translations'; import { useExceptionActions } from '../alerts_table/timeline_actions/use_add_exception_actions'; import { useAlertsActions } from '../alerts_table/timeline_actions/use_alerts_actions'; diff --git a/x-pack/plugins/security_solution/public/detections/configurations/examples/observablity_alerts/columns.ts b/x-pack/plugins/security_solution/public/detections/configurations/examples/observablity_alerts/columns.ts index 37199840ae3fb..d3ba561bcd699 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/examples/observablity_alerts/columns.ts +++ b/x-pack/plugins/security_solution/public/detections/configurations/examples/observablity_alerts/columns.ts @@ -12,7 +12,7 @@ import { ALERT_STATUS, } from '@kbn/rule-data-utils/technical_field_names'; -import { ColumnHeaderOptions } from '../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../common/types'; import { defaultColumnHeaderType } from '../../../../timelines/components/timeline/body/column_headers/default_headers'; import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../../timelines/components/timeline/body/constants'; diff --git a/x-pack/plugins/security_solution/public/detections/configurations/examples/observablity_alerts/render_cell_value.test.tsx b/x-pack/plugins/security_solution/public/detections/configurations/examples/observablity_alerts/render_cell_value.test.tsx index 685bc8f42b1d8..95787629079fa 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/examples/observablity_alerts/render_cell_value.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/configurations/examples/observablity_alerts/render_cell_value.test.tsx @@ -17,7 +17,7 @@ import { defaultHeaders, mockTimelineData, TestProviders } from '../../../../com import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline'; import { CellValueElementProps } from '../../../../timelines/components/timeline/cell_rendering'; import { DefaultCellRenderer } from '../../../../timelines/components/timeline/cell_rendering/default_cell_renderer'; -import { ColumnHeaderOptions } from '../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../common/types'; import { RenderCellValue } from '.'; diff --git a/x-pack/plugins/security_solution/public/detections/configurations/examples/security_solution_rac/columns.ts b/x-pack/plugins/security_solution/public/detections/configurations/examples/security_solution_rac/columns.ts index 45433a39d8b97..7ae54e60944cc 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/examples/security_solution_rac/columns.ts +++ b/x-pack/plugins/security_solution/public/detections/configurations/examples/security_solution_rac/columns.ts @@ -6,7 +6,7 @@ */ import { EuiDataGridColumn } from '@elastic/eui'; -import { ColumnHeaderOptions } from '../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../common/types'; import { defaultColumnHeaderType } from '../../../../timelines/components/timeline/body/column_headers/default_headers'; import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../../timelines/components/timeline/body/constants'; diff --git a/x-pack/plugins/security_solution/public/detections/configurations/examples/security_solution_rac/render_cell_value.test.tsx b/x-pack/plugins/security_solution/public/detections/configurations/examples/security_solution_rac/render_cell_value.test.tsx index ccd71404a2216..f9dfc0625ac17 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/examples/security_solution_rac/render_cell_value.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/configurations/examples/security_solution_rac/render_cell_value.test.tsx @@ -17,7 +17,7 @@ import { CellValueElementProps } from '../../../../timelines/components/timeline import { DefaultCellRenderer } from '../../../../timelines/components/timeline/cell_rendering/default_cell_renderer'; import { RenderCellValue } from '.'; -import { ColumnHeaderOptions } from '../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../common/types'; jest.mock('../../../../common/lib/kibana/'); diff --git a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts index 72aba6e186fcb..a9e5679c5b831 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts +++ b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts @@ -6,7 +6,7 @@ */ import { EuiDataGridColumn } from '@elastic/eui'; -import { ColumnHeaderOptions } from '../../../../common'; +import { ColumnHeaderOptions } from '../../../../common/types'; import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers'; import { diff --git a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx index a7def2a23ef1d..6087553522eb8 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx @@ -9,7 +9,7 @@ import { mount } from 'enzyme'; import { cloneDeep } from 'lodash/fp'; import React from 'react'; -import { ColumnHeaderOptions } from '../../../../common'; +import { ColumnHeaderOptions } from '../../../../common/types'; import { mockBrowserFields } from '../../../common/containers/source/mock'; import { DragDropContextWrapper } from '../../../common/components/drag_and_drop/drag_drop_context_wrapper'; import { defaultHeaders, mockTimelineData, TestProviders } from '../../../common/mock'; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.tsx index 4db6f67acb265..412c4a69ec2f5 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.tsx @@ -11,7 +11,7 @@ import { LinkPanelListItem } from '../link_panel'; import { useRiskyHostsDashboardButtonHref } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'; import { useRiskyHostsDashboardLinks } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_links'; import { HostRisk } from '../../containers/overview_risky_host_links/use_hosts_risk_score'; -import { HostsRiskScore } from '../../../../common'; +import { HostsRiskScore } from '../../../../common/search_strategy'; const getListItemsFromHits = (items: HostsRiskScore[]): LinkPanelListItem[] => { return items.map(({ host, risk_score: count, risk: copy }) => ({ diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score.ts index 15cb7ef7b1c46..eb363f4f77067 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score.ts +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score.ts @@ -13,7 +13,7 @@ import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import { useKibana } from '../../../common/lib/kibana'; import { inputsActions } from '../../../common/store/actions'; import { isIndexNotFoundError } from '../../../common/utils/exceptions'; -import { HostsRiskScore } from '../../../../common'; +import { HostsRiskScore } from '../../../../common/search_strategy'; import { useHostsRiskScoreComplete } from './use_hosts_risk_score_complete'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import { getHostRiskIndex } from '../../../helpers'; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score_complete.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score_complete.ts index f925339e68490..959fb94c5bbd7 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score_complete.ts +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score_complete.ts @@ -16,7 +16,7 @@ import { HostsQueries, HostsRiskScoreRequestOptions, HostsRiskScoreStrategyResponse, -} from '../../../../common'; +} from '../../../../common/search_strategy'; type GetHostsRiskScoreProps = HostsRiskScoreRequestOptions & { data: DataPublicPluginStart; diff --git a/x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.test.tsx index 1708509b31864..59dcf350e9aba 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.test.tsx @@ -16,7 +16,7 @@ import { import { TestProviders } from '../../../common/mock'; import { useKibana } from '../../../common/lib/kibana'; import type { DataView } from '../../../../../../../src/plugins/data/common'; -import { TimelineId } from '../../../../common'; +import { TimelineId } from '../../../../common/types'; const useKibanaMock = useKibana as jest.Mocked; diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_items.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_items.tsx index 73fb7c19a6f46..bb95c615d874e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_items.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_items.tsx @@ -17,7 +17,7 @@ import type { BrowserFields } from '../../../common/containers/source'; import { getColumnsWithTimestamp } from '../../../common/components/event_details/helpers'; import type { OnUpdateColumns } from '../timeline/events'; import { FieldName } from './field_name'; -import type { ColumnHeaderOptions } from '../../../../common'; +import type { ColumnHeaderOptions } from '../../../../common/types'; import { useKibana } from '../../../common/lib/kibana'; const DraggableFieldsBrowserFieldComponent = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx index e833b8411cd9f..6e9672d08b366 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx @@ -11,7 +11,7 @@ import styled from 'styled-components'; import { OnUpdateColumns } from '../timeline/events'; import { WithHoverActions } from '../../../common/components/with_hover_actions'; -import { ColumnHeaderOptions } from '../../../../common'; +import { ColumnHeaderOptions } from '../../../../common/types'; import { HoverActions } from '../../../common/components/hover_actions'; import { TimelineContext } from '../../../../../timelines/public'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx index 7dc0a98461760..f5086fd1a83da 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx @@ -10,7 +10,7 @@ import { waitFor } from '@testing-library/react'; import { FormattedIp } from './index'; import { TestProviders } from '../../../common/mock'; -import { TimelineId, TimelineTabs } from '../../../../common'; +import { TimelineId, TimelineTabs } from '../../../../common/types'; import { StatefulEventContext } from '../../../../../timelines/public'; import { timelineActions } from '../../store/timeline'; import { activeTimeline } from '../../containers/active_timeline_context'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/footer.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/footer.tsx index 50fe19f4d804a..70f3e7004fb8e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/footer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/footer.tsx @@ -10,7 +10,8 @@ import { EuiFlyoutFooter, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { find, get, isEmpty } from 'lodash/fp'; import { connect, ConnectedProps } from 'react-redux'; import { TakeActionDropdown } from '../../../../detections/components/take_action_dropdown'; -import { TimelineEventsDetailsItem, TimelineId } from '../../../../../common'; +import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy'; +import { TimelineId } from '../../../../../common/types'; import { useExceptionModal } from '../../../../detections/components/alerts_table/timeline_actions/use_add_exception_modal'; import { AddExceptionModalWrapper } from '../../../../detections/components/alerts_table/timeline_actions/alert_context_menu'; import { EventFiltersFlyout } from '../../../../management/pages/event_filters/view/components/flyout'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 947786695ded3..224662f0fd6ab 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -31,7 +31,7 @@ import { import { getFieldValue } from '../../../../detections/components/host_isolation/helpers'; import { ALERT_DETAILS } from './translations'; import { useWithCaseDetailsRefresh } from '../../../../common/components/endpoint/host_isolation/endpoint_host_isolation_cases_context'; -import { TimelineNonEcsData } from '../../../../../common'; +import { TimelineNonEcsData } from '../../../../../common/search_strategy'; import { Ecs } from '../../../../../common/ecs'; import { EventDetailsFooter } from './footer'; import { EntityType } from '../../../../../../timelines/common'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx index 8795255dfcfd4..1306509a87a54 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx @@ -8,7 +8,7 @@ import { EuiButtonIcon } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { ColumnHeaderOptions } from '../../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../../common/types'; import { OnColumnRemoved } from '../../../events'; import { EventsHeadingExtra, EventsLoading } from '../../../styles'; import { Sort } from '../../sort'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/default_headers.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/default_headers.ts index 7eb98b7475952..58e8d1869233f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/default_headers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/default_headers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ColumnHeaderOptions } from '../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../common/types'; import { ColumnHeaderType } from '../../../../store/timeline/model'; import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../constants'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/filter/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/filter/index.tsx index 828b8d8701188..b897fef57f076 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/filter/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/filter/index.tsx @@ -8,7 +8,7 @@ import { noop } from 'lodash/fp'; import React from 'react'; -import { ColumnHeaderOptions } from '../../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../../common/types'; import { DEFAULT_COLUMN_MIN_WIDTH } from '../../constants'; import { OnFilterChange } from '../../../events'; import { TextFilter } from '../text_filter'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx index 18b407a035708..166a4c2da871c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { isDataViewFieldSubtypeNested } from '@kbn/es-query'; -import { ColumnHeaderOptions } from '../../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../../common/types'; import { useDeepEqualSelector, useShallowEqualSelector, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx index b33e47dd27b96..8f64b4e7e6db3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx @@ -9,7 +9,7 @@ import { mount, shallow } from 'enzyme'; import { cloneDeep } from 'lodash/fp'; import React from 'react'; -import { ColumnHeaderOptions } from '../../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../../common/types'; import { defaultHeaders } from '../../../../../../common/mock'; import { HeaderToolTipContent } from '.'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx index 017ba82e8e33c..4be37de54b365 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx @@ -10,7 +10,7 @@ import { isEmpty } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; -import { ColumnHeaderOptions } from '../../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../../common/types'; import { getIconFromType } from '../../../../../../common/components/event_details/helpers'; import * as i18n from '../translations'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/helpers.ts index 60118b1e55e58..b1ea4899615a6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/helpers.ts @@ -6,7 +6,7 @@ */ import { get } from 'lodash/fp'; -import { ColumnHeaderOptions } from '../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../common/types'; import { BrowserFields } from '../../../../../common/containers/source'; import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../constants'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx index 7124a3de968ba..d71dfcae1b4e6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx @@ -14,7 +14,7 @@ import { ARIA_ROWINDEX_ATTRIBUTE, getRowRendererClassName, } from '../../../../../../../../timelines/public'; -import { RowRenderer } from '../../../../../../../common'; +import { RowRenderer } from '../../../../../../../common/types'; import { BrowserFields } from '../../../../../../common/containers/source'; import { TimelineItem } from '../../../../../../../common/search_strategy/timeline'; import { getRowRenderer } from '../../renderers/get_row_renderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx index 79836b0ac1e4e..dcfb0efc11455 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx @@ -9,7 +9,7 @@ import { shallow } from 'enzyme'; import { cloneDeep } from 'lodash/fp'; import React from 'react'; -import { RowRenderer } from '../../../../../../../common'; +import { RowRenderer } from '../../../../../../../common/types'; import { BrowserFields } from '../../../../../../common/containers/source'; import { mockBrowserFields } from '../../../../../../common/containers/source/mock'; import { Ecs } from '../../../../../../../common/ecs'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/column_renderer.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/column_renderer.ts index d05386c2504c5..9dd17ee9ffed9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/column_renderer.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/column_renderer.ts @@ -9,7 +9,7 @@ import type React from 'react'; import type { Filter } from '@kbn/es-query'; import { BrowserFields } from '../../../../../../../timelines/common/search_strategy'; -import { ColumnHeaderOptions, RowRenderer } from '../../../../../../common'; +import { ColumnHeaderOptions, RowRenderer } from '../../../../../../common/types'; import { Ecs } from '../../../../../../common/ecs'; import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx index 8de9692a116fa..cc4d3eeee2533 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx @@ -11,7 +11,7 @@ import React, { Fragment } from 'react'; import styled from 'styled-components'; import { ENRICHMENT_DESTINATION_PATH } from '../../../../../../../common/constants'; -import { RowRenderer } from '../../../../../../../common'; +import { RowRenderer } from '../../../../../../../common/types'; import { Fields } from '../../../../../../../common/search_strategy'; import { ID_FIELD_NAME } from '../../../../../../common/components/event_details/event_id'; import { RowRendererContainer } from '../row_renderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx index a3a6618e7d07a..334bfa76b84e1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { ColumnHeaderOptions } from '../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../common/types'; import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline'; import { DraggableWrapper, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.ts index 2d1be6ee7914a..cb6ea7760842b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RowRenderer } from '../../../../../../common'; +import { RowRenderer } from '../../../../../../common/types'; import { Ecs } from '../../../../../../common/ecs'; export const getRowRenderer = (ecs: Ecs, rowRenderers: RowRenderer[]): RowRenderer | null => diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.test.tsx index 1bbfc837ebd62..bf92417c8023a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.test.tsx @@ -10,7 +10,7 @@ import { waitFor } from '@testing-library/react'; import { HostName } from './host_name'; import { TestProviders } from '../../../../../common/mock'; -import { TimelineId, TimelineTabs } from '../../../../../../common'; +import { TimelineId, TimelineTabs } from '../../../../../../common/types'; import { StatefulEventContext } from '../../../../../../../timelines/public'; import { timelineActions } from '../../../../store/timeline'; import { activeTimeline } from '../../../../containers/active_timeline_context'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/index.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/index.ts index 11c501f9426f4..9a68a8b658149 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RowRenderer } from '../../../../../../common'; +import { RowRenderer } from '../../../../../../common/types'; import { auditdRowRenderers } from './auditd/generic_row_renderer'; import { ColumnRenderer } from './column_renderer'; import { emptyColumnRenderer } from './empty_column_renderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.tsx index f00eb332c564b..35e8647bc413d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.tsx @@ -9,7 +9,7 @@ import { head } from 'lodash/fp'; import React from 'react'; import type { Filter } from '@kbn/es-query'; -import { ColumnHeaderOptions } from '../../../../../../common'; +import { ColumnHeaderOptions } from '../../../../../../common/types'; import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline'; import { getEmptyTagValue } from '../../../../../common/components/empty_value'; import { ColumnRenderer } from './column_renderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.test.tsx index 8fdd6483a14f8..f71db57c1b42a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.test.tsx @@ -13,12 +13,9 @@ import { REASON_FIELD_NAME } from './constants'; import { reasonColumnRenderer } from './reason_column_renderer'; import { plainColumnRenderer } from './plain_column_renderer'; -import { - BrowserFields, - ColumnHeaderOptions, - RowRenderer, - RowRendererId, -} from '../../../../../../common'; +import { RowRendererId, ColumnHeaderOptions, RowRenderer } from '../../../../../../common/types'; +import { BrowserFields } from '../../../../../../common/search_strategy'; + import { render } from '@testing-library/react'; import { TestProviders } from '../../../../../../../timelines/public/mock'; import { useDraggableKeyboardWrapper as mockUseDraggableKeyboardWrapper } from '../../../../../../../timelines/public/components'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.tsx index b3f145beb886b..a5ba7360c966f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.tsx @@ -9,7 +9,8 @@ import { EuiSpacer, EuiPanel } from '@elastic/eui'; import { isEqual } from 'lodash/fp'; import React, { useMemo } from 'react'; -import { BrowserFields, ColumnHeaderOptions, RowRenderer } from '../../../../../../common'; +import { ColumnHeaderOptions, RowRenderer } from '../../../../../../common/types'; +import { BrowserFields } from '../../../../../../common/search_strategy'; import { Ecs } from '../../../../../../common/ecs'; import { eventRendererNames } from '../../../row_renderers_browser/catalog/constants'; import { ColumnRenderer } from './column_renderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx index 983ee00ef9b6b..ae2caa8ce8401 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx @@ -81,7 +81,7 @@ import { EndpointAlertCriteria, } from './generic_row_renderer'; import * as i18n from './translations'; -import { RowRenderer } from '../../../../../../../common'; +import { RowRenderer } from '../../../../../../../common/types'; jest.mock('../../../../../../common/lib/kibana'); diff --git a/x-pack/plugins/security_solution/public/ueba/components/host_rules_table/columns.tsx b/x-pack/plugins/security_solution/public/ueba/components/host_rules_table/columns.tsx index 456a0bbe898ea..1393f11189cca 100644 --- a/x-pack/plugins/security_solution/public/ueba/components/host_rules_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/ueba/components/host_rules_table/columns.tsx @@ -17,7 +17,7 @@ import { Provider } from '../../../timelines/components/timeline/data_providers/ import { HostRulesColumns } from './'; import * as i18n from './translations'; -import { HostRulesFields } from '../../../../common'; +import { HostRulesFields } from '../../../../common/search_strategy'; export const getHostRulesColumns = (): HostRulesColumns => [ { diff --git a/x-pack/plugins/security_solution/public/ueba/components/host_rules_table/index.tsx b/x-pack/plugins/security_solution/public/ueba/components/host_rules_table/index.tsx index 3d369a56a7bc0..540ad05d1da8b 100644 --- a/x-pack/plugins/security_solution/public/ueba/components/host_rules_table/index.tsx +++ b/x-pack/plugins/security_solution/public/ueba/components/host_rules_table/index.tsx @@ -18,13 +18,12 @@ import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { uebaActions, uebaModel, uebaSelectors } from '../../store'; import { getHostRulesColumns } from './columns'; import * as i18n from './translations'; -import { +import type { HostRulesEdges, HostRulesItem, HostRulesSortField, - HostRulesFields, -} from '../../../../common'; -import { Direction } from '../../../../common/search_strategy'; +} from '../../../../common/search_strategy'; +import { Direction, HostRulesFields } from '../../../../common/search_strategy'; import { HOST_RULES } from '../../pages/translations'; import { rowItems } from '../utils'; diff --git a/x-pack/plugins/security_solution/public/ueba/components/host_tactics_table/columns.tsx b/x-pack/plugins/security_solution/public/ueba/components/host_tactics_table/columns.tsx index 68e1195a9c7eb..0f635cc8cd198 100644 --- a/x-pack/plugins/security_solution/public/ueba/components/host_tactics_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/ueba/components/host_tactics_table/columns.tsx @@ -17,7 +17,7 @@ import { Provider } from '../../../timelines/components/timeline/data_providers/ import { HostTacticsColumns } from './'; import * as i18n from './translations'; -import { HostTacticsFields } from '../../../../common'; +import { HostTacticsFields } from '../../../../common/search_strategy'; export const getHostTacticsColumns = (): HostTacticsColumns => [ { diff --git a/x-pack/plugins/security_solution/public/ueba/components/host_tactics_table/index.tsx b/x-pack/plugins/security_solution/public/ueba/components/host_tactics_table/index.tsx index 28bd3d6ad43a0..8b5d9bbd65635 100644 --- a/x-pack/plugins/security_solution/public/ueba/components/host_tactics_table/index.tsx +++ b/x-pack/plugins/security_solution/public/ueba/components/host_tactics_table/index.tsx @@ -23,8 +23,8 @@ import { HostTacticsItem, HostTacticsSortField, HostTacticsFields, -} from '../../../../common'; -import { Direction } from '../../../../common/search_strategy'; + Direction, +} from '../../../../common/search_strategy'; import { HOST_TACTICS } from '../../pages/translations'; import { rowItems } from '../utils'; diff --git a/x-pack/plugins/security_solution/public/ueba/components/risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/ueba/components/risk_score_table/index.tsx index 9e9c6f81a43bb..516068d40eb0e 100644 --- a/x-pack/plugins/security_solution/public/ueba/components/risk_score_table/index.tsx +++ b/x-pack/plugins/security_solution/public/ueba/components/risk_score_table/index.tsx @@ -18,13 +18,12 @@ import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { uebaActions, uebaModel, uebaSelectors } from '../../store'; import { getRiskScoreColumns } from './columns'; import * as i18n from './translations'; -import { +import type { RiskScoreEdges, RiskScoreItem, RiskScoreSortField, - RiskScoreFields, -} from '../../../../common'; -import { Direction } from '../../../../common/search_strategy'; +} from '../../../../common/search_strategy'; +import { RiskScoreFields, Direction } from '../../../../common/search_strategy'; import { rowItems } from '../utils'; const tableType = uebaModel.UebaTableType.riskScore; diff --git a/x-pack/plugins/security_solution/public/ueba/containers/host_rules/index.tsx b/x-pack/plugins/security_solution/public/ueba/containers/host_rules/index.tsx index 7db1a77244bbe..7771d470b1556 100644 --- a/x-pack/plugins/security_solution/public/ueba/containers/host_rules/index.tsx +++ b/x-pack/plugins/security_solution/public/ueba/containers/host_rules/index.tsx @@ -23,7 +23,7 @@ import { UebaQueries, HostRulesRequestOptions, HostRulesStrategyResponse, -} from '../../../../common'; +} from '../../../../common/search_strategy'; import { ESTermQuery } from '../../../../common/typed_json'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/ueba/containers/host_tactics/index.tsx b/x-pack/plugins/security_solution/public/ueba/containers/host_tactics/index.tsx index 35dd2a0b08d4e..42f5707bae6e3 100644 --- a/x-pack/plugins/security_solution/public/ueba/containers/host_tactics/index.tsx +++ b/x-pack/plugins/security_solution/public/ueba/containers/host_tactics/index.tsx @@ -23,7 +23,7 @@ import { UebaQueries, HostTacticsRequestOptions, HostTacticsStrategyResponse, -} from '../../../../common'; +} from '../../../../common/search_strategy'; import { ESTermQuery } from '../../../../common/typed_json'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/ueba/containers/risk_score/index.tsx b/x-pack/plugins/security_solution/public/ueba/containers/risk_score/index.tsx index f2f353ffc0cff..cc27c614f5897 100644 --- a/x-pack/plugins/security_solution/public/ueba/containers/risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/ueba/containers/risk_score/index.tsx @@ -23,7 +23,7 @@ import { UebaQueries, RiskScoreRequestOptions, RiskScoreStrategyResponse, -} from '../../../../common'; +} from '../../../../common/search_strategy'; import { ESTermQuery } from '../../../../common/typed_json'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/ueba/containers/user_rules/index.tsx b/x-pack/plugins/security_solution/public/ueba/containers/user_rules/index.tsx index 3c4e45bd3a1e5..21376d82eb17a 100644 --- a/x-pack/plugins/security_solution/public/ueba/containers/user_rules/index.tsx +++ b/x-pack/plugins/security_solution/public/ueba/containers/user_rules/index.tsx @@ -22,7 +22,7 @@ import { UserRulesRequestOptions, UserRulesStrategyResponse, UserRulesStrategyUserResponse, -} from '../../../../common'; +} from '../../../../common/search_strategy'; import { ESTermQuery } from '../../../../common/typed_json'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/ueba/pages/navigation/user_rules_query_tab_body.tsx b/x-pack/plugins/security_solution/public/ueba/pages/navigation/user_rules_query_tab_body.tsx index f7542b7b4b8a6..684b5cc12eaf9 100644 --- a/x-pack/plugins/security_solution/public/ueba/pages/navigation/user_rules_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/ueba/pages/navigation/user_rules_query_tab_body.tsx @@ -13,7 +13,7 @@ import { HostQueryProps } from './types'; import { manageQuery } from '../../../common/components/page/manage_query'; import { HostRulesTable } from '../../components/host_rules_table'; import { uebaModel } from '../../store'; -import { UserRulesFields } from '../../../../common'; +import { UserRulesFields } from '../../../../common/search_strategy'; const UserRulesTableManage = manageQuery(HostRulesTable); diff --git a/x-pack/plugins/security_solution/public/ueba/pages/ueba.tsx b/x-pack/plugins/security_solution/public/ueba/pages/ueba.tsx index 0c837e60cd2dc..c553052b35319 100644 --- a/x-pack/plugins/security_solution/public/ueba/pages/ueba.tsx +++ b/x-pack/plugins/security_solution/public/ueba/pages/ueba.tsx @@ -21,7 +21,7 @@ import { SiemSearchBar } from '../../common/components/search_bar'; import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; import { useGlobalFullScreen } from '../../common/containers/use_full_screen'; import { useGlobalTime } from '../../common/containers/use_global_time'; -import { TimelineId } from '../../../common'; +import { TimelineId } from '../../../common/types'; import { LastEventIndexKey } from '../../../common/search_strategy'; import { useKibana } from '../../common/lib/kibana'; import { convertToBuildEsQuery } from '../../common/lib/keury'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/get_strategy.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/get_strategy.ts index 3c4b1cd0ef373..15164842fe0d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/get_strategy.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/get_strategy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { assertUnreachable } from '../../../../../../common'; +import { assertUnreachable } from '../../../../../../common/utility_types'; import type { ConfigType } from '../../../../../config'; import { MergeStrategyFunction } from '../types'; import { mergeAllFieldsWithSource } from './merge_all_fields_with_source'; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts index 1d4d9b1e0f2ea..18dcdfe103b1f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts @@ -9,7 +9,7 @@ import * as module from './helpers'; import { savePinnedEvents } from '../../../saved_object/pinned_events'; import { getNote } from '../../../saved_object/notes'; import { FrameworkRequest } from '../../../../framework'; -import { SavedTimeline } from '../../../../../../common'; +import { SavedTimeline } from '../../../../../../common/types'; import { mockTemplate, mockTimeline } from '../../../__mocks__/create_timelines'; import { buildFrameworkRequest } from '../../../utils/common'; import { SecurityPluginSetup } from '../../../../../../../security/server'; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/index.ts index a609a8b356f10..94504afcc6c02 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/index.ts @@ -10,7 +10,7 @@ import { HostsRiskScoreRequestOptions, HostsQueries, HostsRiskScoreStrategyResponse, -} from '../../../../../../common'; +} from '../../../../../../common/search_strategy'; import type { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; import { inspectStringifyObject } from '../../../../../utils/build_query'; import { buildHostsRiskScoreQuery } from './query.hosts_risk.dsl'; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts index 5bbc9b7726002..05bb496a7444e 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { HostsRiskScoreRequestOptions } from '../../../../../../common'; +import { HostsRiskScoreRequestOptions } from '../../../../../../common/search_strategy'; export const buildHostsRiskScoreQuery = ({ timerange, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts index b97b8ed4f6549..59985a927aa8f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MatrixHistogramTypeToAggName } from '../../../../../../common'; +import { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy'; import { buildAlertsHistogramQuery } from './query.alerts_histogram.dsl'; export const alertsMatrixHistogramConfig = { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts index ec307173ec20c..c7e67566acfc2 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MatrixHistogramTypeToAggName } from '../../../../../../common'; +import { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy'; import { buildAnomaliesHistogramQuery } from './query.anomalies_histogram.dsl'; export const anomaliesMatrixHistogramConfig = { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts index 17f7d78167232..bde058942f2bb 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MatrixHistogramTypeToAggName } from '../../../../../../common'; +import { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy'; import { getEntitiesParser } from '../helpers'; import { buildAuthenticationsHistogramQuery } from './query.authentications_histogram.dsl'; import { buildAuthenticationsHistogramQueryEntities } from './query.authentications_histogram_entities.dsl'; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts index 643b3f657ef0c..f52a760db842e 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts @@ -7,7 +7,7 @@ import { buildDnsHistogramQuery } from './query.dns_histogram.dsl'; import { getDnsParsedData } from './helpers'; -import { MatrixHistogramTypeToAggName } from '../../../../../../common'; +import { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy'; export const dnsMatrixHistogramConfig = { buildDsl: buildDnsHistogramQuery, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts index a280950c37cd6..954cd6509e2b0 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MatrixHistogramTypeToAggName } from '../../../../../../common'; +import { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy'; import { buildEventsHistogramQuery } from './query.events_histogram.dsl'; export const eventsMatrixHistogramConfig = { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/__mocks__/index.ts index 2ff4831616ab9..12d46c4be2282 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/__mocks__/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MatrixHistogramType } from '../../../../../../../common'; +import { MatrixHistogramType } from '../../../../../../../common/search_strategy'; export const mockOptions = { defaultIndex: ['.siem-preview-signals-default'], diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/index.ts index 51e45035f420d..0ca14c5489c56 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MatrixHistogramTypeToAggName } from '../../../../../../common'; +import { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy'; import { buildPreviewHistogramQuery } from './query.preview_histogram.dsl'; export const previewMatrixHistogramConfig = { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/helpers.ts index f9c94eea3ff29..8b8f6ec204e98 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/helpers.ts @@ -6,7 +6,11 @@ */ import { getOr } from 'lodash/fp'; -import { HostRulesHit, HostRulesEdges, HostRulesFields } from '../../../../../../common'; +import { + HostRulesHit, + HostRulesEdges, + HostRulesFields, +} from '../../../../../../common/search_strategy'; export const formatHostRulesData = (buckets: HostRulesHit[]): HostRulesEdges[] => buckets.map((bucket) => ({ diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/index.ts index 78a1cfe20d212..fb1416070ddc0 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/index.ts @@ -12,7 +12,7 @@ import { HostRulesRequestOptions, HostRulesStrategyResponse, UebaQueries, -} from '../../../../../../common'; +} from '../../../../../../common/search_strategy'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; import { buildHostRulesQuery } from './query.host_rules.dsl'; import type { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/helpers.ts index b20cf4582c824..6e8a9e0f43b2f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/helpers.ts @@ -11,7 +11,7 @@ import { HostTacticsEdges, HostTacticsFields, HostTechniqueHit, -} from '../../../../../../common'; +} from '../../../../../../common/search_strategy'; export const formatHostTacticsData = (buckets: HostTacticsHit[]): HostTacticsEdges[] => buckets.reduce((acc: HostTacticsEdges[], bucket) => { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/index.ts index c90ad5a311790..86d6bc6f75b6f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/index.ts @@ -8,11 +8,11 @@ import { getOr } from 'lodash/fp'; import { SecuritySolutionFactory } from '../../types'; import { + UebaQueries, HostTacticsEdges, HostTacticsRequestOptions, HostTacticsStrategyResponse, - UebaQueries, -} from '../../../../../../common'; +} from '../../../../../../common/search_strategy'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; import { buildHostTacticsQuery } from './query.host_tactics.dsl'; import type { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/helpers.ts index ace2faf819877..4ef866f2fd705 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/helpers.ts @@ -6,7 +6,7 @@ */ import { getOr } from 'lodash/fp'; -import { RiskScoreHit, RiskScoreEdges } from '../../../../../../common'; +import { RiskScoreHit, RiskScoreEdges } from '../../../../../../common/search_strategy'; export const formatRiskScoreData = (buckets: RiskScoreHit[]): RiskScoreEdges[] => buckets.map((bucket) => ({ diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/index.ts index 8e65b53c3e68d..cc55d83828426 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/index.ts @@ -8,11 +8,11 @@ import { getOr } from 'lodash/fp'; import { SecuritySolutionFactory } from '../../types'; import { + UebaQueries, RiskScoreEdges, RiskScoreRequestOptions, RiskScoreStrategyResponse, - UebaQueries, -} from '../../../../../../common'; +} from '../../../../../../common/search_strategy'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; import { buildRiskScoreQuery } from './query.risk_score.dsl'; import type { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/helpers.ts index c0f38af37c1f5..02b284e6bd200 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/helpers.ts @@ -6,7 +6,11 @@ */ import { getOr } from 'lodash/fp'; -import { UserRulesHit, UserRulesFields, UserRulesByUser } from '../../../../../../common'; +import { + UserRulesHit, + UserRulesFields, + UserRulesByUser, +} from '../../../../../../common/search_strategy'; import { formatHostRulesData } from '../host_rules/helpers'; export const formatUserRulesData = (buckets: UserRulesHit[]): UserRulesByUser[] => diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/index.ts index 8a5099618d109..872aa546d6553 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/index.ts @@ -14,7 +14,7 @@ import { UserRulesRequestOptions, UserRulesStrategyResponse, UsersRulesHit, -} from '../../../../../../common'; +} from '../../../../../../common/search_strategy'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; import { buildUserRulesQuery } from './query.user_rules.dsl'; import type { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; diff --git a/x-pack/test/common/services/security_solution/roles_users_utils.ts b/x-pack/test/common/services/security_solution/roles_users_utils.ts index dd184c6a94b46..681e710aa896c 100644 --- a/x-pack/test/common/services/security_solution/roles_users_utils.ts +++ b/x-pack/test/common/services/security_solution/roles_users_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { assertUnreachable } from '../../../../plugins/security_solution/common'; +import { assertUnreachable } from '../../../../plugins/security_solution/common/utility_types'; import { FtrProviderContext } from '../../ftr_provider_context'; import { t1AnalystUser, From 2ef888f3e3636f06a126804619344b44846ac96e Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Thu, 2 Dec 2021 14:19:09 -0500 Subject: [PATCH 15/65] [Security Solution][Endpoint] Remove (don't register) Endpoint Trusted Apps specific APIs (#120134) * change trusted apps data loader script to use exceptions API * Comment out registration of Trusted Apps API routes --- .../scripts/endpoint/trusted_apps/index.ts | 49 ++++- .../endpoint/routes/trusted_apps/index.ts | 173 +++++++++--------- 2 files changed, 127 insertions(+), 95 deletions(-) diff --git a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts index 97695ec60062c..ad0f9cb5dae49 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts @@ -11,7 +11,14 @@ import { KbnClient } from '@kbn/test'; import pMap from 'p-map'; import { basename } from 'path'; import { AxiosResponse } from 'axios'; -import { TRUSTED_APPS_CREATE_API, TRUSTED_APPS_LIST_API } from '../../../common/endpoint/constants'; +import { + ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION, + ENDPOINT_TRUSTED_APPS_LIST_ID, + ENDPOINT_TRUSTED_APPS_LIST_NAME, + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { CreateExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; import { TrustedApp } from '../../../common/endpoint/types'; import { TrustedAppGenerator } from '../../../common/endpoint/data_generators/trusted_app_generator'; import { indexFleetEndpointPolicy } from '../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; @@ -21,6 +28,7 @@ import { PACKAGE_POLICY_API_ROUTES, PACKAGE_POLICY_SAVED_OBJECT_TYPE, } from '../../../../fleet/common'; +import { newTrustedAppToCreateExceptionListItem } from '../../../public/management/pages/trusted_apps/service/mappers'; const defaultLogger = new ToolingLog({ level: 'info', writeTo: process.stdout }); const separator = '----------------------------------------'; @@ -76,17 +84,14 @@ export const run: (options?: RunOptions) => Promise = async ({ url: kibana, }); - // touch the Trusted Apps List so it can be created - // and // setup fleet with endpoint integrations + // and + // and ensure the trusted apps list is created logger.info('setting up Fleet with endpoint and creating trusted apps list'); const [installedEndpointPackage] = await Promise.all([ setupFleetForEndpoint(kbnClient).then((response) => response.endpointPackage), - kbnClient.request({ - method: 'GET', - path: TRUSTED_APPS_LIST_API, - }), + ensureCreateEndpointTrustedAppsList(kbnClient), ]); // Setup a list of real endpoint policies and return a method to randomly select one @@ -125,8 +130,8 @@ export const run: (options?: RunOptions) => Promise = async ({ return kbnClient .request({ method: 'POST', - path: TRUSTED_APPS_CREATE_API, - body, + path: EXCEPTION_LIST_ITEM_URL, + body: newTrustedAppToCreateExceptionListItem(body), }) .then(({ data }) => { logger.write(data.id); @@ -176,3 +181,29 @@ const fetchEndpointPolicies = ( }, }); }; + +const ensureCreateEndpointTrustedAppsList = async (kbn: KbnClient) => { + const newListDefinition: CreateExceptionListSchema = { + description: ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION, + list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, + meta: undefined, + name: ENDPOINT_TRUSTED_APPS_LIST_NAME, + os_types: [], + tags: [], + type: 'endpoint', + namespace_type: 'agnostic', + }; + + await kbn + .request({ + method: 'POST', + path: EXCEPTION_LIST_URL, + body: newListDefinition, + }) + .catch((e) => { + // Ignore if list was already created + if (e.response.status !== 409) { + throw e; + } + }); +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/index.ts index 1d5df9c6e88b8..d00e1fc555b17 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/index.ts @@ -5,95 +5,96 @@ * 2.0. */ -import { - DeleteTrustedAppsRequestSchema, - GetOneTrustedAppRequestSchema, - GetTrustedAppsRequestSchema, - PostTrustedAppCreateRequestSchema, - PutTrustedAppUpdateRequestSchema, - GetTrustedAppsSummaryRequestSchema, -} from '../../../../common/endpoint/schema/trusted_apps'; -import { - TRUSTED_APPS_CREATE_API, - TRUSTED_APPS_DELETE_API, - TRUSTED_APPS_GET_API, - TRUSTED_APPS_LIST_API, - TRUSTED_APPS_UPDATE_API, - TRUSTED_APPS_SUMMARY_API, -} from '../../../../common/endpoint/constants'; - -import { - getTrustedAppsCreateRouteHandler, - getTrustedAppsDeleteRouteHandler, - getTrustedAppsGetOneHandler, - getTrustedAppsListRouteHandler, - getTrustedAppsSummaryRouteHandler, - getTrustedAppsUpdateRouteHandler, -} from './handlers'; +// import { +// DeleteTrustedAppsRequestSchema, +// GetOneTrustedAppRequestSchema, +// GetTrustedAppsRequestSchema, +// PostTrustedAppCreateRequestSchema, +// PutTrustedAppUpdateRequestSchema, +// GetTrustedAppsSummaryRequestSchema, +// } from '../../../../common/endpoint/schema/trusted_apps'; +// import { +// TRUSTED_APPS_CREATE_API, +// TRUSTED_APPS_DELETE_API, +// TRUSTED_APPS_GET_API, +// TRUSTED_APPS_LIST_API, +// TRUSTED_APPS_UPDATE_API, +// TRUSTED_APPS_SUMMARY_API, +// } from '../../../../common/endpoint/constants'; +// +// import { +// getTrustedAppsCreateRouteHandler, +// getTrustedAppsDeleteRouteHandler, +// getTrustedAppsGetOneHandler, +// getTrustedAppsListRouteHandler, +// getTrustedAppsSummaryRouteHandler, +// getTrustedAppsUpdateRouteHandler, +// } from './handlers'; import { SecuritySolutionPluginRouter } from '../../../types'; import { EndpointAppContext } from '../../types'; export const registerTrustedAppsRoutes = ( - router: SecuritySolutionPluginRouter, - endpointAppContext: EndpointAppContext + _router: SecuritySolutionPluginRouter, + _endpointAppContext: EndpointAppContext ) => { - // DELETE one - router.delete( - { - path: TRUSTED_APPS_DELETE_API, - validate: DeleteTrustedAppsRequestSchema, - options: { authRequired: true }, - }, - getTrustedAppsDeleteRouteHandler(endpointAppContext) - ); - - // GET one - router.get( - { - path: TRUSTED_APPS_GET_API, - validate: GetOneTrustedAppRequestSchema, - options: { authRequired: true }, - }, - getTrustedAppsGetOneHandler(endpointAppContext) - ); - - // GET list - router.get( - { - path: TRUSTED_APPS_LIST_API, - validate: GetTrustedAppsRequestSchema, - options: { authRequired: true }, - }, - getTrustedAppsListRouteHandler(endpointAppContext) - ); - - // CREATE - router.post( - { - path: TRUSTED_APPS_CREATE_API, - validate: PostTrustedAppCreateRequestSchema, - options: { authRequired: true }, - }, - getTrustedAppsCreateRouteHandler(endpointAppContext) - ); - - // PUT - router.put( - { - path: TRUSTED_APPS_UPDATE_API, - validate: PutTrustedAppUpdateRequestSchema, - options: { authRequired: true }, - }, - getTrustedAppsUpdateRouteHandler(endpointAppContext) - ); - - // SUMMARY - router.get( - { - path: TRUSTED_APPS_SUMMARY_API, - validate: GetTrustedAppsSummaryRequestSchema, - options: { authRequired: true }, - }, - getTrustedAppsSummaryRouteHandler(endpointAppContext) - ); + // FIXME: DELETE all trusted apps api related modules (#2148) + // // DELETE one + // router.delete( + // { + // path: TRUSTED_APPS_DELETE_API, + // validate: DeleteTrustedAppsRequestSchema, + // options: { authRequired: true }, + // }, + // getTrustedAppsDeleteRouteHandler(endpointAppContext) + // ); + // + // // GET one + // router.get( + // { + // path: TRUSTED_APPS_GET_API, + // validate: GetOneTrustedAppRequestSchema, + // options: { authRequired: true }, + // }, + // getTrustedAppsGetOneHandler(endpointAppContext) + // ); + // + // // GET list + // router.get( + // { + // path: TRUSTED_APPS_LIST_API, + // validate: GetTrustedAppsRequestSchema, + // options: { authRequired: true }, + // }, + // getTrustedAppsListRouteHandler(endpointAppContext) + // ); + // + // // CREATE + // router.post( + // { + // path: TRUSTED_APPS_CREATE_API, + // validate: PostTrustedAppCreateRequestSchema, + // options: { authRequired: true }, + // }, + // getTrustedAppsCreateRouteHandler(endpointAppContext) + // ); + // + // // PUT + // router.put( + // { + // path: TRUSTED_APPS_UPDATE_API, + // validate: PutTrustedAppUpdateRequestSchema, + // options: { authRequired: true }, + // }, + // getTrustedAppsUpdateRouteHandler(endpointAppContext) + // ); + // + // // SUMMARY + // router.get( + // { + // path: TRUSTED_APPS_SUMMARY_API, + // validate: GetTrustedAppsSummaryRequestSchema, + // options: { authRequired: true }, + // }, + // getTrustedAppsSummaryRouteHandler(endpointAppContext) + // ); }; From 2d13b7be3c41778ace6083b1bcf3a5df03da4ed6 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 2 Dec 2021 20:39:03 +0100 Subject: [PATCH 16/65] [Graph] Add KibanaThemeProvider (#119802) * [Graph] Add ThemeProvider * Wrap top_nav_menu in kibana theme Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../graph/public/{application.ts => application.tsx} | 7 +++++-- .../workspace_layout/workspace_top_nav_menu.tsx | 11 +++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) rename x-pack/plugins/graph/public/{application.ts => application.tsx} (93%) diff --git a/x-pack/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.tsx similarity index 93% rename from x-pack/plugins/graph/public/application.ts rename to x-pack/plugins/graph/public/application.tsx index 5a7f931538bf6..235c9aa843797 100644 --- a/x-pack/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.tsx @@ -20,6 +20,7 @@ import { ScopedHistory, } from 'kibana/public'; import ReactDOM from 'react-dom'; +import React from 'react'; import { DataPlugin, IndexPatternsContract } from '../../../../src/plugins/data/public'; import { LicensingPluginStart } from '../../licensing/public'; import { checkLicense } from '../common/check_license'; @@ -32,6 +33,7 @@ import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public' import { GraphSavePolicy } from './types'; import { graphRouter } from './router'; import { SpacesApi } from '../../spaces/public'; +import { KibanaThemeProvider } from '../../../../src/plugins/kibana_react/public'; /** * These are dependencies of the Graph app besides the base dependencies @@ -69,7 +71,8 @@ export interface GraphDependencies { export type GraphServices = Omit; export const renderApp = ({ history, element, ...deps }: GraphDependencies) => { - const { chrome, capabilities } = deps; + const { chrome, capabilities, core } = deps; + const { theme$ } = core.theme; if (!capabilities.graph.save) { chrome.setBadge({ @@ -107,7 +110,7 @@ export const renderApp = ({ history, element, ...deps }: GraphDependencies) => { window.dispatchEvent(new HashChangeEvent('hashchange')); }); - const app = graphRouter(deps); + const app = {graphRouter(deps)}; ReactDOM.render(app, element); element.setAttribute('class', 'gphAppWrapper'); diff --git a/x-pack/plugins/graph/public/components/workspace_layout/workspace_top_nav_menu.tsx b/x-pack/plugins/graph/public/components/workspace_layout/workspace_top_nav_menu.tsx index dc7365672ffa2..f3bebfa68ca4a 100644 --- a/x-pack/plugins/graph/public/components/workspace_layout/workspace_top_nav_menu.tsx +++ b/x-pack/plugins/graph/public/components/workspace_layout/workspace_top_nav_menu.tsx @@ -11,7 +11,7 @@ import { Provider, useStore } from 'react-redux'; import { AppMountParameters, Capabilities, CoreStart } from 'kibana/public'; import { useHistory, useLocation } from 'react-router-dom'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../../src/plugins/navigation/public'; -import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; +import { toMountPoint, wrapWithTheme } from '../../../../../../src/plugins/kibana_react/public'; import { datasourceSelector, hasFieldsSelector } from '../../state_management'; import { GraphSavePolicy, GraphWorkspaceSavedObject, Workspace } from '../../types'; import { AsObservable, Settings, SettingsWorkspaceProps } from '../settings'; @@ -145,9 +145,12 @@ export const WorkspaceTopNavMenu = (props: WorkspaceTopNavMenuProps) => { props.coreStart.overlays.openFlyout( toMountPoint( - - - + wrapWithTheme( + + + , + props.coreStart.theme.theme$ + ) ), { size: 'm', From a88ac0c2e6be4913a0c39c4fb01a4480f1f8fded Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 2 Dec 2021 19:56:40 +0000 Subject: [PATCH 17/65] chore(NA): splits types from code on @kbn/cli-dev-mode (#120248) --- package.json | 1 + packages/BUILD.bazel | 1 + packages/kbn-cli-dev-mode/BUILD.bazel | 26 ++++++++++++++++++++++---- packages/kbn-cli-dev-mode/package.json | 1 - yarn.lock | 4 ++++ 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index c8da0bf051390..75da2784a391b 100644 --- a/package.json +++ b/package.json @@ -560,6 +560,7 @@ "@types/kbn__analytics": "link:bazel-bin/packages/kbn-analytics/npm_module_types", "@types/kbn__apm-config-loader": "link:bazel-bin/packages/kbn-apm-config-loader/npm_module_types", "@types/kbn__apm-utils": "link:bazel-bin/packages/kbn-apm-utils/npm_module_types", + "@types/kbn__cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode/npm_module_types", "@types/kbn__i18n": "link:bazel-bin/packages/kbn-i18n/npm_module_types", "@types/kbn__i18n-react": "link:bazel-bin/packages/kbn-i18n-react/npm_module_types", "@types/license-checker": "15.0.0", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index f10044a68def9..9e124cf63a391 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -82,6 +82,7 @@ filegroup( "//packages/kbn-analytics:build_types", "//packages/kbn-apm-config-loader:build_types", "//packages/kbn-apm-utils:build_types", + "//packages/kbn-cli-dev-mode:build_types", "//packages/kbn-i18n:build_types", "//packages/kbn-i18n-react:build_types", ], diff --git a/packages/kbn-cli-dev-mode/BUILD.bazel b/packages/kbn-cli-dev-mode/BUILD.bazel index e66a621781234..686866ce7bc88 100644 --- a/packages/kbn-cli-dev-mode/BUILD.bazel +++ b/packages/kbn-cli-dev-mode/BUILD.bazel @@ -1,9 +1,10 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") -load("//src/dev/bazel:index.bzl", "jsts_transpiler") +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") PKG_BASE_NAME = "kbn-cli-dev-mode" PKG_REQUIRE_NAME = "@kbn/cli-dev-mode" +TYPES_PKG_REQUIRE_NAME = "@types/kbn__cli-dev-mode" SOURCE_FILES = glob( [ @@ -103,7 +104,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + deps = RUNTIME_DEPS + [":target_node"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) @@ -122,3 +123,20 @@ filegroup( ], visibility = ["//visibility:public"], ) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = TYPES_PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-cli-dev-mode/package.json b/packages/kbn-cli-dev-mode/package.json index ac5ec227e92c5..80076a9851087 100644 --- a/packages/kbn-cli-dev-mode/package.json +++ b/packages/kbn-cli-dev-mode/package.json @@ -1,7 +1,6 @@ { "name": "@kbn/cli-dev-mode", "main": "./target_node/index.js", - "types": "./target_types/index.d.ts", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", "private": true, diff --git a/yarn.lock b/yarn.lock index 85dba79953939..7a1afe15ddd33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5825,6 +5825,10 @@ version "0.0.0" uid "" +"@types/kbn__cli-dev-mode@link:bazel-bin/packages/kbn-cli-dev-mode/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__i18n-react@link:bazel-bin/packages/kbn-i18n-react/npm_module_types": version "0.0.0" uid "" From 23652de60551279ea23c5ab41071dea65d893951 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 2 Dec 2021 22:13:53 +0200 Subject: [PATCH 18/65] [Visualize] Fixes wrong display of split warning callout (#120199) --- .../visualize_editor_common.test.tsx | 83 +++++++++++++++++++ .../components/visualize_editor_common.tsx | 4 +- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/plugins/visualize/public/application/components/visualize_editor_common.test.tsx b/src/plugins/visualize/public/application/components/visualize_editor_common.test.tsx index dbe658b8b2d3c..d0c7b56638bc2 100644 --- a/src/plugins/visualize/public/application/components/visualize_editor_common.test.tsx +++ b/src/plugins/visualize/public/application/components/visualize_editor_common.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { shallowWithIntl, mountWithIntl } from '@kbn/test/jest'; import { VisualizeEditorCommon } from './visualize_editor_common'; import { VisualizeEditorVisInstance } from '../types'; +import { SplitChartWarning } from './split_chart_warning'; const mockGetLegacyUrlConflict = jest.fn(); const mockRedirectLegacyUrl = jest.fn(() => Promise.resolve()); @@ -115,4 +116,86 @@ describe('VisualizeEditorCommon', () => { 'TSVB visualization' ); }); + + it('should display a warning callout for new heatmap implementation with split aggs', async () => { + const wrapper = shallowWithIntl( + {}} + hasUnappliedChanges={false} + isEmbeddableRendered={false} + onAppLeave={() => {}} + visEditorRef={React.createRef()} + visInstance={ + { + savedVis: { + id: 'test', + sharingSavedObjectProps: { + outcome: 'conflict', + aliasTargetId: 'alias_id', + }, + }, + vis: { + type: { + title: 'Heatmap', + name: 'heatmap', + }, + data: { + aggs: { + aggs: [ + { + schema: 'split', + }, + ], + }, + }, + }, + } as unknown as VisualizeEditorVisInstance + } + /> + ); + expect(wrapper.find(SplitChartWarning).length).toBe(1); + }); + + it('should not display a warning callout for XY charts with split aggs', async () => { + const wrapper = shallowWithIntl( + {}} + hasUnappliedChanges={false} + isEmbeddableRendered={false} + onAppLeave={() => {}} + visEditorRef={React.createRef()} + visInstance={ + { + savedVis: { + id: 'test', + sharingSavedObjectProps: { + outcome: 'conflict', + aliasTargetId: 'alias_id', + }, + }, + vis: { + type: { + title: 'XY', + name: 'line', + }, + data: { + aggs: { + aggs: [ + { + schema: 'split', + }, + ], + }, + }, + }, + } as unknown as VisualizeEditorVisInstance + } + /> + ); + expect(wrapper.find(SplitChartWarning).length).toBe(0); + }); }); diff --git a/src/plugins/visualize/public/application/components/visualize_editor_common.tsx b/src/plugins/visualize/public/application/components/visualize_editor_common.tsx index b764c9de48346..706fe55d0754e 100644 --- a/src/plugins/visualize/public/application/components/visualize_editor_common.tsx +++ b/src/plugins/visualize/public/application/components/visualize_editor_common.tsx @@ -134,7 +134,9 @@ export const VisualizeEditorCommon = ({ /> )} {visInstance?.vis?.type?.stage === 'experimental' && } - {!hasHeatmapLegacyhartsEnabled && isSplitChart && } + {!hasHeatmapLegacyhartsEnabled && + isSplitChart && + visInstance?.vis.type.name === 'heatmap' && } {visInstance?.vis?.type?.getInfoMessage?.(visInstance.vis)} {getLegacyUrlConflictCallout()} {visInstance && ( From f01106c100c3af70fc6fd32938a39e69ed27dd05 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:17:51 -0800 Subject: [PATCH 19/65] [DOCS] Updates security section of intro doc (#120036) * Updates security section of intro doc * Update docs/user/introduction.asciidoc Co-authored-by: Adam Locke Co-authored-by: Adam Locke --- docs/user/introduction.asciidoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/user/introduction.asciidoc b/docs/user/introduction.asciidoc index 8972f92831717..89a21b0424ed0 100644 --- a/docs/user/introduction.asciidoc +++ b/docs/user/introduction.asciidoc @@ -169,9 +169,8 @@ them in bulk operations. === Secure {kib} {kib} offers a range of security features for you to control who has access to what. -The security features are automatically turned on when -{ref}/security-minimal-setup.html[security is enabled in -{es}]. For a description of all available configuration options, +{ref}/configuring-stack-security.html[Security is enabled automatically] when you enroll {kib} with a secured {es} cluster. +For a description of all available configuration options, refer to <>. [float] From ea37dbb6c3c8620853267ff09c1716657f2c4eff Mon Sep 17 00:00:00 2001 From: nastasha-solomon <79124755+nastasha-solomon@users.noreply.github.com> Date: Thu, 2 Dec 2021 17:20:05 -0500 Subject: [PATCH 20/65] [Security Solution] Doc pre-reqs for using the SN ITSM, SecOps, and ITOM connectors (#117122) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../action-types/servicenow-itom.asciidoc | 32 ++++--- .../action-types/servicenow-sir.asciidoc | 87 ++++++++++++++--- .../action-types/servicenow.asciidoc | 88 +++++++++++++++--- .../images/servicenow-params-test.png | Bin 184611 -> 233010 bytes .../servicenow-sir-update-connector.png | Bin 0 -> 250246 bytes 5 files changed, 167 insertions(+), 40 deletions(-) create mode 100644 docs/management/connectors/images/servicenow-sir-update-connector.png diff --git a/docs/management/connectors/action-types/servicenow-itom.asciidoc b/docs/management/connectors/action-types/servicenow-itom.asciidoc index 017290dde9b15..af231c327f955 100644 --- a/docs/management/connectors/action-types/servicenow-itom.asciidoc +++ b/docs/management/connectors/action-types/servicenow-itom.asciidoc @@ -1,25 +1,31 @@ [role="xpack"] [[servicenow-itom-action-type]] -=== ServiceNow connector and action +=== ServiceNow ITOM connector and action ++++ ServiceNow ITOM ++++ -The ServiceNow ITOM connector uses the https://docs.servicenow.com/bundle/rome-it-operations-management/page/product/event-management/task/send-events-via-web-service.html[Event API] to create ServiceNow events. +The {sn} ITOM connector uses the https://docs.servicenow.com/bundle/rome-it-operations-management/page/product/event-management/task/send-events-via-web-service.html[Event API] to create {sn} events. + +[float] +[[servicenow-itom-connector-prerequisites]] +==== Prerequisites +Create an integration user in {sn} and assign it the following roles. + +* `personalize_choices`: Allows the user to retrieve Choice element options, such as Severity. +* `evt_mgmt_integration`: Enables integration with external event sources by allowing the user to create events. [float] [[servicenow-itom-connector-configuration]] ==== Connector configuration -ServiceNow ITOM connectors have the following configuration properties. +{sn} ITOM connectors have the following configuration properties. -Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** UI connector listing, and in the connector list when configuring an action. -URL:: ServiceNow instance URL. +Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** connector listing, and in the connector list when configuring an action. +URL:: {sn} instance URL. Username:: Username for HTTP Basic authentication. Password:: Password for HTTP Basic authentication. -The ServiceNow user requires at minimum read, create, and update access to the Event table and read access to the https://docs.servicenow.com/bundle/paris-platform-administration/page/administer/localization/reference/r_ChoicesTable.html[sys_choice]. If you don't provide access to sys_choice, then the choices will not render. - [float] [[servicenow-itom-connector-networking-configuration]] ==== Connector networking configuration @@ -55,12 +61,12 @@ Secrets defines sensitive information for the connector type. [[define-servicenow-itom-ui]] ==== Define connector in Stack Management -Define ServiceNow ITOM connector properties. +Define {sn} ITOM connector properties. [role="screenshot"] image::management/connectors/images/servicenow-itom-connector.png[ServiceNow ITOM connector] -Test ServiceNow ITOM action parameters. +Test {sn} ITOM action parameters. [role="screenshot"] image::management/connectors/images/servicenow-itom-params-test.png[ServiceNow ITOM params test] @@ -69,7 +75,7 @@ image::management/connectors/images/servicenow-itom-params-test.png[ServiceNow I [[servicenow-itom-action-configuration]] ==== Action configuration -ServiceNow ITOM actions have the following configuration properties. +{sn} ITOM actions have the following configuration properties. Source:: The name of the event source type. Node:: The Host that the event was triggered for. @@ -77,7 +83,7 @@ Type:: The type of event. Resource:: The name of the resource. Metric name:: Name of the metric. Source instance (event_class):: Specific instance of the source. -Message key:: All actions sharing this key will be associated with the same ServiceNow alert. Default value: `:`. +Message key:: All actions sharing this key will be associated with the same {sn} alert. Default value: `:`. Severity:: The severity of the event. Description:: The details about the event. @@ -85,6 +91,6 @@ Refer to https://docs.servicenow.com/bundle/rome-it-operations-management/page/p [float] [[configuring-servicenow-itom]] -==== Configure ServiceNow ITOM +==== Configure {sn} ITOM -ServiceNow offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. +{sn} offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. diff --git a/docs/management/connectors/action-types/servicenow-sir.asciidoc b/docs/management/connectors/action-types/servicenow-sir.asciidoc index 8847a99fe3af0..70500b26c16e6 100644 --- a/docs/management/connectors/action-types/servicenow-sir.asciidoc +++ b/docs/management/connectors/action-types/servicenow-sir.asciidoc @@ -1,25 +1,83 @@ [role="xpack"] [[servicenow-sir-action-type]] -=== ServiceNow connector and action +=== ServiceNow SecOps connector and action ++++ ServiceNow SecOps ++++ -The ServiceNow SecOps connector uses the https://docs.servicenow.com/bundle/orlando-application-development/page/integrate/inbound-rest/concept/c_TableAPI.html[V2 Table API] to create ServiceNow security incidents. +The {sn} SecOps connector uses the https://docs.servicenow.com/bundle/orlando-application-development/page/integrate/inbound-rest/concept/c_TableAPI.html[V2 Table API] to create {sn} security incidents. + +[float] +[[servicenow-sir-connector-prerequisites]] +==== Prerequisites +After upgrading from {stack} version 7.15.0 or earlier to version 7.16.0 or later, you must complete the following within your {sn} instance before creating a new {sn} SecOps connector or <>: + +* Install https://store.servicenow.com/sn_appstore_store.do#!/store/application/2f0746801baeb01019ae54e4604bcb0f[Elastic for Security Operations (SecOps)] from the {sn} Store. +* Create a {sn} integration user and assign it the appropriate roles. +* Create a Cross-Origin Resource Sharing (CORS) rule. + +*Create a {sn} integration user* + +To ensure authenticated communication between Elastic and {sn}, create a {sn} integration user and assign it the appropriate roles.  + +. In your {sn} instance, go to *System Security -> Users and Groups -> Users*. +. Click *New*. +. Complete the form, then right-click on the menu bar and click *Save*. +. Go to the *Roles* tab and click *Edit*. +. Assign the integration user the following roles:  +* `import_set_loader` +* `import_transformer` +* `personalize_choices` +* `sn_si.basic` +* `x_elas2_sir_int.integration_user` +. Click *Save*. + +*Create a CORS rule* + +A CORS rule is required for communication between Elastic and {sn}. To create a CORS rule: + +. In your {sn} instance, go to *System Web Services -> REST -> CORS Rules*. +. Click *New*. +. Configure the rule as follows: +* *Name*: Name the rule. +* *REST API*: Set the rule to use the Elastic SecOps API by choosing `Elastic SIR API [x_elas2_sir_int/elastic_api]`. +* *Domain*: Enter the Kibana URL. +. Go to the *HTTP methods* tab and select *GET*. +. Click *Submit* to create the rule. + +[float] +[[servicenow-sir-connector-update]] +==== Update a deprecated {sn} SecOps connector + +{sn} SecOps connectors created in {stack} version 7.15.0 or earlier are marked as deprecated after you upgrade to version 7.16.0 or later. Deprecated connectors have a yellow icon after their name and display a warning message when selected. + +[role="screenshot"] +image::management/connectors/images/servicenow-sir-update-connector.png[Shows deprecated ServiceNow connectors] + +IMPORTANT: Deprecated connectors will continue to function with the rules they were added to and can be assigned to new rules. However, it is strongly recommended to update deprecated connectors or <> to ensure you have access to connector enhancements, such as updating incidents. + +To update a deprecated connector: + +. Open the main menu and go to *Stack Management -> Rules and connectors -> Connectors*. +. Select the deprecated connector to open the *Edit connector* flyout. +. In the warning message, click *Update this connector*. +. Complete the guided steps in the *Edit connector* flyout. +.. Install https://store.servicenow.com/sn_appstore_store.do#!/store/application/2f0746801baeb01019ae54e4604bcb0f[Elastic for Security Operations (SecOps)] from the {sn} Store and complete the <>. +.. Enter the URL of your {sn} instance. +.. Enter the username and password of your {sn} instance. +. Click *Update*. [float] [[servicenow-sir-connector-configuration]] ==== Connector configuration -ServiceNow SecOps connectors have the following configuration properties. +{sn} SecOps connectors have the following configuration properties. Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** UI connector listing, and in the connector list when configuring an action. -URL:: ServiceNow instance URL. +URL:: {sn} instance URL. Username:: Username for HTTP Basic authentication. Password:: Password for HTTP Basic authentication. -The ServiceNow user requires at minimum read, create, and update access to the Security Incident table and read access to the https://docs.servicenow.com/bundle/paris-platform-administration/page/administer/localization/reference/r_ChoicesTable.html[sys_choice]. If you don't provide access to sys_choice, then the choices will not render. - [float] [[servicenow-sir-connector-networking-configuration]] ==== Connector networking configuration @@ -48,7 +106,7 @@ Config defines information for the connector type. `apiUrl`:: An address that corresponds to *URL*. `usesTableApi`:: A boolean that indicates if the connector uses the Table API or the Import Set API. -Note: If `usesTableApi` is set to false the Elastic application should be installed in ServiceNow. +NOTE: If `usesTableApi` is set to false, the Elastic application should be installed in {sn}. Secrets defines sensitive information for the connector type. @@ -59,12 +117,12 @@ Secrets defines sensitive information for the connector type. [[define-servicenow-sir-ui]] ==== Define connector in Stack Management -Define ServiceNow SecOps connector properties. +Define {sn} SecOps connector properties. [role="screenshot"] image::management/connectors/images/servicenow-sir-connector.png[ServiceNow SecOps connector] -Test ServiceNow SecOps action parameters. +Test {sn} SecOps action parameters. [role="screenshot"] image::management/connectors/images/servicenow-sir-params-test.png[ServiceNow SecOps params test] @@ -79,13 +137,16 @@ Short description:: A short description for the incident, used for searching Priority:: The priority of the incident. Category:: The category of the incident. Subcategory:: The subcategory of the incident. -Correlation ID:: All actions sharing this ID will be associated with the same ServiceNow security incident. If an incident exists in ServiceNow with the same correlation ID the security incident will be updated. Default value: `:`. -Correlation Display:: A descriptive label of the alert for correlation purposes in ServiceNow. +Correlation ID:: Connectors using the same Correlation ID will be associated with the same {sn} incident. This value determines whether a new {sn} incident will be created or an existing one is updated. Modifying this value is optional; if not modified, the rule ID and alert ID are combined as `{{ruleID}}:{{alert ID}}` to form the Correlation ID value in {sn}. The maximum character length for this value is 100 characters. + +NOTE: Using the default configuration of `{{ruleID}}:{{alert ID}}` ensures that {sn} will create a separate incident record for every generated alert that uses a unique alert ID. If the rule generates multiple alerts that use the same alert IDs, {sn} creates and continually updates a single incident record for the alert. + +Correlation Display:: A descriptive label of the alert for correlation purposes in {sn}. Description:: The details about the incident. Additional comments:: Additional information for the client, such as how to troubleshoot the issue. [float] [[configuring-servicenow-sir]] -==== Configure ServiceNow SecOps +==== Configure {sn} SecOps -ServiceNow offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. +{sn} offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. diff --git a/docs/management/connectors/action-types/servicenow.asciidoc b/docs/management/connectors/action-types/servicenow.asciidoc index bfa8c7db657d0..73e3baaca2ad1 100644 --- a/docs/management/connectors/action-types/servicenow.asciidoc +++ b/docs/management/connectors/action-types/servicenow.asciidoc @@ -1,25 +1,82 @@ [role="xpack"] [[servicenow-action-type]] -=== ServiceNow connector and action +=== ServiceNow ITSM connector and action ++++ ServiceNow ITSM ++++ -The ServiceNow ITSM connector uses the https://docs.servicenow.com/bundle/orlando-application-development/page/integrate/inbound-rest/concept/c_TableAPI.html[V2 Table API] to create ServiceNow incidents. +The {sn} ITSM connector uses the https://docs.servicenow.com/bundle/orlando-application-development/page/integrate/inbound-rest/concept/c_TableAPI.html[V2 Table API] to create {sn} incidents. + +[float] +[[servicenow-itsm-connector-prerequisites]] +==== Prerequisites +After upgrading from {stack} version 7.15.0 or earlier to version 7.16.0 or later, you must complete the following within your {sn} instance before creating a new {sn} ITSM connector or <>: + +* Install https://store.servicenow.com/sn_appstore_store.do#!/store/application/7148dbc91bf1f450ced060a7234bcb88[Elastic for ITSM] from the {sn} Store. +* Create a {sn} integration user and assign it the appropriate roles. +* Create a Cross-Origin Resource Sharing (CORS) rule. + +*Create a {sn} integration user* + +To ensure authenticated communication between Elastic and {sn}, create a {sn} integration user and assign it the appropriate roles. + +. In your {sn} instance, go to *System Security -> Users and Groups -> Users*. +. Click *New*. +. Complete the form, then right-click on the menu bar and click *Save*. +. Go to the *Roles* tab and click *Edit*. +. Assign the integration user the following roles:  +* `import_set_loader` +* `import_transformer` +* `personalize_choices` +* `x_elas2_inc_int.integration_user` +. Click *Save*. + +*Create a CORS rule* + +A CORS rule is required for communication between Elastic and {sn}. To create a CORS rule: + +. In your {sn} instance, go to *System Web Services -> REST -> CORS Rules*. +. Click *New*. +. Configure the rule as follows: +* *Name*: Name the rule. +* *REST API*: Set the rule to use the Elastic ITSM API by choosing `Elastic ITSM API [x_elas2_inc_int/elastic_api]`. +* *Domain*: Enter the Kibana URL. +. Go to the *HTTP methods* tab and select *GET*. +. Click *Submit* to create the rule. + +[float] +[[servicenow-itsm-connector-update]] +==== Update a deprecated {sn} ITSM connector + +{sn} ITSM connectors created in {stack} version 7.15.0 or earlier are marked as deprecated after you upgrade to version 7.16.0 or later. Deprecated connectors have a yellow icon after their name and display a warning message when selected. + +[role="screenshot"] +image::management/connectors/images/servicenow-sir-update-connector.png[Shows deprecated ServiceNow connectors] + +IMPORTANT: Deprecated connectors will continue to function with the rules they were added to and can be assigned to new rules. However, it is strongly recommended to update deprecated connectors or <> to ensure you have access to connector enhancements, such as updating incidents. + +To update a deprecated connector: + +. Open the main menu and go to *Stack Management -> Rules and connectors -> Connectors*. +. Select the deprecated connector to open the *Edit connector* flyout. +. In the warning message, click *Update this connector*. +. Complete the guided steps in the *Edit connector* flyout. +.. Install https://store.servicenow.com/sn_appstore_store.do#!/store/application/7148dbc91bf1f450ced060a7234bcb88[Elastic for ITSM] and complete the <>. +.. Enter the URL of your {sn} instance. +.. Enter the username and password of your {sn} instance. +. Click *Update*. [float] [[servicenow-connector-configuration]] ==== Connector configuration -ServiceNow ITSM connectors have the following configuration properties. +{sn} ITSM connectors have the following configuration properties. Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** UI connector listing, and in the connector list when configuring an action. -URL:: ServiceNow instance URL. +URL:: {sn} instance URL. Username:: Username for HTTP Basic authentication. Password:: Password for HTTP Basic authentication. -The ServiceNow user requires at minimum read, create, and update access to the Incident table and read access to the https://docs.servicenow.com/bundle/paris-platform-administration/page/administer/localization/reference/r_ChoicesTable.html[sys_choice]. If you don't provide access to sys_choice, then the choices will not render. - [float] [[servicenow-connector-networking-configuration]] ==== Connector networking configuration @@ -48,7 +105,7 @@ Config defines information for the connector type. `apiUrl`:: An address that corresponds to *URL*. `usesTableApi`:: A boolean that indicates if the connector uses the Table API or the Import Set API. -Note: If `usesTableApi` is set to false the Elastic application should be installed in ServiceNow. +NOTE: If `usesTableApi` is set to false, the Elastic application should be installed in {sn}. Secrets defines sensitive information for the connector type. @@ -59,12 +116,12 @@ Secrets defines sensitive information for the connector type. [[define-servicenow-ui]] ==== Define connector in Stack Management -Define ServiceNow ITSM connector properties. +Define {sn} ITSM connector properties. [role="screenshot"] image::management/connectors/images/servicenow-connector.png[ServiceNow connector] -Test ServiceNow ITSM action parameters. +Test {sn} ITSM action parameters. [role="screenshot"] image::management/connectors/images/servicenow-params-test.png[ServiceNow params test] @@ -73,21 +130,24 @@ image::management/connectors/images/servicenow-params-test.png[ServiceNow params [[servicenow-action-configuration]] ==== Action configuration -ServiceNow ITSM actions have the following configuration properties. +{sn} ITSM actions have the following configuration properties. Urgency:: The extent to which the incident resolution can delay. Severity:: The severity of the incident. Impact:: The effect an incident has on business. Can be measured by the number of affected users or by how critical it is to the business in question. Category:: The category of the incident. Subcategory:: The category of the incident. -Correlation ID:: All actions sharing this ID will be associated with the same ServiceNow incident. If an incident exists in ServiceNow with the same correlation ID the incident will be updated. Default value: `:`. -Correlation Display:: A descriptive label of the alert for correlation purposes in ServiceNow. +Correlation ID:: Connectors using the same Correlation ID will be associated with the same {sn} incident. This value determines whether a new {sn} incident will be created or an existing one is updated. Modifying this value is optional; if not modified, the rule ID and alert ID are combined as `{{ruleID}}:{{alert ID}}` to form the Correlation ID value in {sn}. The maximum character length for this value is 100 characters. + +NOTE: Using the default configuration of `{{ruleID}}:{{alert ID}}` ensures that {sn} will create a separate incident record for every generated alert that uses a unique alert ID. If the rule generates multiple alerts that use the same alert IDs, {sn} creates and continually updates a single incident record for the alert. + +Correlation Display:: A descriptive label of the alert for correlation purposes in {sn}. Short description:: A short description for the incident, used for searching the contents of the knowledge base. Description:: The details about the incident. Additional comments:: Additional information for the client, such as how to troubleshoot the issue. [float] [[configuring-servicenow]] -==== Configure ServiceNow +==== Configure {sn} -ServiceNow offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. +{sn} offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. diff --git a/docs/management/connectors/images/servicenow-params-test.png b/docs/management/connectors/images/servicenow-params-test.png index 79f1580c873d2647de495089041ac11814011ada..d8a85608bd40e0990da212db3cb54d37e436ff59 100644 GIT binary patch literal 233010 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 cKeZ5nP5D^oUGZ>a2k Date: Thu, 2 Dec 2021 17:37:26 -0500 Subject: [PATCH 21/65] [Alerting][UI] Adding tooltip to Last Run column title (#120159) --- .../components/alerts_list.test.tsx | 36 +++++++++++++++++++ .../alerts_list/components/alerts_list.tsx | 18 +++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 5067cbbee9bf4..65f82da69ccc5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -328,6 +328,8 @@ describe('alerts_list component with items', () => { } it('renders table of alerts', async () => { + // Use fake timers so we don't have to wait for the EuiToolTip timeout + jest.useFakeTimers(); await setup(); expect(wrapper.find('EuiBasicTable')).toHaveLength(1); expect(wrapper.find('EuiTableRow')).toHaveLength(mockedAlertsData.length); @@ -359,6 +361,23 @@ describe('alerts_list component with items', () => { wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-lastExecutionDate"]').length ).toEqual(mockedAlertsData.length); + // Last run tooltip + wrapper + .find('[data-test-subj="alertsTableCell-lastExecutionDateTooltip"]') + .first() + .simulate('mouseOver'); + + // Run the timers so the EuiTooltip will be visible + jest.runAllTimers(); + + wrapper.update(); + expect(wrapper.find('.euiToolTipPopover').text()).toBe('Start time of the last execution.'); + + wrapper + .find('[data-test-subj="alertsTableCell-lastExecutionDateTooltip"]') + .first() + .simulate('mouseOut'); + // Schedule interval column expect( wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-interval"]').length @@ -377,6 +396,20 @@ describe('alerts_list component with items', () => { ).length ); + // Duration tooltip + wrapper + .find('[data-test-subj="alertsTableCell-durationTooltip"]') + .first() + .simulate('mouseOver'); + + // Run the timers so the EuiTooltip will be visible + jest.runAllTimers(); + + wrapper.update(); + expect(wrapper.find('.euiToolTipPopover').text()).toBe( + 'The length of time it took for the rule to run.' + ); + // Status column expect(wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-status"]').length).toEqual( mockedAlertsData.length @@ -399,6 +432,9 @@ describe('alerts_list component with items', () => { expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-error"]').last().text()).toEqual( 'License Error' ); + + // Clearing all mocks will also reset fake timers. + jest.clearAllMocks(); }); it('loads alerts when refresh button is clicked', async () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 9a9e8f0f56717..f1e88223e57b6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -483,7 +483,22 @@ export const AlertsList: React.FunctionComponent = () => { }, { field: 'executionStatus.lastExecutionDate', - name: 'Last run', + name: ( + + + Last run{' '} + + + + ), sortable: true, width: '15%', 'data-test-subj': 'alertsTableCell-lastExecutionDate', @@ -523,6 +538,7 @@ export const AlertsList: React.FunctionComponent = () => { width: '12%', name: ( Date: Fri, 3 Dec 2021 00:51:59 +0100 Subject: [PATCH 22/65] [RAC] Adds stats about rules to Alerts page (#119750) * Adds EuiStat to alerts page template * Adds functional tests for stat counters * Whoops - no exclusive tests * Uses triggers_actions_ui API * Toaster on error * Early review feedback * Uses new rule aggregation API * Makes tests pass * Adds logging * Whoops forgot an await * Limits triggers actions UI exports to loadAlertAggregations * Creates rules via API for functional tests * Extracts common methods to create rules via API * Removes unnecessary template strings * Cleanup * Reuses common dummy alert plugin fixture * Removes unnecessary config * Removes unnecessary config --- .../containers/alerts_page/alerts_page.tsx | 114 ++++++++++- .../triggers_actions_ui/public/index.ts | 1 + .../services/observability/alerts/common.ts | 11 + .../apps/triggers_actions_ui/alerts_list.ts | 189 ++++++++++-------- .../lib/alert_api_actions.ts | 87 ++++++++ .../apps/observability/alerts/index.ts | 68 +++++++ .../with_rac_write.config.ts | 50 ++++- 7 files changed, 432 insertions(+), 88 deletions(-) create mode 100644 x-pack/test/functional_with_es_ssl/lib/alert_api_actions.ts diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx index b19a1dbe86fe1..06040d9a186ff 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx @@ -5,15 +5,17 @@ * 2.0. */ -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; import { IndexPatternBase } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; -import React, { useCallback, useRef, useState, useEffect } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { AlertStatus } from '@kbn/rule-data-utils/alerts_as_data_status'; import { ALERT_STATUS } from '@kbn/rule-data-utils/technical_field_names'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { loadAlertAggregations as loadRuleAggregations } from '../../../../../../../plugins/triggers_actions_ui/public'; import { AlertStatusFilterButton } from '../../../../../common/typings'; import { ParsedTechnicalFields } from '../../../../../../rule_registry/common/parse_technical_fields'; import { ExperimentalBadge } from '../../../../components/shared/experimental_badge'; @@ -34,6 +36,12 @@ import { import './styles.scss'; import { AlertsStatusFilter, AlertsDisclaimer, AlertsSearchBar } from '../../components'; +interface RuleStatsState { + total: number; + disabled: number; + muted: number; + error: number; +} export interface TopAlert { fields: ParsedTechnicalFields; start: number; @@ -41,6 +49,12 @@ export interface TopAlert { link?: string; active: boolean; } + +const Divider = euiStyled.div` + border-right: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; + height: 100%; +`; + const regExpEscape = (str: string) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); const NO_INDEX_NAMES: string[] = []; const NO_INDEX_PATTERNS: IndexPatternBase[] = []; @@ -60,6 +74,17 @@ function AlertsPage() { const timefilterService = useTimefilterService(); const { rangeFrom, setRangeFrom, rangeTo, setRangeTo, kuery, setKuery, workflowStatus } = useAlertsPageStateContainer(); + const { + http, + notifications: { toasts }, + } = core; + const [ruleStatsLoading, setRuleStatsLoading] = useState(false); + const [ruleStats, setRuleStats] = useState({ + total: 0, + disabled: 0, + muted: 0, + error: 0, + }); useEffect(() => { syncAlertStatusFilterStatus(kuery as string); @@ -73,6 +98,48 @@ function AlertsPage() { }, ]); + async function loadRuleStats() { + setRuleStatsLoading(true); + try { + const response = await loadRuleAggregations({ + http, + }); + // Note that the API uses the semantics of 'alerts' instead of 'rules' + const { alertExecutionStatus, ruleMutedStatus, ruleEnabledStatus } = response; + if (alertExecutionStatus && ruleMutedStatus && ruleEnabledStatus) { + const total = Object.entries(alertExecutionStatus).reduce((acc, [key, value]) => { + if (key !== 'error') { + acc = acc + value; + } + return acc; + }, 0); + const { error } = alertExecutionStatus; + const { muted } = ruleMutedStatus; + const { disabled } = ruleEnabledStatus; + setRuleStats({ + ...ruleStats, + total, + disabled, + muted, + error, + }); + } + setRuleStatsLoading(false); + } catch (_e) { + toasts.addDanger({ + title: i18n.translate('xpack.observability.alerts.ruleStats.loadError', { + defaultMessage: 'Unable to load rule stats', + }), + }); + setRuleStatsLoading(false); + } + } + + useEffect(() => { + loadRuleStats(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // In a future milestone we'll have a page dedicated to rule management in // observability. For now link to the settings page. const manageRulesHref = prepend('/app/management/insightsAndAlerting/triggersActions/alerts'); @@ -198,12 +265,53 @@ function AlertsPage() { ), rightSideItems: [ + , + , + , + , + , {i18n.translate('xpack.observability.alerts.manageRulesButtonLabel', { defaultMessage: 'Manage Rules', })} , - ], + ].reverse(), }} > diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 4c4a424b51eea..97f4d847361f2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -45,6 +45,7 @@ export function plugin() { export { Plugin }; export * from './plugin'; +export { loadAlertAggregations } from './application/lib/alert_api/aggregate'; export { loadActionTypes } from './application/lib/action_connector_api/connector_types'; diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts index 8c6352fff9864..2f888d3d733c0 100644 --- a/x-pack/test/functional/services/observability/alerts/common.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -5,6 +5,7 @@ * 2.0. */ +import expect from '@kbn/expect'; import { chunk } from 'lodash'; import { ALERT_STATUS_ACTIVE, @@ -270,6 +271,15 @@ export function ObservabilityAlertsCommonProvider({ return actionsOverflowButtons[index] || null; }; + const getAlertStatValue = async (testSubj: string) => { + const stat = await testSubjects.find(testSubj); + const title = await stat.findByCssSelector('.euiStat__title'); + const count = await title.getVisibleText(); + const value = Number.parseInt(count, 10); + expect(Number.isNaN(value)).to.be(false); + return value; + }; + return { getQueryBar, clearQueryBar, @@ -307,5 +317,6 @@ export function ObservabilityAlertsCommonProvider({ viewRuleDetailsButtonClick, viewRuleDetailsLinkClick, getAlertsFlyoutViewRuleDetailsLinkOrFail, + getAlertStatValue, }; } diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index 04b9b1b45b633..0a0e409ef53a9 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -7,8 +7,16 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { + createAction, + createAlert, + createAlertManualCleanup, + createFailingAlert, + disableAlert, + muteAlert, +} from '../../lib/alert_api_actions'; import { ObjectRemover } from '../../lib/object_remover'; -import { generateUniqueKey, getTestAlertData, getTestActionData } from '../../lib/get_test_data'; +import { generateUniqueKey } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); @@ -18,52 +26,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const retry = getService('retry'); const objectRemover = new ObjectRemover(supertest); - async function createAlertManualCleanup(overwrites: Record = {}) { - const { body: createdAlert } = await supertest - .post(`/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestAlertData(overwrites)) - .expect(200); - return createdAlert; - } - - async function createFailingAlert() { - return await createAlert({ - rule_type_id: 'test.failing', - schedule: { interval: '30s' }, - }); - } - - async function createAlert(overwrites: Record = {}) { - const createdAlert = await createAlertManualCleanup(overwrites); - objectRemover.add(createdAlert.id, 'alert', 'alerts'); - return createdAlert; - } - - async function createAction(overwrites: Record = {}) { - const { body: createdAction } = await supertest - .post(`/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send(getTestActionData(overwrites)) - .expect(200); - objectRemover.add(createdAction.id, 'action', 'actions'); - return createdAction; - } - - async function muteAlert(alertId: string) { - const { body: alert } = await supertest - .post(`/api/alerting/rule/${alertId}/_mute_all`) - .set('kbn-xsrf', 'foo'); - return alert; - } - - async function disableAlert(alertId: string) { - const { body: alert } = await supertest - .post(`/api/alerting/rule/${alertId}/_disable`) - .set('kbn-xsrf', 'foo'); - return alert; - } - async function refreshAlertsList() { await testSubjects.click('rulesTab'); } @@ -80,9 +42,21 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should display alerts in alphabetical order', async () => { const uniqueKey = generateUniqueKey(); - await createAlert({ name: 'b', tags: [uniqueKey] }); - await createAlert({ name: 'c', tags: [uniqueKey] }); - await createAlert({ name: 'a', tags: [uniqueKey] }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'b', tags: [uniqueKey] }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'c', tags: [uniqueKey] }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'a', tags: [uniqueKey] }, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(uniqueKey); @@ -95,7 +69,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should search for alert', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ + supertest, + objectRemover, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -108,8 +85,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should update alert list on the search clear button click', async () => { - await createAlert({ name: 'b' }); - await createAlert({ name: 'c', tags: [] }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'b' }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'c', tags: [] }, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts('b'); @@ -138,7 +123,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should search for tags', async () => { - const createdAlert = await createAlert({ tags: ['tag', 'tagtag', 'taggity tag'] }); + const createdAlert = await createAlert({ + supertest, + objectRemover, + overwrites: { tags: ['tag', 'tagtag', 'taggity tag'] }, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(`${createdAlert.name} tag`); @@ -151,7 +140,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should display an empty list when search did not return any alerts', async () => { - await createAlert(); + await createAlert({ + supertest, + objectRemover, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(`An Alert That For Sure Doesn't Exist!`); @@ -159,7 +151,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should disable single alert', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ + supertest, + objectRemover, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -175,8 +170,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should re-enable single alert', async () => { - const createdAlert = await createAlert(); - await disableAlert(createdAlert.id); + const createdAlert = await createAlert({ + supertest, + objectRemover, + }); + await disableAlert({ supertest, alertId: createdAlert.id }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -191,7 +189,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should mute single alert', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ + supertest, + objectRemover, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -207,7 +208,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should be able to mute the rule with non "alerts" consumer from a non editable context', async () => { - const createdAlert = await createAlert({ consumer: 'siem' }); + const createdAlert = await createAlert({ + supertest, + objectRemover, + overwrites: { consumer: 'siem' }, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -223,8 +228,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should unmute single alert', async () => { - const createdAlert = await createAlert(); - await muteAlert(createdAlert.id); + const createdAlert = await createAlert({ + supertest, + objectRemover, + }); + await muteAlert({ supertest, alertId: createdAlert.id }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -239,8 +247,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should delete single alert', async () => { - await createAlert(); - const secondAlert = await createAlertManualCleanup(); + await createAlert({ + supertest, + objectRemover, + }); + const secondAlert = await createAlertManualCleanup({ supertest }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(secondAlert.name); @@ -262,7 +273,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should mute all selection', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ supertest, objectRemover }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -285,7 +296,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should unmute all selection', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ supertest, objectRemover }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -307,7 +318,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should disable all selection', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ supertest, objectRemover }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -328,7 +339,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should enable all selection', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ supertest, objectRemover }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); @@ -352,7 +363,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should delete all selection', async () => { const namePrefix = generateUniqueKey(); - const createdAlert = await createAlertManualCleanup({ name: `${namePrefix}-1` }); + const createdAlert = await createAlertManualCleanup({ + supertest, + overwrites: { name: `${namePrefix}-1` }, + }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(namePrefix); @@ -376,8 +390,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should filter alerts by the status', async () => { - await createAlert(); - const failingAlert = await createFailingAlert(); + await createAlert({ supertest, objectRemover }); + const failingAlert = await createFailingAlert({ supertest, objectRemover }); // initialy alert get Pending status, so we need to retry refresh list logic to get the post execution statuses await retry.try(async () => { await refreshAlertsList(); @@ -398,7 +412,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should display total alerts by status and error banner only when exists alerts with status error', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ supertest, objectRemover }); await retry.try(async () => { await refreshAlertsList(); const refreshResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); @@ -414,7 +428,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ); expect(alertsErrorBannerWhenNoErrors).to.have.length(0); - await createFailingAlert(); + await createFailingAlert({ supertest, objectRemover }); await retry.try(async () => { await refreshAlertsList(); const alertsErrorBannerExistErrors = await find.allByCssSelector( @@ -438,8 +452,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should filter alerts by the alert type', async () => { - await createAlert(); - const failingAlert = await createFailingAlert(); + await createAlert({ supertest, objectRemover }); + const failingAlert = await createFailingAlert({ supertest, objectRemover }); await refreshAlertsList(); await testSubjects.click('alertTypeFilterButton'); expect(await (await testSubjects.find('alertType0Group')).getVisibleText()).to.eql('Alerts'); @@ -455,16 +469,23 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should filter alerts by the action type', async () => { - await createAlert(); - const action = await createAction(); + await createAlert({ + supertest, + objectRemover, + }); + const action = await createAction({ supertest, objectRemover }); const noopAlertWithAction = await createAlert({ - actions: [ - { - id: action.id, - group: 'default', - params: { level: 'info', message: 'gfghfhg' }, - }, - ], + supertest, + objectRemover, + overwrites: { + actions: [ + { + id: action.id, + group: 'default', + params: { level: 'info', message: 'gfghfhg' }, + }, + ], + }, }); await refreshAlertsList(); await testSubjects.click('actionTypeFilterButton'); diff --git a/x-pack/test/functional_with_es_ssl/lib/alert_api_actions.ts b/x-pack/test/functional_with_es_ssl/lib/alert_api_actions.ts new file mode 100644 index 0000000000000..40e567c299826 --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/lib/alert_api_actions.ts @@ -0,0 +1,87 @@ +/* + * 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 { ObjectRemover } from './object_remover'; +import { getTestAlertData, getTestActionData } from './get_test_data'; + +export async function createAlertManualCleanup({ + supertest, + overwrites = {}, +}: { + supertest: any; + overwrites?: Record; +}) { + const { body: createdAlert } = await supertest + .post('/api/alerting/rule') + .set('kbn-xsrf', 'foo') + .send(getTestAlertData(overwrites)) + .expect(200); + return createdAlert; +} + +export async function createFailingAlert({ + supertest, + objectRemover, +}: { + supertest: any; + objectRemover: ObjectRemover; +}) { + return await createAlert({ + supertest, + overwrites: { + rule_type_id: 'test.failing', + schedule: { interval: '30s' }, + }, + objectRemover, + }); +} + +export async function createAlert({ + supertest, + objectRemover, + overwrites = {}, +}: { + supertest: any; + objectRemover: ObjectRemover; + overwrites?: Record; +}) { + const createdAlert = await createAlertManualCleanup({ supertest, overwrites }); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); + return createdAlert; +} + +export async function createAction({ + supertest, + objectRemover, + overwrites = {}, +}: { + supertest: any; + objectRemover: ObjectRemover; + overwrites?: Record; +}) { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send(getTestActionData(overwrites)) + .expect(200); + objectRemover.add(createdAction.id, 'action', 'actions'); + return createdAction; +} + +export async function muteAlert({ supertest, alertId }: { supertest: any; alertId: string }) { + const { body: alert } = await supertest + .post(`/api/alerting/rule/${alertId}/_mute_all`) + .set('kbn-xsrf', 'foo'); + return alert; +} + +export async function disableAlert({ supertest, alertId }: { supertest: any; alertId: string }) { + const { body: alert } = await supertest + .post(`/api/alerting/rule/${alertId}/_disable`) + .set('kbn-xsrf', 'foo'); + return alert; +} diff --git a/x-pack/test/observability_functional/apps/observability/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/alerts/index.ts index 3abf04ed29e67..12a83f19ca258 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/index.ts @@ -7,6 +7,13 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { ObjectRemover } from '../../../../functional_with_es_ssl/lib/object_remover'; +import { + createAlert, + disableAlert, + muteAlert, +} from '../../../../functional_with_es_ssl/lib/alert_api_actions'; +import { generateUniqueKey } from '../../../../functional_with_es_ssl/lib/get_test_data'; async function asyncForEach(array: T[], callback: (item: T, index: number) => void) { for (let index = 0; index < array.length; index++) { @@ -21,6 +28,8 @@ const TOTAL_ALERTS_CELL_COUNT = 165; export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const find = getService('find'); + const supertest = getService('supertest'); + const objectRemover = new ObjectRemover(supertest); describe('Observability alerts', function () { this.tags('includeFirefox'); @@ -236,6 +245,65 @@ export default ({ getService }: FtrProviderContext) => { expect(await find.existsByCssSelector('[title="Rules and Connectors"]')).to.eql(true); }); }); + + describe('Stat counters', () => { + beforeEach(async () => { + const uniqueKey = generateUniqueKey(); + + const alertToDisable = await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'b', tags: [uniqueKey] }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'c', tags: [uniqueKey] }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'a', tags: [uniqueKey] }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'd', tags: [uniqueKey] }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'e', tags: [uniqueKey] }, + }); + const alertToMute = await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'f', tags: [uniqueKey] }, + }); + + await disableAlert({ supertest, alertId: alertToDisable.id }); + await muteAlert({ supertest, alertId: alertToMute.id }); + + await observability.alerts.common.navigateToTimeWithData(); + }); + + afterEach(async () => { + await objectRemover.removeAll(); + }); + + it('Exist and display expected values', async () => { + const subjToValueMap: { [key: string]: number } = { + statRuleCount: 6, + statDisabled: 1, + statMuted: 1, + statErrors: 0, + }; + await asyncForEach(Object.keys(subjToValueMap), async (subject: string) => { + const value = await observability.alerts.common.getAlertStatValue(subject); + expect(value).to.be(subjToValueMap[subject]); + }); + }); + }); }); }); }; diff --git a/x-pack/test/observability_functional/with_rac_write.config.ts b/x-pack/test/observability_functional/with_rac_write.config.ts index dcf6b121d6258..71a1de1df6a77 100644 --- a/x-pack/test/observability_functional/with_rac_write.config.ts +++ b/x-pack/test/observability_functional/with_rac_write.config.ts @@ -6,10 +6,26 @@ */ import { readFileSync } from 'fs'; -import { resolve } from 'path'; +import { resolve, join } from 'path'; import { CA_CERT_PATH } from '@kbn/dev-utils'; import { FtrConfigProviderContext } from '@kbn/test'; +// .server-log is specifically not enabled +const enabledActionTypes = [ + '.email', + '.index', + '.pagerduty', + '.swimlane', + '.servicenow', + '.slack', + '.webhook', + 'test.authorization', + 'test.failing', + 'test.index-record', + 'test.noop', + 'test.rate-limit', +]; + export default async function ({ readConfigFile }: FtrConfigProviderContext) { const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); @@ -36,6 +52,38 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...xpackFunctionalConfig.get('kbnTestServer.serverArgs'), `--elasticsearch.hosts=https://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, + `--plugin-path=${join( + __dirname, + '..', + 'functional_with_es_ssl', + 'fixtures', + 'plugins', + 'alerts' + )}`, + `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, + `--xpack.actions.preconfiguredAlertHistoryEsIndex=false`, + `--xpack.actions.preconfigured=${JSON.stringify({ + 'my-slack1': { + actionTypeId: '.slack', + name: 'Slack#xyztest', + secrets: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + }, + 'my-server-log': { + actionTypeId: '.server-log', + name: 'Serverlog#xyz', + }, + 'my-email-connector': { + actionTypeId: '.email', + name: 'Email#test-preconfigured-email', + config: { + from: 'me@example.com', + host: 'localhost', + port: '1025', + }, + }, + })}`, ], }, uiSettings: { From 92a8636f0ff63ab072527574e96e6616327b2ea4 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Thu, 2 Dec 2021 16:27:41 -0800 Subject: [PATCH 23/65] Upgrade Typescript to 4.3.5 (#104470) Co-authored-by: Mikhail Shustov Co-authored-by: Dario Gieselaar Co-authored-by: CJ Cenizal Co-authored-by: Justin Kambic Co-authored-by: Stratoula Kalafateli Co-authored-by: Jean-Louis Leysens Co-authored-by: Kyle Pollich Co-authored-by: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Co-authored-by: Kevin Qualters Co-authored-by: Candace Park Co-authored-by: Brian Seeders Co-authored-by: spalger --- ...core-public.scopedhistory._constructor_.md | 4 +- ...-plugin-core-public.scopedhistory.block.md | 2 +- ...e-public.scopedhistory.createsubhistory.md | 2 +- ...kibana-plugin-core-public.scopedhistory.md | 4 +- ...lugin-core-server.kibanaresponsefactory.md | 32 +- package.json | 10 +- packages/kbn-alerts/BUILD.bazel | 2 - packages/kbn-alerts/tsconfig.json | 2 +- .../buid_api_declaration.test.ts | 3 +- .../build_basic_api_declaration.ts | 2 +- .../build_api_declarations/js_doc_utils.ts | 4 +- .../src/api_docs/mdx/write_plugin_mdx_docs.ts | 2 +- .../src/api_docs/tests/api_doc_suite.test.ts | 2 +- .../api_docs/tests/snapshots/plugin_a.json | 6 +- .../src/api_docs/tests/snapshots/plugin_a.mdx | 2 +- .../api_docs/tests/snapshots/plugin_a_foo.mdx | 2 +- .../src/api_docs/tests/snapshots/plugin_b.mdx | 2 +- packages/kbn-field-types/BUILD.bazel | 1 - packages/kbn-optimizer/BUILD.bazel | 2 - packages/kbn-pm/dist/index.js | 8490 +++++++---------- packages/kbn-react-field/BUILD.bazel | 1 - packages/kbn-react-field/tsconfig.json | 1 - .../BUILD.bazel | 2 - .../tsconfig.json | 2 +- packages/kbn-storybook/src/webpack.config.ts | 6 +- packages/kbn-test/src/jest/run.ts | 5 +- .../kbn-typed-react-router-config/BUILD.bazel | 1 + .../src/create_router.test.tsx | 2 + .../src/create_router.ts | 14 +- .../src/types/index.ts | 20 +- packages/kbn-ui-shared-deps-npm/BUILD.bazel | 2 - packages/kbn-ui-shared-deps-npm/tsconfig.json | 1 - packages/kbn-ui-shared-deps-src/tsconfig.json | 1 - src/core/public/application/scoped_history.ts | 11 +- src/core/public/chrome/chrome_service.tsx | 2 +- src/core/public/core_app/core_app.ts | 4 +- .../public/doc_links/doc_links_service.ts | 2 +- .../fatal_errors/fatal_errors_service.tsx | 2 +- .../integrations/integrations_service.ts | 2 +- .../notifications/notifications_service.ts | 4 +- src/core/public/public.api.md | 7 +- .../public/rendering/rendering_service.tsx | 2 +- src/core/public/theme/theme_service.ts | 2 +- .../public/ui_settings/ui_settings_service.ts | 2 +- .../server/context/container/context.mock.ts | 1 - src/core/server/context/context_service.ts | 2 +- src/core/server/http/http_service.mock.ts | 13 +- src/core/server/http/http_service.ts | 4 +- src/core/server/http/router/headers.ts | 16 +- src/core/server/i18n/i18n_service.ts | 4 +- src/core/server/logging/logging_service.ts | 2 +- src/core/server/metrics/metrics_service.ts | 2 +- src/core/server/server.api.md | 33 +- src/core/server/status/status_service.ts | 2 +- src/core/types/elasticsearch/search.ts | 36 +- src/dev/build/build_distributables.ts | 8 +- src/dev/precommit_hook/casing_check_config.js | 1 + src/dev/typescript/run_type_check_cli.ts | 2 +- .../models/sense_editor/sense_editor.ts | 4 +- .../embeddable/grid/dashboard_grid.tsx | 6 +- .../search_interceptor/search_interceptor.ts | 9 +- .../data/public/ui/search_bar/search_bar.tsx | 2 +- .../data/server/search/collectors/usage.ts | 8 +- .../editors/number/number.tsx | 2 +- .../components/scripting_help/test_script.tsx | 2 +- .../components/field_editor/field_editor.tsx | 2 +- .../main/services/discover_search_session.ts | 4 +- src/plugins/discover/public/build_services.ts | 6 +- .../discover/public/kibana_services.ts | 4 +- .../attribute_service/attribute_service.tsx | 12 +- .../embeddable_state_transfer.ts | 4 +- .../common/execution/execution_contract.ts | 6 +- .../expression_types/expression_type.ts | 4 +- .../common/field_formats_registry.ts | 6 +- .../components/vis/input_control_vis.tsx | 6 +- .../public/components/add_data/add_data.tsx | 13 +- .../components/manage_data/manage_data.tsx | 13 +- .../control_group_container_factory.ts | 1 - .../utils/use/use_editor_updates.test.ts | 5 +- test/tsconfig.json | 2 +- tsconfig.base.json | 4 +- typings/resize-observer-polyfill/index.d.ts | 10 + .../drilldown.tsx | 8 +- x-pack/plugins/apm/kibana.json | 4 +- .../plugins/apm/public/application/uxApp.tsx | 1 + .../app/service_inventory/index.tsx | 4 +- .../service_map/Popover/backend_contents.tsx | 1 + .../components/shared/managed_table/index.tsx | 1 + .../shared/time_comparison/index.tsx | 1 + .../apm/public/hooks/use_apm_router.ts | 4 +- .../create_apm_event_client/index.test.ts | 6 +- .../create_apm_event_client/index.ts | 226 +- .../apm/server/lib/helpers/setup_request.ts | 7 +- .../get_transaction_group_stats.ts | 6 +- x-pack/plugins/apm/server/plugin.ts | 6 +- ...ister_transaction_error_rate_alert_type.ts | 38 +- .../apm/server/routes/data_view/route.ts | 13 +- .../get_error_group_main_statistics.ts | 1 + .../service_map/get_service_anomalies.ts | 1 + ...ervice_instances_transaction_statistics.ts | 1 + .../settings/apm_indices/get_apm_indices.ts | 2 +- .../server/routes/traces/get_trace_items.ts | 6 +- x-pack/plugins/apm/server/routes/typings.ts | 11 +- x-pack/plugins/apm/server/types.ts | 160 +- .../abstract_dashboard_drilldown.tsx | 13 +- .../utils/saved_search_utils.test.ts | 2 +- .../accordion_list.test.tsx | 4 +- .../components/crawl_requests_table.test.tsx | 2 + .../components/crawl_requests_table.tsx | 7 +- .../credentials_list.test.tsx | 4 +- .../ignored_queries_panel.test.tsx | 1 + .../error_pages/components/no_data_layout.tsx | 2 +- .../field_manager/field_manager.test.tsx | 2 +- .../graph/public/state_management/fields.ts | 4 +- .../public/state_management/persistence.ts | 16 +- .../graph/public/state_management/store.ts | 2 +- .../public/state_management/workspace.ts | 12 +- .../plugins/graph/public/types/app_state.ts | 2 +- .../components/mappings_editor/reducer.ts | 2 - .../expression_editor/criterion.tsx | 6 +- .../log_text_stream/loading_item_view.tsx | 2 +- .../pipelines_create/pipelines_create.tsx | 10 +- .../maps/public/actions/layer_actions.ts | 8 +- .../tooltip_selector/tooltip_selector.tsx | 2 + .../feature_geometry_filter_form.tsx | 4 +- .../ml/server/lib/alerts/alerting_service.ts | 3 +- .../data_recognizer/data_recognizer.test.ts | 2 +- .../public/routes/live_queries/new/index.tsx | 6 +- .../reporting_api_client.ts | 8 +- .../rule_data_client/rule_data_client.mock.ts | 1 + .../utils/create_lifecycle_executor.test.ts | 1 + .../utils/create_lifecycle_rule_type.test.ts | 1 + .../management/users/edit_user/user_form.tsx | 2 +- .../common/endpoint/schema/trusted_apps.ts | 6 +- .../security_solution/cypress/tsconfig.json | 1 - .../public/common/mock/utils.ts | 2 +- .../detection_engine/rules/helpers.test.tsx | 3 +- .../public/management/index.ts | 2 +- .../view/side_effect_simulator_factory.ts | 9 +- .../timelines/components/timeline/styles.tsx | 4 +- .../signals/query_signals_route.test.ts | 1 + .../detection_engine/signals/utils.test.ts | 2 +- .../security_solution/index.ts | 16 +- .../public/components/t_grid/styles.tsx | 4 +- .../lib/reindexing/credential_store.test.ts | 18 +- .../monitor_list/use_monitor_histogram.ts | 1 + .../uptime/public/state/alerts/alerts.ts | 8 +- .../uptime/public/state/effects/alerts.ts | 32 +- .../public/state/effects/fetch_effect.ts | 4 +- .../uptime/public/state/effects/journey.ts | 8 +- .../uptime/public/state/effects/ml_anomaly.ts | 30 +- .../public/state/effects/network_events.ts | 47 +- x-pack/plugins/uptime/public/state/index.ts | 3 +- .../uptime/public/state/selectors/index.ts | 2 +- .../server/lib/requests/get_monitor_status.ts | 1 + .../lib/requests/get_ping_histogram.test.ts | 38 + .../server/lib/requests/get_ping_histogram.ts | 2 +- .../services/observability/users.ts | 1 - yarn.lock | 81 +- 159 files changed, 4022 insertions(+), 5878 deletions(-) create mode 100644 typings/resize-observer-polyfill/index.d.ts diff --git a/docs/development/core/public/kibana-plugin-core-public.scopedhistory._constructor_.md b/docs/development/core/public/kibana-plugin-core-public.scopedhistory._constructor_.md index 32b0950aa1065..67264a26ac5db 100644 --- a/docs/development/core/public/kibana-plugin-core-public.scopedhistory._constructor_.md +++ b/docs/development/core/public/kibana-plugin-core-public.scopedhistory._constructor_.md @@ -9,13 +9,13 @@ Constructs a new instance of the `ScopedHistory` class Signature: ```typescript -constructor(parentHistory: History, basePath: string); +constructor(parentHistory: History, basePath: string); ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| parentHistory | History | | +| parentHistory | History<HistoryLocationState> | | | basePath | string | | diff --git a/docs/development/core/public/kibana-plugin-core-public.scopedhistory.block.md b/docs/development/core/public/kibana-plugin-core-public.scopedhistory.block.md index eb632465e4699..acbb06c6aa6ec 100644 --- a/docs/development/core/public/kibana-plugin-core-public.scopedhistory.block.md +++ b/docs/development/core/public/kibana-plugin-core-public.scopedhistory.block.md @@ -9,5 +9,5 @@ Add a block prompt requesting user confirmation when navigating away from the cu Signature: ```typescript -block: (prompt?: string | boolean | History.TransitionPromptHook | undefined) => UnregisterCallback; +block: (prompt?: string | boolean | TransitionPromptHook | undefined) => UnregisterCallback; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.scopedhistory.createsubhistory.md b/docs/development/core/public/kibana-plugin-core-public.scopedhistory.createsubhistory.md index a976eeed912b2..7c5dfccb5b008 100644 --- a/docs/development/core/public/kibana-plugin-core-public.scopedhistory.createsubhistory.md +++ b/docs/development/core/public/kibana-plugin-core-public.scopedhistory.createsubhistory.md @@ -9,5 +9,5 @@ Creates a `ScopedHistory` for a subpath of this `ScopedHistory`. Useful Signature: ```typescript -createSubHistory: (basePath: string) => ScopedHistory; +createSubHistory: (basePath: string) => ScopedHistory; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.scopedhistory.md b/docs/development/core/public/kibana-plugin-core-public.scopedhistory.md index 0d04fc3d6a860..a3c369b143b4a 100644 --- a/docs/development/core/public/kibana-plugin-core-public.scopedhistory.md +++ b/docs/development/core/public/kibana-plugin-core-public.scopedhistory.md @@ -28,9 +28,9 @@ export declare class ScopedHistory implements Hi | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [action](./kibana-plugin-core-public.scopedhistory.action.md) | | Action | The last action dispatched on the history stack. | -| [block](./kibana-plugin-core-public.scopedhistory.block.md) | | (prompt?: string \| boolean \| History.TransitionPromptHook<HistoryLocationState> \| undefined) => UnregisterCallback | Add a block prompt requesting user confirmation when navigating away from the current page. | +| [block](./kibana-plugin-core-public.scopedhistory.block.md) | | (prompt?: string \| boolean \| TransitionPromptHook<HistoryLocationState> \| undefined) => UnregisterCallback | Add a block prompt requesting user confirmation when navigating away from the current page. | | [createHref](./kibana-plugin-core-public.scopedhistory.createhref.md) | | (location: LocationDescriptorObject<HistoryLocationState>, { prependBasePath }?: { prependBasePath?: boolean \| undefined; }) => Href | Creates an href (string) to the location. If prependBasePath is true (default), it will prepend the location's path with the scoped history basePath. | -| [createSubHistory](./kibana-plugin-core-public.scopedhistory.createsubhistory.md) | | <SubHistoryLocationState = unknown>(basePath: string) => ScopedHistory<SubHistoryLocationState> | Creates a ScopedHistory for a subpath of this ScopedHistory. Useful for applications that may have sub-apps that do not need access to the containing application's history. | +| [createSubHistory](./kibana-plugin-core-public.scopedhistory.createsubhistory.md) | | (basePath: string) => ScopedHistory<HistoryLocationState> | Creates a ScopedHistory for a subpath of this ScopedHistory. Useful for applications that may have sub-apps that do not need access to the containing application's history. | | [go](./kibana-plugin-core-public.scopedhistory.go.md) | | (n: number) => void | Send the user forward or backwards in the history stack. | | [goBack](./kibana-plugin-core-public.scopedhistory.goback.md) | | () => void | Send the user one location back in the history stack. Equivalent to calling [ScopedHistory.go(-1)](./kibana-plugin-core-public.scopedhistory.go.md). If no more entries are available backwards, this is a no-op. | | [goForward](./kibana-plugin-core-public.scopedhistory.goforward.md) | | () => void | Send the user one location forward in the history stack. Equivalent to calling [ScopedHistory.go(1)](./kibana-plugin-core-public.scopedhistory.go.md). If no more entries are available forwards, this is a no-op. | diff --git a/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md b/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md index b2e2b4bc6003f..91cb6c370d759 100644 --- a/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md +++ b/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md @@ -12,14 +12,32 @@ Set of helpers used to create `KibanaResponse` to form HTTP response on an incom kibanaResponseFactory: { custom: | Error | Buffer | Stream | { message: string | Error; - attributes?: Record | undefined; + attributes?: ResponseErrorAttributes | undefined; } | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; - badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse; - unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse; - forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse; - notFound: (options?: ErrorHttpResponseOptions) => KibanaResponse; - conflict: (options?: ErrorHttpResponseOptions) => KibanaResponse; - customError: (options: CustomHttpResponseOptions) => KibanaResponse; + badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse; + unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse; + forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse; + notFound: (options?: ErrorHttpResponseOptions) => KibanaResponse; + conflict: (options?: ErrorHttpResponseOptions) => KibanaResponse; + customError: (options: CustomHttpResponseOptions) => KibanaResponse; redirected: (options: RedirectResponseOptions) => KibanaResponse | Buffer | Stream>; ok: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; accepted: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; diff --git a/package.json b/package.json index 75da2784a391b..06e338ab87ca7 100644 --- a/package.json +++ b/package.json @@ -88,9 +88,9 @@ "**/react-syntax-highlighter": "^15.3.1", "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", "**/trim": "1.0.1", - "**/typescript": "4.1.3", + "**/typescript": "4.3.5", "**/underscore": "^1.13.1", - "globby/fast-glob": "3.2.5" + "globby/fast-glob": "3.2.7" }, "dependencies": { "@babel/runtime": "^7.16.3", @@ -538,7 +538,7 @@ "@types/hapi__inert": "^5.2.3", "@types/has-ansi": "^3.0.0", "@types/he": "^1.1.1", - "@types/history": "^4.7.3", + "@types/history": "^4.7.9", "@types/hjson": "^2.4.2", "@types/http-proxy": "^1.17.4", "@types/http-proxy-agent": "^2.0.2", @@ -823,9 +823,9 @@ "terser-webpack-plugin": "^4.2.3", "tough-cookie": "^4.0.0", "ts-loader": "^7.0.5", - "ts-morph": "^9.1.0", + "ts-morph": "^11.0.0", "tsd": "^0.13.1", - "typescript": "4.1.3", + "typescript": "4.3.5", "unlazy-loader": "^0.1.3", "url-loader": "^2.2.0", "val-loader": "^1.1.1", diff --git a/packages/kbn-alerts/BUILD.bazel b/packages/kbn-alerts/BUILD.bazel index 15dbc163cd288..a6e5f167735c0 100644 --- a/packages/kbn-alerts/BUILD.bazel +++ b/packages/kbn-alerts/BUILD.bazel @@ -34,13 +34,11 @@ RUNTIME_DEPS = [ "@npm//@elastic/eui", "@npm//enzyme", "@npm//react", - "@npm//resize-observer-polyfill", ] TYPES_DEPS = [ "//packages/kbn-i18n:npm_module_types", "@npm//@elastic/eui", - "@npm//resize-observer-polyfill", "@npm//tslib", "@npm//@types/enzyme", "@npm//@types/jest", diff --git a/packages/kbn-alerts/tsconfig.json b/packages/kbn-alerts/tsconfig.json index fa18a40744354..ac523fb77a9e1 100644 --- a/packages/kbn-alerts/tsconfig.json +++ b/packages/kbn-alerts/tsconfig.json @@ -8,7 +8,7 @@ "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-alerts/src", - "types": ["jest", "node", "resize-observer-polyfill"] + "types": ["jest", "node"] }, "include": ["src/**/*"], } diff --git a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/buid_api_declaration.test.ts b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/buid_api_declaration.test.ts index 6697dfe53ee36..75c0bf4985b84 100644 --- a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/buid_api_declaration.test.ts +++ b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/buid_api_declaration.test.ts @@ -114,7 +114,8 @@ it('Function inside interface has a label', () => { expect(fn?.type).toBe(TypeKind.FunctionKind); }); -it('Test ReactElement signature', () => { +// FAILING: https://github.com/elastic/kibana/issues/120125 +it.skip('Test ReactElement signature', () => { const node = nodes.find((n) => getNodeName(n) === 'AReactElementFn'); expect(node).toBeDefined(); const def = buildApiDeclarationTopNode(node!, { diff --git a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_basic_api_declaration.ts b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_basic_api_declaration.ts index d9538467a0984..cb0928fa3763d 100644 --- a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_basic_api_declaration.ts +++ b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_basic_api_declaration.ts @@ -48,7 +48,7 @@ export function buildBasicApiDeclaration(node: Node, opts: BuildApiDecOpts): Api signature: getSignature(node, opts.plugins, opts.log), path: getSourceForNode(node), deprecated, - removeBy: removeByTag ? removeByTag.getComment() : undefined, + removeBy: removeByTag ? removeByTag.getCommentText() : undefined, }; return { ...apiDec, diff --git a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/js_doc_utils.ts b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/js_doc_utils.ts index 55639f16d7a97..7b6ac5a6ec5f6 100644 --- a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/js_doc_utils.ts +++ b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/js_doc_utils.ts @@ -43,14 +43,14 @@ export function getJSDocs(node: Node): JSDoc[] | undefined { export function getJSDocReturnTagComment(node: Node | JSDoc[]): TextWithLinks { const tags = getJSDocTags(node); const returnTag = tags.find((tag) => Node.isJSDocReturnTag(tag)); - if (returnTag) return getTextWithLinks(returnTag.getComment()); + if (returnTag) return getTextWithLinks(returnTag.getCommentText()); return []; } export function getJSDocParamComment(node: Node | JSDoc[], name: string): TextWithLinks { const tags = getJSDocTags(node); const paramTag = tags.find((tag) => Node.isJSDocParameterTag(tag) && tag.getName() === name); - if (paramTag) return getTextWithLinks(paramTag.getComment()); + if (paramTag) return getTextWithLinks(paramTag.getCommentText()); return []; } diff --git a/packages/kbn-docs-utils/src/api_docs/mdx/write_plugin_mdx_docs.ts b/packages/kbn-docs-utils/src/api_docs/mdx/write_plugin_mdx_docs.ts index aae77a7508954..fabe55d93c8ef 100644 --- a/packages/kbn-docs-utils/src/api_docs/mdx/write_plugin_mdx_docs.ts +++ b/packages/kbn-docs-utils/src/api_docs/mdx/write_plugin_mdx_docs.ts @@ -98,7 +98,7 @@ ${ **Code health stats** -| Public API count | Any count | Items lacking comments | Missing exports | +| Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| | ${pluginStats.apiCount} | ${pluginStats.isAnyType.length} | ${ pluginStats.missingComments.length diff --git a/packages/kbn-docs-utils/src/api_docs/tests/api_doc_suite.test.ts b/packages/kbn-docs-utils/src/api_docs/tests/api_doc_suite.test.ts index 7708eca0ec67a..cf536ce5158c1 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/api_doc_suite.test.ts +++ b/packages/kbn-docs-utils/src/api_docs/tests/api_doc_suite.test.ts @@ -330,7 +330,7 @@ describe('Types', () => { "section": "def-public.MyProps", "text": "MyProps", }, - ">", + ", string | React.JSXElementConstructor>", ] `); }); diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.json b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.json index 0eb9438ce594f..a3b3cdcbe28d0 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.json +++ b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.json @@ -76,7 +76,7 @@ "label": "component", "description": [], "signature": [ - "React.ComponentClass<{}, any> | React.FunctionComponent<{}> | undefined" + "React.ComponentType<{}> | undefined" ], "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/classes.ts", "deprecated": false @@ -1241,7 +1241,7 @@ "section": "def-public.MyProps", "text": "MyProps" }, - ">" + ", string | React.JSXElementConstructor>" ], "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/types.ts", "deprecated": false, @@ -2356,7 +2356,7 @@ "deprecated": false, "children": [], "returnComment": [ - "The currently selected {@link SearchLanguage}" + "The currently selected {@link SearchLanguage }" ] } ], diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.mdx b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.mdx index 44223783ada57..f6a7893fe5998 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.mdx +++ b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.mdx @@ -16,7 +16,7 @@ Contact Kibana Core for questions regarding this plugin. **Code health stats** -| Public API count | Any count | Items lacking comments | Missing exports | +| Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| | 131 | 1 | 71 | 2 | diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a_foo.mdx b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a_foo.mdx index b27d6d2de2f8f..13754ad452b01 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a_foo.mdx +++ b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a_foo.mdx @@ -16,7 +16,7 @@ Contact Kibana Core for questions regarding this plugin. **Code health stats** -| Public API count | Any count | Items lacking comments | Missing exports | +| Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| | 131 | 1 | 71 | 2 | diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_b.mdx b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_b.mdx index df0dec0054497..afc42a59ee96d 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_b.mdx +++ b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_b.mdx @@ -16,7 +16,7 @@ Contact Kibana Core for questions regarding this plugin. **Code health stats** -| Public API count | Any count | Items lacking comments | Missing exports | +| Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| | 2 | 0 | 2 | 0 | diff --git a/packages/kbn-field-types/BUILD.bazel b/packages/kbn-field-types/BUILD.bazel index 1fb97a5914ee4..0492829dd5320 100644 --- a/packages/kbn-field-types/BUILD.bazel +++ b/packages/kbn-field-types/BUILD.bazel @@ -38,7 +38,6 @@ TYPES_DEPS = [ "@npm//@types/node", "@npm//@types/node-forge", "@npm//@types/testing-library__jest-dom", - "@npm//resize-observer-polyfill", "@npm//@emotion/react", "@npm//jest-styled-components", ] diff --git a/packages/kbn-optimizer/BUILD.bazel b/packages/kbn-optimizer/BUILD.bazel index 485e5f1044aa3..647fcdfcbaad3 100644 --- a/packages/kbn-optimizer/BUILD.bazel +++ b/packages/kbn-optimizer/BUILD.bazel @@ -51,7 +51,6 @@ RUNTIME_DEPS = [ "@npm//node-sass", "@npm//normalize-path", "@npm//pirates", - "@npm//resize-observer-polyfill", "@npm//rxjs", "@npm//source-map-support", "@npm//watchpack", @@ -77,7 +76,6 @@ TYPES_DEPS = [ "@npm//jest-diff", "@npm//lmdb-store", "@npm//pirates", - "@npm//resize-observer-polyfill", "@npm//rxjs", "@npm//zlib", "@npm//@types/compression-webpack-plugin", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index ab8b9766f28d0..c1d0f69e4ea07 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,21 +94,21 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(569); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(563); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildBazelProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(346); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(340); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjects", function() { return _utils_projects__WEBPACK_IMPORTED_MODULE_2__["getProjects"]; }); -/* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(348); +/* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(342); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return _utils_project__WEBPACK_IMPORTED_MODULE_3__["Project"]; }); -/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(349); +/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(343); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "transformDependencies", function() { return _utils_package_json__WEBPACK_IMPORTED_MODULE_4__["transformDependencies"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(568); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(562); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -141,7 +141,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(129); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(563); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(557); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one @@ -8811,12 +8811,12 @@ exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "commands", function() { return commands; }); /* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(130); -/* harmony import */ var _build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(533); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(534); -/* harmony import */ var _reset__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(558); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(559); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(561); -/* harmony import */ var _patch_native_modules__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(562); +/* harmony import */ var _build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(527); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(528); +/* harmony import */ var _reset__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(552); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(553); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(555); +/* harmony import */ var _patch_native_modules__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(556); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -8855,11 +8855,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); /* harmony import */ var _utils_child_process__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(221); /* harmony import */ var _utils_link_project_executables__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(230); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(346); -/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(414); -/* harmony import */ var _utils_sort_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(417); -/* harmony import */ var _utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(425); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(427); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(340); +/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(408); +/* harmony import */ var _utils_sort_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(411); +/* harmony import */ var _utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(419); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(421); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -16552,7 +16552,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(132); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(345); +/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(339); /* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(ncp__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_4__); @@ -17966,12 +17966,12 @@ const {promisify} = __webpack_require__(113); const path = __webpack_require__(4); const globby = __webpack_require__(241); const isGlob = __webpack_require__(266); -const slash = __webpack_require__(336); +const slash = __webpack_require__(330); const gracefulFs = __webpack_require__(233); -const isPathCwd = __webpack_require__(338); -const isPathInside = __webpack_require__(339); -const rimraf = __webpack_require__(340); -const pMap = __webpack_require__(341); +const isPathCwd = __webpack_require__(332); +const isPathInside = __webpack_require__(333); +const rimraf = __webpack_require__(334); +const pMap = __webpack_require__(335); const rimrafP = promisify(rimraf); @@ -18095,9 +18095,9 @@ const arrayUnion = __webpack_require__(242); const merge2 = __webpack_require__(243); const glob = __webpack_require__(244); const fastGlob = __webpack_require__(257); -const dirGlob = __webpack_require__(332); -const gitignore = __webpack_require__(334); -const {FilterStream, UniqueStream} = __webpack_require__(337); +const dirGlob = __webpack_require__(326); +const gitignore = __webpack_require__(328); +const {FilterStream, UniqueStream} = __webpack_require__(331); const DEFAULT_FILTER = () => false; @@ -21708,73 +21708,73 @@ function slice (args) { /***/ (function(module, exports, __webpack_require__) { "use strict"; - -const taskManager = __webpack_require__(258); -const async_1 = __webpack_require__(293); -const stream_1 = __webpack_require__(328); -const sync_1 = __webpack_require__(329); -const settings_1 = __webpack_require__(331); -const utils = __webpack_require__(259); -async function FastGlob(source, options) { - assertPatternsInput(source); - const works = getWorks(source, async_1.default, options); - const result = await Promise.all(works); - return utils.array.flatten(result); -} -// https://github.com/typescript-eslint/typescript-eslint/issues/60 -// eslint-disable-next-line no-redeclare -(function (FastGlob) { - function sync(source, options) { - assertPatternsInput(source); - const works = getWorks(source, sync_1.default, options); - return utils.array.flatten(works); - } - FastGlob.sync = sync; - function stream(source, options) { - assertPatternsInput(source); - const works = getWorks(source, stream_1.default, options); - /** - * The stream returned by the provider cannot work with an asynchronous iterator. - * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. - * This affects performance (+25%). I don't see best solution right now. - */ - return utils.stream.merge(works); - } - FastGlob.stream = stream; - function generateTasks(source, options) { - assertPatternsInput(source); - const patterns = [].concat(source); - const settings = new settings_1.default(options); - return taskManager.generate(patterns, settings); - } - FastGlob.generateTasks = generateTasks; - function isDynamicPattern(source, options) { - assertPatternsInput(source); - const settings = new settings_1.default(options); - return utils.pattern.isDynamicPattern(source, settings); - } - FastGlob.isDynamicPattern = isDynamicPattern; - function escapePath(source) { - assertPatternsInput(source); - return utils.path.escape(source); - } - FastGlob.escapePath = escapePath; -})(FastGlob || (FastGlob = {})); -function getWorks(source, _Provider, options) { - const patterns = [].concat(source); - const settings = new settings_1.default(options); - const tasks = taskManager.generate(patterns, settings); - const provider = new _Provider(settings); - return tasks.map(provider.read, provider); -} -function assertPatternsInput(input) { - const source = [].concat(input); - const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); - if (!isValidSource) { - throw new TypeError('Patterns must be a string (non empty) or an array of strings'); - } -} -module.exports = FastGlob; + +const taskManager = __webpack_require__(258); +const async_1 = __webpack_require__(287); +const stream_1 = __webpack_require__(322); +const sync_1 = __webpack_require__(323); +const settings_1 = __webpack_require__(325); +const utils = __webpack_require__(259); +async function FastGlob(source, options) { + assertPatternsInput(source); + const works = getWorks(source, async_1.default, options); + const result = await Promise.all(works); + return utils.array.flatten(result); +} +// https://github.com/typescript-eslint/typescript-eslint/issues/60 +// eslint-disable-next-line no-redeclare +(function (FastGlob) { + function sync(source, options) { + assertPatternsInput(source); + const works = getWorks(source, sync_1.default, options); + return utils.array.flatten(works); + } + FastGlob.sync = sync; + function stream(source, options) { + assertPatternsInput(source); + const works = getWorks(source, stream_1.default, options); + /** + * The stream returned by the provider cannot work with an asynchronous iterator. + * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. + * This affects performance (+25%). I don't see best solution right now. + */ + return utils.stream.merge(works); + } + FastGlob.stream = stream; + function generateTasks(source, options) { + assertPatternsInput(source); + const patterns = [].concat(source); + const settings = new settings_1.default(options); + return taskManager.generate(patterns, settings); + } + FastGlob.generateTasks = generateTasks; + function isDynamicPattern(source, options) { + assertPatternsInput(source); + const settings = new settings_1.default(options); + return utils.pattern.isDynamicPattern(source, settings); + } + FastGlob.isDynamicPattern = isDynamicPattern; + function escapePath(source) { + assertPatternsInput(source); + return utils.path.escape(source); + } + FastGlob.escapePath = escapePath; +})(FastGlob || (FastGlob = {})); +function getWorks(source, _Provider, options) { + const patterns = [].concat(source); + const settings = new settings_1.default(options); + const tasks = taskManager.generate(patterns, settings); + const provider = new _Provider(settings); + return tasks.map(provider.read, provider); +} +function assertPatternsInput(input) { + const source = [].concat(input); + const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); + if (!isValidSource) { + throw new TypeError('Patterns must be a string (non empty) or an array of strings'); + } +} +module.exports = FastGlob; /***/ }), @@ -21782,71 +21782,86 @@ module.exports = FastGlob; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; -const utils = __webpack_require__(259); -function generate(patterns, settings) { - const positivePatterns = getPositivePatterns(patterns); - const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); - const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); - const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); - const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -exports.generate = generate; -function convertPatternsToTasks(positive, negative, dynamic) { - const positivePatternsGroup = groupPatternsByBaseDirectory(positive); - // When we have a global group – there is no reason to divide the patterns into independent tasks. - // In this case, the global task covers the rest. - if ('.' in positivePatternsGroup) { - const task = convertPatternGroupToTask('.', positive, negative, dynamic); - return [task]; - } - return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); -} -exports.convertPatternsToTasks = convertPatternsToTasks; -function getPositivePatterns(patterns) { - return utils.pattern.getPositivePatterns(patterns); -} -exports.getPositivePatterns = getPositivePatterns; -function getNegativePatternsAsPositive(patterns, ignore) { - const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); - const positive = negative.map(utils.pattern.convertToPositivePattern); - return positive; -} -exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -function groupPatternsByBaseDirectory(patterns) { - const group = {}; - return patterns.reduce((collection, pattern) => { - const base = utils.pattern.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, group); -} -exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map((base) => { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - dynamic, - positive, - negative, - base, - patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) - }; -} -exports.convertPatternGroupToTask = convertPatternGroupToTask; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; +const utils = __webpack_require__(259); +function generate(patterns, settings) { + const positivePatterns = getPositivePatterns(patterns); + const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); + const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); + const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); + const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); + const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); + return staticTasks.concat(dynamicTasks); +} +exports.generate = generate; +/** + * Returns tasks grouped by basic pattern directories. + * + * Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately. + * This is necessary because directory traversal starts at the base directory and goes deeper. + */ +function convertPatternsToTasks(positive, negative, dynamic) { + const tasks = []; + const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive); + const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive); + const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory); + const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory); + tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic)); + /* + * For the sake of reducing future accesses to the file system, we merge all tasks within the current directory + * into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest. + */ + if ('.' in insideCurrentDirectoryGroup) { + tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic)); + } + else { + tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic)); + } + return tasks; +} +exports.convertPatternsToTasks = convertPatternsToTasks; +function getPositivePatterns(patterns) { + return utils.pattern.getPositivePatterns(patterns); +} +exports.getPositivePatterns = getPositivePatterns; +function getNegativePatternsAsPositive(patterns, ignore) { + const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); + const positive = negative.map(utils.pattern.convertToPositivePattern); + return positive; +} +exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; +function groupPatternsByBaseDirectory(patterns) { + const group = {}; + return patterns.reduce((collection, pattern) => { + const base = utils.pattern.getBaseDirectory(pattern); + if (base in collection) { + collection[base].push(pattern); + } + else { + collection[base] = [pattern]; + } + return collection; + }, group); +} +exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; +function convertPatternGroupsToTasks(positive, negative, dynamic) { + return Object.keys(positive).map((base) => { + return convertPatternGroupToTask(base, positive[base], negative, dynamic); + }); +} +exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; +function convertPatternGroupToTask(base, positive, negative, dynamic) { + return { + dynamic, + positive, + negative, + base, + patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) + }; +} +exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), @@ -21854,23 +21869,23 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; -const array = __webpack_require__(260); -exports.array = array; -const errno = __webpack_require__(261); -exports.errno = errno; -const fs = __webpack_require__(262); -exports.fs = fs; -const path = __webpack_require__(263); -exports.path = path; -const pattern = __webpack_require__(264); -exports.pattern = pattern; -const stream = __webpack_require__(291); -exports.stream = stream; -const string = __webpack_require__(292); -exports.string = string; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; +const array = __webpack_require__(260); +exports.array = array; +const errno = __webpack_require__(261); +exports.errno = errno; +const fs = __webpack_require__(262); +exports.fs = fs; +const path = __webpack_require__(263); +exports.path = path; +const pattern = __webpack_require__(264); +exports.pattern = pattern; +const stream = __webpack_require__(285); +exports.stream = stream; +const string = __webpack_require__(286); +exports.string = string; /***/ }), @@ -21878,28 +21893,28 @@ exports.string = string; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.splitWhen = exports.flatten = void 0; -function flatten(items) { - return items.reduce((collection, item) => [].concat(collection, item), []); -} -exports.flatten = flatten; -function splitWhen(items, predicate) { - const result = [[]]; - let groupIndex = 0; - for (const item of items) { - if (predicate(item)) { - groupIndex++; - result[groupIndex] = []; - } - else { - result[groupIndex].push(item); - } - } - return result; -} -exports.splitWhen = splitWhen; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.splitWhen = exports.flatten = void 0; +function flatten(items) { + return items.reduce((collection, item) => [].concat(collection, item), []); +} +exports.flatten = flatten; +function splitWhen(items, predicate) { + const result = [[]]; + let groupIndex = 0; + for (const item of items) { + if (predicate(item)) { + groupIndex++; + result[groupIndex] = []; + } + else { + result[groupIndex].push(item); + } + } + return result; +} +exports.splitWhen = splitWhen; /***/ }), @@ -21907,13 +21922,13 @@ exports.splitWhen = splitWhen; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEnoentCodeError = void 0; -function isEnoentCodeError(error) { - return error.code === 'ENOENT'; -} -exports.isEnoentCodeError = isEnoentCodeError; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEnoentCodeError = void 0; +function isEnoentCodeError(error) { + return error.code === 'ENOENT'; +} +exports.isEnoentCodeError = isEnoentCodeError; /***/ }), @@ -21921,25 +21936,25 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createDirentFromStats = void 0; -class DirentFromStats { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats(name, stats) { - return new DirentFromStats(name, stats); -} -exports.createDirentFromStats = createDirentFromStats; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createDirentFromStats = void 0; +class DirentFromStats { + constructor(name, stats) { + this.name = name; + this.isBlockDevice = stats.isBlockDevice.bind(stats); + this.isCharacterDevice = stats.isCharacterDevice.bind(stats); + this.isDirectory = stats.isDirectory.bind(stats); + this.isFIFO = stats.isFIFO.bind(stats); + this.isFile = stats.isFile.bind(stats); + this.isSocket = stats.isSocket.bind(stats); + this.isSymbolicLink = stats.isSymbolicLink.bind(stats); + } +} +function createDirentFromStats(name, stats) { + return new DirentFromStats(name, stats); +} +exports.createDirentFromStats = createDirentFromStats; /***/ }), @@ -21947,39 +21962,39 @@ exports.createDirentFromStats = createDirentFromStats; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; -const path = __webpack_require__(4); -const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ -const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; -/** - * Designed to work only with simple paths: `dir\\file`. - */ -function unixify(filepath) { - return filepath.replace(/\\/g, '/'); -} -exports.unixify = unixify; -function makeAbsolute(cwd, filepath) { - return path.resolve(cwd, filepath); -} -exports.makeAbsolute = makeAbsolute; -function escape(pattern) { - return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); -} -exports.escape = escape; -function removeLeadingDotSegment(entry) { - // We do not use `startsWith` because this is 10x slower than current implementation for some cases. - // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with - if (entry.charAt(0) === '.') { - const secondCharactery = entry.charAt(1); - if (secondCharactery === '/' || secondCharactery === '\\') { - return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); - } - } - return entry; -} -exports.removeLeadingDotSegment = removeLeadingDotSegment; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; +const path = __webpack_require__(4); +const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ +const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; +/** + * Designed to work only with simple paths: `dir\\file`. + */ +function unixify(filepath) { + return filepath.replace(/\\/g, '/'); +} +exports.unixify = unixify; +function makeAbsolute(cwd, filepath) { + return path.resolve(cwd, filepath); +} +exports.makeAbsolute = makeAbsolute; +function escape(pattern) { + return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); +} +exports.escape = escape; +function removeLeadingDotSegment(entry) { + // We do not use `startsWith` because this is 10x slower than current implementation for some cases. + // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with + if (entry.charAt(0) === '.') { + const secondCharactery = entry.charAt(1); + if (secondCharactery === '/' || secondCharactery === '\\') { + return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); + } + } + return entry; +} +exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), @@ -21987,138 +22002,163 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; -const path = __webpack_require__(4); -const globParent = __webpack_require__(265); -const micromatch = __webpack_require__(268); -const picomatch = __webpack_require__(285); -const GLOBSTAR = '**'; -const ESCAPE_SYMBOL = '\\'; -const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; -const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; -const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; -const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; -const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; -function isStaticPattern(pattern, options = {}) { - return !isDynamicPattern(pattern, options); -} -exports.isStaticPattern = isStaticPattern; -function isDynamicPattern(pattern, options = {}) { - /** - * A special case with an empty string is necessary for matching patterns that start with a forward slash. - * An empty string cannot be a dynamic pattern. - * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. - */ - if (pattern === '') { - return false; - } - /** - * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check - * filepath directly (without read directory). - */ - if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { - return true; - } - if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { - return true; - } - return false; -} -exports.isDynamicPattern = isDynamicPattern; -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -exports.convertToPositivePattern = convertToPositivePattern; -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -exports.convertToNegativePattern = convertToNegativePattern; -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -exports.isNegativePattern = isNegativePattern; -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -exports.isPositivePattern = isPositivePattern; -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -exports.getNegativePatterns = getNegativePatterns; -function getPositivePatterns(patterns) { - return patterns.filter(isPositivePattern); -} -exports.getPositivePatterns = getPositivePatterns; -function getBaseDirectory(pattern) { - return globParent(pattern, { flipBackslashes: false }); -} -exports.getBaseDirectory = getBaseDirectory; -function hasGlobStar(pattern) { - return pattern.includes(GLOBSTAR); -} -exports.hasGlobStar = hasGlobStar; -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR); -} -exports.endsWithSlashGlobStar = endsWithSlashGlobStar; -function isAffectDepthOfReadingPattern(pattern) { - const basename = path.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -function expandPatternsWithBraceExpansion(patterns) { - return patterns.reduce((collection, pattern) => { - return collection.concat(expandBraceExpansion(pattern)); - }, []); -} -exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; -function expandBraceExpansion(pattern) { - return micromatch.braces(pattern, { - expand: true, - nodupes: true - }); -} -exports.expandBraceExpansion = expandBraceExpansion; -function getPatternParts(pattern, options) { - let { parts } = picomatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); - /** - * The scan method returns an empty array in some cases. - * See micromatch/picomatch#58 for more details. - */ - if (parts.length === 0) { - parts = [pattern]; - } - /** - * The scan method does not return an empty part for the pattern with a forward slash. - * This is another part of micromatch/picomatch#58. - */ - if (parts[0].startsWith('/')) { - parts[0] = parts[0].slice(1); - parts.unshift(''); - } - return parts; -} -exports.getPatternParts = getPatternParts; -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -exports.makeRe = makeRe; -function convertPatternsToRe(patterns, options) { - return patterns.map((pattern) => makeRe(pattern, options)); -} -exports.convertPatternsToRe = convertPatternsToRe; -function matchAny(entry, patternsRe) { - return patternsRe.some((patternRe) => patternRe.test(entry)); -} -exports.matchAny = matchAny; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; +const path = __webpack_require__(4); +const globParent = __webpack_require__(265); +const micromatch = __webpack_require__(268); +const GLOBSTAR = '**'; +const ESCAPE_SYMBOL = '\\'; +const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; +const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; +const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; +const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; +const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; +function isStaticPattern(pattern, options = {}) { + return !isDynamicPattern(pattern, options); +} +exports.isStaticPattern = isStaticPattern; +function isDynamicPattern(pattern, options = {}) { + /** + * A special case with an empty string is necessary for matching patterns that start with a forward slash. + * An empty string cannot be a dynamic pattern. + * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. + */ + if (pattern === '') { + return false; + } + /** + * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check + * filepath directly (without read directory). + */ + if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { + return true; + } + if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { + return true; + } + return false; +} +exports.isDynamicPattern = isDynamicPattern; +function convertToPositivePattern(pattern) { + return isNegativePattern(pattern) ? pattern.slice(1) : pattern; +} +exports.convertToPositivePattern = convertToPositivePattern; +function convertToNegativePattern(pattern) { + return '!' + pattern; +} +exports.convertToNegativePattern = convertToNegativePattern; +function isNegativePattern(pattern) { + return pattern.startsWith('!') && pattern[1] !== '('; +} +exports.isNegativePattern = isNegativePattern; +function isPositivePattern(pattern) { + return !isNegativePattern(pattern); +} +exports.isPositivePattern = isPositivePattern; +function getNegativePatterns(patterns) { + return patterns.filter(isNegativePattern); +} +exports.getNegativePatterns = getNegativePatterns; +function getPositivePatterns(patterns) { + return patterns.filter(isPositivePattern); +} +exports.getPositivePatterns = getPositivePatterns; +/** + * Returns patterns that can be applied inside the current directory. + * + * @example + * // ['./*', '*', 'a/*'] + * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) + */ +function getPatternsInsideCurrentDirectory(patterns) { + return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern)); +} +exports.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory; +/** + * Returns patterns to be expanded relative to (outside) the current directory. + * + * @example + * // ['../*', './../*'] + * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) + */ +function getPatternsOutsideCurrentDirectory(patterns) { + return patterns.filter(isPatternRelatedToParentDirectory); +} +exports.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory; +function isPatternRelatedToParentDirectory(pattern) { + return pattern.startsWith('..') || pattern.startsWith('./..'); +} +exports.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory; +function getBaseDirectory(pattern) { + return globParent(pattern, { flipBackslashes: false }); +} +exports.getBaseDirectory = getBaseDirectory; +function hasGlobStar(pattern) { + return pattern.includes(GLOBSTAR); +} +exports.hasGlobStar = hasGlobStar; +function endsWithSlashGlobStar(pattern) { + return pattern.endsWith('/' + GLOBSTAR); +} +exports.endsWithSlashGlobStar = endsWithSlashGlobStar; +function isAffectDepthOfReadingPattern(pattern) { + const basename = path.basename(pattern); + return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); +} +exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; +function expandPatternsWithBraceExpansion(patterns) { + return patterns.reduce((collection, pattern) => { + return collection.concat(expandBraceExpansion(pattern)); + }, []); +} +exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; +function expandBraceExpansion(pattern) { + return micromatch.braces(pattern, { + expand: true, + nodupes: true + }); +} +exports.expandBraceExpansion = expandBraceExpansion; +function getPatternParts(pattern, options) { + let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); + /** + * The scan method returns an empty array in some cases. + * See micromatch/picomatch#58 for more details. + */ + if (parts.length === 0) { + parts = [pattern]; + } + /** + * The scan method does not return an empty part for the pattern with a forward slash. + * This is another part of micromatch/picomatch#58. + */ + if (parts[0].startsWith('/')) { + parts[0] = parts[0].slice(1); + parts.unshift(''); + } + return parts; +} +exports.getPatternParts = getPatternParts; +function makeRe(pattern, options) { + return micromatch.makeRe(pattern, options); +} +exports.makeRe = makeRe; +function convertPatternsToRe(patterns, options) { + return patterns.map((pattern) => makeRe(pattern, options)); +} +exports.convertPatternsToRe = convertPatternsToRe; +function matchAny(entry, patternsRe) { + return patternsRe.some((patternRe) => patternRe.test(entry)); +} +exports.matchAny = matchAny; /***/ }), @@ -26436,2237 +26476,149 @@ module.exports = parse; "use strict"; - -module.exports = __webpack_require__(286); - - -/***/ }), -/* 286 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const path = __webpack_require__(4); -const scan = __webpack_require__(287); -const parse = __webpack_require__(290); -const utils = __webpack_require__(288); -const constants = __webpack_require__(289); -const isObject = val => val && typeof val === 'object' && !Array.isArray(val); - -/** - * Creates a matcher function from one or more glob patterns. The - * returned function takes a string to match as its first argument, - * and returns true if the string is a match. The returned matcher - * function also takes a boolean as the second argument that, when true, - * returns an object with additional information. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch(glob[, options]); - * - * const isMatch = picomatch('*.!(*a)'); - * console.log(isMatch('a.a')); //=> false - * console.log(isMatch('a.b')); //=> true - * ``` - * @name picomatch - * @param {String|Array} `globs` One or more glob patterns. - * @param {Object=} `options` - * @return {Function=} Returns a matcher function. - * @api public - */ - -const picomatch = (glob, options, returnState = false) => { - if (Array.isArray(glob)) { - const fns = glob.map(input => picomatch(input, options, returnState)); - const arrayMatcher = str => { - for (const isMatch of fns) { - const state = isMatch(str); - if (state) return state; - } - return false; - }; - return arrayMatcher; - } - - const isState = isObject(glob) && glob.tokens && glob.input; - - if (glob === '' || (typeof glob !== 'string' && !isState)) { - throw new TypeError('Expected pattern to be a non-empty string'); - } - - const opts = options || {}; - const posix = utils.isWindows(options); - const regex = isState - ? picomatch.compileRe(glob, options) - : picomatch.makeRe(glob, options, false, true); - - const state = regex.state; - delete regex.state; - - let isIgnored = () => false; - if (opts.ignore) { - const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; - isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); - } - - const matcher = (input, returnObject = false) => { - const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); - const result = { glob, state, regex, posix, input, output, match, isMatch }; - - if (typeof opts.onResult === 'function') { - opts.onResult(result); - } - - if (isMatch === false) { - result.isMatch = false; - return returnObject ? result : false; - } - - if (isIgnored(input)) { - if (typeof opts.onIgnore === 'function') { - opts.onIgnore(result); - } - result.isMatch = false; - return returnObject ? result : false; - } - - if (typeof opts.onMatch === 'function') { - opts.onMatch(result); - } - return returnObject ? result : true; - }; - - if (returnState) { - matcher.state = state; - } - - return matcher; -}; - -/** - * Test `input` with the given `regex`. This is used by the main - * `picomatch()` function to test the input string. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.test(input, regex[, options]); - * - * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/)); - * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' } - * ``` - * @param {String} `input` String to test. - * @param {RegExp} `regex` - * @return {Object} Returns an object with matching info. - * @api public - */ - -picomatch.test = (input, regex, options, { glob, posix } = {}) => { - if (typeof input !== 'string') { - throw new TypeError('Expected input to be a string'); - } - - if (input === '') { - return { isMatch: false, output: '' }; - } - - const opts = options || {}; - const format = opts.format || (posix ? utils.toPosixSlashes : null); - let match = input === glob; - let output = (match && format) ? format(input) : input; - - if (match === false) { - output = format ? format(input) : input; - match = output === glob; - } - - if (match === false || opts.capture === true) { - if (opts.matchBase === true || opts.basename === true) { - match = picomatch.matchBase(input, regex, options, posix); - } else { - match = regex.exec(output); - } - } - - return { isMatch: Boolean(match), match, output }; -}; - -/** - * Match the basename of a filepath. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.matchBase(input, glob[, options]); - * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true - * ``` - * @param {String} `input` String to test. - * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe). - * @return {Boolean} - * @api public - */ - -picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { - const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); - return regex.test(path.basename(input)); -}; - -/** - * Returns true if **any** of the given glob `patterns` match the specified `string`. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.isMatch(string, patterns[, options]); - * - * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true - * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false - * ``` - * @param {String|Array} str The string to test. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} [options] See available [options](#options). - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ - -picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); - -/** - * Parse a glob pattern to create the source string for a regular - * expression. - * - * ```js - * const picomatch = require('picomatch'); - * const result = picomatch.parse(pattern[, options]); - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} Returns an object with useful properties and output to be used as a regex source string. - * @api public - */ - -picomatch.parse = (pattern, options) => { - if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options)); - return parse(pattern, { ...options, fastpaths: false }); -}; - -/** - * Scan a glob pattern to separate the pattern into segments. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.scan(input[, options]); - * - * const result = picomatch.scan('!./foo/*.js'); - * console.log(result); - * { prefix: '!./', - * input: '!./foo/*.js', - * start: 3, - * base: 'foo', - * glob: '*.js', - * isBrace: false, - * isBracket: false, - * isGlob: true, - * isExtglob: false, - * isGlobstar: false, - * negated: true } - * ``` - * @param {String} `input` Glob pattern to scan. - * @param {Object} `options` - * @return {Object} Returns an object with - * @api public - */ - -picomatch.scan = (input, options) => scan(input, options); - -/** - * Create a regular expression from a parsed glob pattern. - * - * ```js - * const picomatch = require('picomatch'); - * const state = picomatch.parse('*.js'); - * // picomatch.compileRe(state[, options]); - * - * console.log(picomatch.compileRe(state)); - * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ - * ``` - * @param {String} `state` The object returned from the `.parse` method. - * @param {Object} `options` - * @return {RegExp} Returns a regex created from the given pattern. - * @api public - */ - -picomatch.compileRe = (parsed, options, returnOutput = false, returnState = false) => { - if (returnOutput === true) { - return parsed.output; - } - - const opts = options || {}; - const prepend = opts.contains ? '' : '^'; - const append = opts.contains ? '' : '$'; - - let source = `${prepend}(?:${parsed.output})${append}`; - if (parsed && parsed.negated === true) { - source = `^(?!${source}).*$`; - } - - const regex = picomatch.toRegex(source, options); - if (returnState === true) { - regex.state = parsed; - } - - return regex; -}; - -picomatch.makeRe = (input, options, returnOutput = false, returnState = false) => { - if (!input || typeof input !== 'string') { - throw new TypeError('Expected a non-empty string'); - } - - const opts = options || {}; - let parsed = { negated: false, fastpaths: true }; - let prefix = ''; - let output; - - if (input.startsWith('./')) { - input = input.slice(2); - prefix = parsed.prefix = './'; - } - - if (opts.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { - output = parse.fastpaths(input, options); - } - - if (output === undefined) { - parsed = parse(input, options); - parsed.prefix = prefix + (parsed.prefix || ''); - } else { - parsed.output = output; - } - - return picomatch.compileRe(parsed, options, returnOutput, returnState); -}; - -/** - * Create a regular expression from the given regex source string. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.toRegex(source[, options]); - * - * const { output } = picomatch.parse('*.js'); - * console.log(picomatch.toRegex(output)); - * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ - * ``` - * @param {String} `source` Regular expression source string. - * @param {Object} `options` - * @return {RegExp} - * @api public - */ - -picomatch.toRegex = (source, options) => { - try { - const opts = options || {}; - return new RegExp(source, opts.flags || (opts.nocase ? 'i' : '')); - } catch (err) { - if (options && options.debug === true) throw err; - return /$^/; - } -}; - -/** - * Picomatch constants. - * @return {Object} - */ - -picomatch.constants = constants; - -/** - * Expose "picomatch" - */ - -module.exports = picomatch; - - -/***/ }), -/* 287 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const utils = __webpack_require__(288); -const { - CHAR_ASTERISK, /* * */ - CHAR_AT, /* @ */ - CHAR_BACKWARD_SLASH, /* \ */ - CHAR_COMMA, /* , */ - CHAR_DOT, /* . */ - CHAR_EXCLAMATION_MARK, /* ! */ - CHAR_FORWARD_SLASH, /* / */ - CHAR_LEFT_CURLY_BRACE, /* { */ - CHAR_LEFT_PARENTHESES, /* ( */ - CHAR_LEFT_SQUARE_BRACKET, /* [ */ - CHAR_PLUS, /* + */ - CHAR_QUESTION_MARK, /* ? */ - CHAR_RIGHT_CURLY_BRACE, /* } */ - CHAR_RIGHT_PARENTHESES, /* ) */ - CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(289); - -const isPathSeparator = code => { - return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; -}; - -const depth = token => { - if (token.isPrefix !== true) { - token.depth = token.isGlobstar ? Infinity : 1; - } -}; - -/** - * Quickly scans a glob pattern and returns an object with a handful of - * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), - * `glob` (the actual pattern), and `negated` (true if the path starts with `!`). - * - * ```js - * const pm = require('picomatch'); - * console.log(pm.scan('foo/bar/*.js')); - * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {Object} Returns an object with tokens and regex source string. - * @api public - */ - -const scan = (input, options) => { - const opts = options || {}; - - const length = input.length - 1; - const scanToEnd = opts.parts === true || opts.scanToEnd === true; - const slashes = []; - const tokens = []; - const parts = []; - - let str = input; - let index = -1; - let start = 0; - let lastIndex = 0; - let isBrace = false; - let isBracket = false; - let isGlob = false; - let isExtglob = false; - let isGlobstar = false; - let braceEscaped = false; - let backslashes = false; - let negated = false; - let finished = false; - let braces = 0; - let prev; - let code; - let token = { value: '', depth: 0, isGlob: false }; - - const eos = () => index >= length; - const peek = () => str.charCodeAt(index + 1); - const advance = () => { - prev = code; - return str.charCodeAt(++index); - }; - - while (index < length) { - code = advance(); - let next; - - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - code = advance(); - - if (code === CHAR_LEFT_CURLY_BRACE) { - braceEscaped = true; - } - continue; - } - - if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { - braces++; - - while (eos() !== true && (code = advance())) { - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - advance(); - continue; - } - - if (code === CHAR_LEFT_CURLY_BRACE) { - braces++; - continue; - } - - if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { - isBrace = token.isBrace = true; - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - - break; - } - - if (braceEscaped !== true && code === CHAR_COMMA) { - isBrace = token.isBrace = true; - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - - break; - } - - if (code === CHAR_RIGHT_CURLY_BRACE) { - braces--; - - if (braces === 0) { - braceEscaped = false; - isBrace = token.isBrace = true; - finished = true; - break; - } - } - } - - if (scanToEnd === true) { - continue; - } - - break; - } - - if (code === CHAR_FORWARD_SLASH) { - slashes.push(index); - tokens.push(token); - token = { value: '', depth: 0, isGlob: false }; - - if (finished === true) continue; - if (prev === CHAR_DOT && index === (start + 1)) { - start += 2; - continue; - } - - lastIndex = index + 1; - continue; - } - - if (opts.noext !== true) { - const isExtglobChar = code === CHAR_PLUS - || code === CHAR_AT - || code === CHAR_ASTERISK - || code === CHAR_QUESTION_MARK - || code === CHAR_EXCLAMATION_MARK; - - if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { - isGlob = token.isGlob = true; - isExtglob = token.isExtglob = true; - finished = true; - - if (scanToEnd === true) { - while (eos() !== true && (code = advance())) { - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - code = advance(); - continue; - } - - if (code === CHAR_RIGHT_PARENTHESES) { - isGlob = token.isGlob = true; - finished = true; - break; - } - } - continue; - } - break; - } - } - - if (code === CHAR_ASTERISK) { - if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - break; - } - - if (code === CHAR_QUESTION_MARK) { - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - break; - } - - if (code === CHAR_LEFT_SQUARE_BRACKET) { - while (eos() !== true && (next = advance())) { - if (next === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - advance(); - continue; - } - - if (next === CHAR_RIGHT_SQUARE_BRACKET) { - isBracket = token.isBracket = true; - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - break; - } - } - } - - if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { - negated = token.negated = true; - start++; - continue; - } - - if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { - isGlob = token.isGlob = true; - - if (scanToEnd === true) { - while (eos() !== true && (code = advance())) { - if (code === CHAR_LEFT_PARENTHESES) { - backslashes = token.backslashes = true; - code = advance(); - continue; - } - - if (code === CHAR_RIGHT_PARENTHESES) { - finished = true; - break; - } - } - continue; - } - break; - } - - if (isGlob === true) { - finished = true; - - if (scanToEnd === true) { - continue; - } - - break; - } - } - - if (opts.noext === true) { - isExtglob = false; - isGlob = false; - } - - let base = str; - let prefix = ''; - let glob = ''; - - if (start > 0) { - prefix = str.slice(0, start); - str = str.slice(start); - lastIndex -= start; - } - - if (base && isGlob === true && lastIndex > 0) { - base = str.slice(0, lastIndex); - glob = str.slice(lastIndex); - } else if (isGlob === true) { - base = ''; - glob = str; - } else { - base = str; - } - - if (base && base !== '' && base !== '/' && base !== str) { - if (isPathSeparator(base.charCodeAt(base.length - 1))) { - base = base.slice(0, -1); - } - } - - if (opts.unescape === true) { - if (glob) glob = utils.removeBackslashes(glob); - - if (base && backslashes === true) { - base = utils.removeBackslashes(base); - } - } - - const state = { - prefix, - input, - start, - base, - glob, - isBrace, - isBracket, - isGlob, - isExtglob, - isGlobstar, - negated - }; - - if (opts.tokens === true) { - state.maxDepth = 0; - if (!isPathSeparator(code)) { - tokens.push(token); - } - state.tokens = tokens; - } - - if (opts.parts === true || opts.tokens === true) { - let prevIndex; - - for (let idx = 0; idx < slashes.length; idx++) { - const n = prevIndex ? prevIndex + 1 : start; - const i = slashes[idx]; - const value = input.slice(n, i); - if (opts.tokens) { - if (idx === 0 && start !== 0) { - tokens[idx].isPrefix = true; - tokens[idx].value = prefix; - } else { - tokens[idx].value = value; - } - depth(tokens[idx]); - state.maxDepth += tokens[idx].depth; - } - if (idx !== 0 || value !== '') { - parts.push(value); - } - prevIndex = i; - } - - if (prevIndex && prevIndex + 1 < input.length) { - const value = input.slice(prevIndex + 1); - parts.push(value); - - if (opts.tokens) { - tokens[tokens.length - 1].value = value; - depth(tokens[tokens.length - 1]); - state.maxDepth += tokens[tokens.length - 1].depth; - } - } - - state.slashes = slashes; - state.parts = parts; - } - - return state; -}; - -module.exports = scan; - - -/***/ }), -/* 288 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const path = __webpack_require__(4); -const win32 = process.platform === 'win32'; -const { - REGEX_BACKSLASH, - REGEX_REMOVE_BACKSLASH, - REGEX_SPECIAL_CHARS, - REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(289); - -exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); -exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); -exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); -exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); -exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); - -exports.removeBackslashes = str => { - return str.replace(REGEX_REMOVE_BACKSLASH, match => { - return match === '\\' ? '' : match; - }); -}; - -exports.supportsLookbehinds = () => { - const segs = process.version.slice(1).split('.').map(Number); - if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { - return true; - } - return false; -}; - -exports.isWindows = options => { - if (options && typeof options.windows === 'boolean') { - return options.windows; - } - return win32 === true || path.sep === '\\'; -}; - -exports.escapeLast = (input, char, lastIdx) => { - const idx = input.lastIndexOf(char, lastIdx); - if (idx === -1) return input; - if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1); - return `${input.slice(0, idx)}\\${input.slice(idx)}`; -}; - -exports.removePrefix = (input, state = {}) => { - let output = input; - if (output.startsWith('./')) { - output = output.slice(2); - state.prefix = './'; - } - return output; -}; - -exports.wrapOutput = (input, state = {}, options = {}) => { - const prepend = options.contains ? '' : '^'; - const append = options.contains ? '' : '$'; - - let output = `${prepend}(?:${input})${append}`; - if (state.negated === true) { - output = `(?:^(?!${output}).*$)`; - } - return output; -}; - - -/***/ }), -/* 289 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const path = __webpack_require__(4); -const WIN_SLASH = '\\\\/'; -const WIN_NO_SLASH = `[^${WIN_SLASH}]`; - -/** - * Posix glob regex - */ - -const DOT_LITERAL = '\\.'; -const PLUS_LITERAL = '\\+'; -const QMARK_LITERAL = '\\?'; -const SLASH_LITERAL = '\\/'; -const ONE_CHAR = '(?=.)'; -const QMARK = '[^/]'; -const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; -const START_ANCHOR = `(?:^|${SLASH_LITERAL})`; -const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; -const NO_DOT = `(?!${DOT_LITERAL})`; -const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; -const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; -const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; -const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; -const STAR = `${QMARK}*?`; - -const POSIX_CHARS = { - DOT_LITERAL, - PLUS_LITERAL, - QMARK_LITERAL, - SLASH_LITERAL, - ONE_CHAR, - QMARK, - END_ANCHOR, - DOTS_SLASH, - NO_DOT, - NO_DOTS, - NO_DOT_SLASH, - NO_DOTS_SLASH, - QMARK_NO_DOT, - STAR, - START_ANCHOR -}; - -/** - * Windows glob regex - */ - -const WINDOWS_CHARS = { - ...POSIX_CHARS, - - SLASH_LITERAL: `[${WIN_SLASH}]`, - QMARK: WIN_NO_SLASH, - STAR: `${WIN_NO_SLASH}*?`, - DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, - NO_DOT: `(?!${DOT_LITERAL})`, - NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, - NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, - NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, - QMARK_NO_DOT: `[^.${WIN_SLASH}]`, - START_ANCHOR: `(?:^|[${WIN_SLASH}])`, - END_ANCHOR: `(?:[${WIN_SLASH}]|$)` -}; - -/** - * POSIX Bracket Regex - */ - -const POSIX_REGEX_SOURCE = { - alnum: 'a-zA-Z0-9', - alpha: 'a-zA-Z', - ascii: '\\x00-\\x7F', - blank: ' \\t', - cntrl: '\\x00-\\x1F\\x7F', - digit: '0-9', - graph: '\\x21-\\x7E', - lower: 'a-z', - print: '\\x20-\\x7E ', - punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', - space: ' \\t\\r\\n\\v\\f', - upper: 'A-Z', - word: 'A-Za-z0-9_', - xdigit: 'A-Fa-f0-9' -}; - -module.exports = { - MAX_LENGTH: 1024 * 64, - POSIX_REGEX_SOURCE, - - // regular expressions - REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, - REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, - REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, - REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, - REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, - REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, - - // Replace globs with equivalent patterns to reduce parsing time. - REPLACEMENTS: { - '***': '*', - '**/**': '**', - '**/**/**': '**' - }, - - // Digits - CHAR_0: 48, /* 0 */ - CHAR_9: 57, /* 9 */ - - // Alphabet chars. - CHAR_UPPERCASE_A: 65, /* A */ - CHAR_LOWERCASE_A: 97, /* a */ - CHAR_UPPERCASE_Z: 90, /* Z */ - CHAR_LOWERCASE_Z: 122, /* z */ - - CHAR_LEFT_PARENTHESES: 40, /* ( */ - CHAR_RIGHT_PARENTHESES: 41, /* ) */ - - CHAR_ASTERISK: 42, /* * */ - - // Non-alphabetic chars. - CHAR_AMPERSAND: 38, /* & */ - CHAR_AT: 64, /* @ */ - CHAR_BACKWARD_SLASH: 92, /* \ */ - CHAR_CARRIAGE_RETURN: 13, /* \r */ - CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */ - CHAR_COLON: 58, /* : */ - CHAR_COMMA: 44, /* , */ - CHAR_DOT: 46, /* . */ - CHAR_DOUBLE_QUOTE: 34, /* " */ - CHAR_EQUAL: 61, /* = */ - CHAR_EXCLAMATION_MARK: 33, /* ! */ - CHAR_FORM_FEED: 12, /* \f */ - CHAR_FORWARD_SLASH: 47, /* / */ - CHAR_GRAVE_ACCENT: 96, /* ` */ - CHAR_HASH: 35, /* # */ - CHAR_HYPHEN_MINUS: 45, /* - */ - CHAR_LEFT_ANGLE_BRACKET: 60, /* < */ - CHAR_LEFT_CURLY_BRACE: 123, /* { */ - CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */ - CHAR_LINE_FEED: 10, /* \n */ - CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */ - CHAR_PERCENT: 37, /* % */ - CHAR_PLUS: 43, /* + */ - CHAR_QUESTION_MARK: 63, /* ? */ - CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */ - CHAR_RIGHT_CURLY_BRACE: 125, /* } */ - CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */ - CHAR_SEMICOLON: 59, /* ; */ - CHAR_SINGLE_QUOTE: 39, /* ' */ - CHAR_SPACE: 32, /* */ - CHAR_TAB: 9, /* \t */ - CHAR_UNDERSCORE: 95, /* _ */ - CHAR_VERTICAL_LINE: 124, /* | */ - CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */ - - SEP: path.sep, - - /** - * Create EXTGLOB_CHARS - */ - - extglobChars(chars) { - return { - '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` }, - '?': { type: 'qmark', open: '(?:', close: ')?' }, - '+': { type: 'plus', open: '(?:', close: ')+' }, - '*': { type: 'star', open: '(?:', close: ')*' }, - '@': { type: 'at', open: '(?:', close: ')' } - }; - }, - - /** - * Create GLOB_CHARS - */ - - globChars(win32) { - return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; - } -}; - - -/***/ }), -/* 290 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const constants = __webpack_require__(289); -const utils = __webpack_require__(288); - -/** - * Constants - */ - -const { - MAX_LENGTH, - POSIX_REGEX_SOURCE, - REGEX_NON_SPECIAL_CHARS, - REGEX_SPECIAL_CHARS_BACKREF, - REPLACEMENTS -} = constants; - -/** - * Helpers - */ - -const expandRange = (args, options) => { - if (typeof options.expandRange === 'function') { - return options.expandRange(...args, options); - } - - args.sort(); - const value = `[${args.join('-')}]`; - - try { - /* eslint-disable-next-line no-new */ - new RegExp(value); - } catch (ex) { - return args.map(v => utils.escapeRegex(v)).join('..'); - } - - return value; -}; - -/** - * Create the message for a syntax error - */ - -const syntaxError = (type, char) => { - return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; -}; - -/** - * Parse the given input string. - * @param {String} input - * @param {Object} options - * @return {Object} - */ - -const parse = (input, options) => { - if (typeof input !== 'string') { - throw new TypeError('Expected a string'); - } - - input = REPLACEMENTS[input] || input; - - const opts = { ...options }; - const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; - - let len = input.length; - if (len > max) { - throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); - } - - const bos = { type: 'bos', value: '', output: opts.prepend || '' }; - const tokens = [bos]; - - const capture = opts.capture ? '' : '?:'; - const win32 = utils.isWindows(options); - - // create constants based on platform, for windows or posix - const PLATFORM_CHARS = constants.globChars(win32); - const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); - - const { - DOT_LITERAL, - PLUS_LITERAL, - SLASH_LITERAL, - ONE_CHAR, - DOTS_SLASH, - NO_DOT, - NO_DOT_SLASH, - NO_DOTS_SLASH, - QMARK, - QMARK_NO_DOT, - STAR, - START_ANCHOR - } = PLATFORM_CHARS; - - const globstar = (opts) => { - return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; - }; - - const nodot = opts.dot ? '' : NO_DOT; - const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; - let star = opts.bash === true ? globstar(opts) : STAR; - - if (opts.capture) { - star = `(${star})`; - } - - // minimatch options support - if (typeof opts.noext === 'boolean') { - opts.noextglob = opts.noext; - } - - const state = { - input, - index: -1, - start: 0, - dot: opts.dot === true, - consumed: '', - output: '', - prefix: '', - backtrack: false, - negated: false, - brackets: 0, - braces: 0, - parens: 0, - quotes: 0, - globstar: false, - tokens - }; - - input = utils.removePrefix(input, state); - len = input.length; - - const extglobs = []; - const braces = []; - const stack = []; - let prev = bos; - let value; - - /** - * Tokenizing helpers - */ - - const eos = () => state.index === len - 1; - const peek = state.peek = (n = 1) => input[state.index + n]; - const advance = state.advance = () => input[++state.index]; - const remaining = () => input.slice(state.index + 1); - const consume = (value = '', num = 0) => { - state.consumed += value; - state.index += num; - }; - const append = token => { - state.output += token.output != null ? token.output : token.value; - consume(token.value); - }; - - const negate = () => { - let count = 1; - - while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) { - advance(); - state.start++; - count++; - } - - if (count % 2 === 0) { - return false; - } - - state.negated = true; - state.start++; - return true; - }; - - const increment = type => { - state[type]++; - stack.push(type); - }; - - const decrement = type => { - state[type]--; - stack.pop(); - }; - - /** - * Push tokens onto the tokens array. This helper speeds up - * tokenizing by 1) helping us avoid backtracking as much as possible, - * and 2) helping us avoid creating extra tokens when consecutive - * characters are plain text. This improves performance and simplifies - * lookbehinds. - */ - - const push = tok => { - if (prev.type === 'globstar') { - const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); - const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren')); - - if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) { - state.output = state.output.slice(0, -prev.output.length); - prev.type = 'star'; - prev.value = '*'; - prev.output = star; - state.output += prev.output; - } - } - - if (extglobs.length && tok.type !== 'paren' && !EXTGLOB_CHARS[tok.value]) { - extglobs[extglobs.length - 1].inner += tok.value; - } - - if (tok.value || tok.output) append(tok); - if (prev && prev.type === 'text' && tok.type === 'text') { - prev.value += tok.value; - prev.output = (prev.output || '') + tok.value; - return; - } - - tok.prev = prev; - tokens.push(tok); - prev = tok; - }; - - const extglobOpen = (type, value) => { - const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; - - token.prev = prev; - token.parens = state.parens; - token.output = state.output; - const output = (opts.capture ? '(' : '') + token.open; - - increment('parens'); - push({ type, value, output: state.output ? '' : ONE_CHAR }); - push({ type: 'paren', extglob: true, value: advance(), output }); - extglobs.push(token); - }; - - const extglobClose = token => { - let output = token.close + (opts.capture ? ')' : ''); - - if (token.type === 'negate') { - let extglobStar = star; - - if (token.inner && token.inner.length > 1 && token.inner.includes('/')) { - extglobStar = globstar(opts); - } - - if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { - output = token.close = `)$))${extglobStar}`; - } - - if (token.prev.type === 'bos' && eos()) { - state.negatedExtglob = true; - } - } - - push({ type: 'paren', extglob: true, value, output }); - decrement('parens'); - }; - - /** - * Fast paths - */ - - if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { - let backslashes = false; - - let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { - if (first === '\\') { - backslashes = true; - return m; - } - - if (first === '?') { - if (esc) { - return esc + first + (rest ? QMARK.repeat(rest.length) : ''); - } - if (index === 0) { - return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ''); - } - return QMARK.repeat(chars.length); - } - - if (first === '.') { - return DOT_LITERAL.repeat(chars.length); - } - - if (first === '*') { - if (esc) { - return esc + first + (rest ? star : ''); - } - return star; - } - return esc ? m : `\\${m}`; - }); - - if (backslashes === true) { - if (opts.unescape === true) { - output = output.replace(/\\/g, ''); - } else { - output = output.replace(/\\+/g, m => { - return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : ''); - }); - } - } - - if (output === input && opts.contains === true) { - state.output = input; - return state; - } - - state.output = utils.wrapOutput(output, state, options); - return state; - } - - /** - * Tokenize input until we reach end-of-string - */ - - while (!eos()) { - value = advance(); - - if (value === '\u0000') { - continue; - } - - /** - * Escaped characters - */ - - if (value === '\\') { - const next = peek(); - - if (next === '/' && opts.bash !== true) { - continue; - } - - if (next === '.' || next === ';') { - continue; - } - - if (!next) { - value += '\\'; - push({ type: 'text', value }); - continue; - } - - // collapse slashes to reduce potential for exploits - const match = /^\\+/.exec(remaining()); - let slashes = 0; - - if (match && match[0].length > 2) { - slashes = match[0].length; - state.index += slashes; - if (slashes % 2 !== 0) { - value += '\\'; - } - } - - if (opts.unescape === true) { - value = advance() || ''; - } else { - value += advance() || ''; - } - - if (state.brackets === 0) { - push({ type: 'text', value }); - continue; - } - } - - /** - * If we're inside a regex character class, continue - * until we reach the closing bracket. - */ - - if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) { - if (opts.posix !== false && value === ':') { - const inner = prev.value.slice(1); - if (inner.includes('[')) { - prev.posix = true; - - if (inner.includes(':')) { - const idx = prev.value.lastIndexOf('['); - const pre = prev.value.slice(0, idx); - const rest = prev.value.slice(idx + 2); - const posix = POSIX_REGEX_SOURCE[rest]; - if (posix) { - prev.value = pre + posix; - state.backtrack = true; - advance(); - - if (!bos.output && tokens.indexOf(prev) === 1) { - bos.output = ONE_CHAR; - } - continue; - } - } - } - } - - if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) { - value = `\\${value}`; - } - - if (value === ']' && (prev.value === '[' || prev.value === '[^')) { - value = `\\${value}`; - } - - if (opts.posix === true && value === '!' && prev.value === '[') { - value = '^'; - } - - prev.value += value; - append({ value }); - continue; - } - - /** - * If we're inside a quoted string, continue - * until we reach the closing double quote. - */ - - if (state.quotes === 1 && value !== '"') { - value = utils.escapeRegex(value); - prev.value += value; - append({ value }); - continue; - } - - /** - * Double quotes - */ - - if (value === '"') { - state.quotes = state.quotes === 1 ? 0 : 1; - if (opts.keepQuotes === true) { - push({ type: 'text', value }); - } - continue; - } - - /** - * Parentheses - */ - - if (value === '(') { - increment('parens'); - push({ type: 'paren', value }); - continue; - } - - if (value === ')') { - if (state.parens === 0 && opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('opening', '(')); - } - - const extglob = extglobs[extglobs.length - 1]; - if (extglob && state.parens === extglob.parens + 1) { - extglobClose(extglobs.pop()); - continue; - } - - push({ type: 'paren', value, output: state.parens ? ')' : '\\)' }); - decrement('parens'); - continue; - } - - /** - * Square brackets - */ - - if (value === '[') { - if (opts.nobracket === true || !remaining().includes(']')) { - if (opts.nobracket !== true && opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('closing', ']')); - } - - value = `\\${value}`; - } else { - increment('brackets'); - } - - push({ type: 'bracket', value }); - continue; - } - - if (value === ']') { - if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) { - push({ type: 'text', value, output: `\\${value}` }); - continue; - } - - if (state.brackets === 0) { - if (opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('opening', '[')); - } - - push({ type: 'text', value, output: `\\${value}` }); - continue; - } - - decrement('brackets'); - - const prevValue = prev.value.slice(1); - if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) { - value = `/${value}`; - } - - prev.value += value; - append({ value }); - - // when literal brackets are explicitly disabled - // assume we should match with a regex character class - if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { - continue; - } - - const escaped = utils.escapeRegex(prev.value); - state.output = state.output.slice(0, -prev.value.length); - - // when literal brackets are explicitly enabled - // assume we should escape the brackets to match literal characters - if (opts.literalBrackets === true) { - state.output += escaped; - prev.value = escaped; - continue; - } - - // when the user specifies nothing, try to match both - prev.value = `(${capture}${escaped}|${prev.value})`; - state.output += prev.value; - continue; - } - - /** - * Braces - */ - - if (value === '{' && opts.nobrace !== true) { - increment('braces'); - - const open = { - type: 'brace', - value, - output: '(', - outputIndex: state.output.length, - tokensIndex: state.tokens.length - }; - - braces.push(open); - push(open); - continue; - } - - if (value === '}') { - const brace = braces[braces.length - 1]; - - if (opts.nobrace === true || !brace) { - push({ type: 'text', value, output: value }); - continue; - } - - let output = ')'; - - if (brace.dots === true) { - const arr = tokens.slice(); - const range = []; - - for (let i = arr.length - 1; i >= 0; i--) { - tokens.pop(); - if (arr[i].type === 'brace') { - break; - } - if (arr[i].type !== 'dots') { - range.unshift(arr[i].value); - } - } - - output = expandRange(range, opts); - state.backtrack = true; - } - - if (brace.comma !== true && brace.dots !== true) { - const out = state.output.slice(0, brace.outputIndex); - const toks = state.tokens.slice(brace.tokensIndex); - brace.value = brace.output = '\\{'; - value = output = '\\}'; - state.output = out; - for (const t of toks) { - state.output += (t.output || t.value); - } - } - - push({ type: 'brace', value, output }); - decrement('braces'); - braces.pop(); - continue; - } - - /** - * Pipes - */ - - if (value === '|') { - if (extglobs.length > 0) { - extglobs[extglobs.length - 1].conditions++; - } - push({ type: 'text', value }); - continue; - } - - /** - * Commas - */ - - if (value === ',') { - let output = value; - - const brace = braces[braces.length - 1]; - if (brace && stack[stack.length - 1] === 'braces') { - brace.comma = true; - output = '|'; - } - - push({ type: 'comma', value, output }); - continue; - } - - /** - * Slashes - */ - - if (value === '/') { - // if the beginning of the glob is "./", advance the start - // to the current index, and don't add the "./" characters - // to the state. This greatly simplifies lookbehinds when - // checking for BOS characters like "!" and "." (not "./") - if (prev.type === 'dot' && state.index === state.start + 1) { - state.start = state.index + 1; - state.consumed = ''; - state.output = ''; - tokens.pop(); - prev = bos; // reset "prev" to the first token - continue; - } - - push({ type: 'slash', value, output: SLASH_LITERAL }); - continue; - } - - /** - * Dots - */ - - if (value === '.') { - if (state.braces > 0 && prev.type === 'dot') { - if (prev.value === '.') prev.output = DOT_LITERAL; - const brace = braces[braces.length - 1]; - prev.type = 'dots'; - prev.output += value; - prev.value += value; - brace.dots = true; - continue; - } - - if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') { - push({ type: 'text', value, output: DOT_LITERAL }); - continue; - } - - push({ type: 'dot', value, output: DOT_LITERAL }); - continue; - } - - /** - * Question marks - */ - - if (value === '?') { - const isGroup = prev && prev.value === '('; - if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - extglobOpen('qmark', value); - continue; - } - - if (prev && prev.type === 'paren') { - const next = peek(); - let output = value; - - if (next === '<' && !utils.supportsLookbehinds()) { - throw new Error('Node.js v10 or higher is required for regex lookbehinds'); - } - - if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) { - output = `\\${value}`; - } - - push({ type: 'text', value, output }); - continue; - } - - if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) { - push({ type: 'qmark', value, output: QMARK_NO_DOT }); - continue; - } - - push({ type: 'qmark', value, output: QMARK }); - continue; - } - - /** - * Exclamation - */ - - if (value === '!') { - if (opts.noextglob !== true && peek() === '(') { - if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) { - extglobOpen('negate', value); - continue; - } - } - - if (opts.nonegate !== true && state.index === 0) { - negate(); - continue; - } - } - - /** - * Plus - */ - - if (value === '+') { - if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - extglobOpen('plus', value); - continue; - } - - if ((prev && prev.value === '(') || opts.regex === false) { - push({ type: 'plus', value, output: PLUS_LITERAL }); - continue; - } - - if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) { - push({ type: 'plus', value }); - continue; - } - - push({ type: 'plus', value: PLUS_LITERAL }); - continue; - } - - /** - * Plain text - */ - - if (value === '@') { - if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - push({ type: 'at', extglob: true, value, output: '' }); - continue; - } - - push({ type: 'text', value }); - continue; - } - - /** - * Plain text - */ - - if (value !== '*') { - if (value === '$' || value === '^') { - value = `\\${value}`; - } - - const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); - if (match) { - value += match[0]; - state.index += match[0].length; - } - - push({ type: 'text', value }); - continue; - } - - /** - * Stars - */ - - if (prev && (prev.type === 'globstar' || prev.star === true)) { - prev.type = 'star'; - prev.star = true; - prev.value += value; - prev.output = star; - state.backtrack = true; - state.globstar = true; - consume(value); - continue; - } - - let rest = remaining(); - if (opts.noextglob !== true && /^\([^?]/.test(rest)) { - extglobOpen('star', value); - continue; - } - - if (prev.type === 'star') { - if (opts.noglobstar === true) { - consume(value); - continue; - } - - const prior = prev.prev; - const before = prior.prev; - const isStart = prior.type === 'slash' || prior.type === 'bos'; - const afterStar = before && (before.type === 'star' || before.type === 'globstar'); - - if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) { - push({ type: 'star', value, output: '' }); - continue; - } - - const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); - const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren'); - if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) { - push({ type: 'star', value, output: '' }); - continue; - } - - // strip consecutive `/**/` - while (rest.slice(0, 3) === '/**') { - const after = input[state.index + 4]; - if (after && after !== '/') { - break; - } - rest = rest.slice(3); - consume('/**', 3); - } - - if (prior.type === 'bos' && eos()) { - prev.type = 'globstar'; - prev.value += value; - prev.output = globstar(opts); - state.output = prev.output; - state.globstar = true; - consume(value); - continue; - } - - if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) { - state.output = state.output.slice(0, -(prior.output + prev.output).length); - prior.output = `(?:${prior.output}`; - - prev.type = 'globstar'; - prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)'); - prev.value += value; - state.globstar = true; - state.output += prior.output + prev.output; - consume(value); - continue; - } - - if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') { - const end = rest[1] !== void 0 ? '|$' : ''; - - state.output = state.output.slice(0, -(prior.output + prev.output).length); - prior.output = `(?:${prior.output}`; - - prev.type = 'globstar'; - prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; - prev.value += value; - - state.output += prior.output + prev.output; - state.globstar = true; - - consume(value + advance()); - - push({ type: 'slash', value: '/', output: '' }); - continue; - } - - if (prior.type === 'bos' && rest[0] === '/') { - prev.type = 'globstar'; - prev.value += value; - prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; - state.output = prev.output; - state.globstar = true; - consume(value + advance()); - push({ type: 'slash', value: '/', output: '' }); - continue; - } - - // remove single star from output - state.output = state.output.slice(0, -prev.output.length); - - // reset previous token to globstar - prev.type = 'globstar'; - prev.output = globstar(opts); - prev.value += value; - - // reset output with globstar - state.output += prev.output; - state.globstar = true; - consume(value); - continue; - } - - const token = { type: 'star', value, output: star }; - - if (opts.bash === true) { - token.output = '.*?'; - if (prev.type === 'bos' || prev.type === 'slash') { - token.output = nodot + token.output; - } - push(token); - continue; - } - - if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) { - token.output = value; - push(token); - continue; - } - - if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') { - if (prev.type === 'dot') { - state.output += NO_DOT_SLASH; - prev.output += NO_DOT_SLASH; - - } else if (opts.dot === true) { - state.output += NO_DOTS_SLASH; - prev.output += NO_DOTS_SLASH; - - } else { - state.output += nodot; - prev.output += nodot; - } - - if (peek() !== '*') { - state.output += ONE_CHAR; - prev.output += ONE_CHAR; - } - } - - push(token); - } - - while (state.brackets > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']')); - state.output = utils.escapeLast(state.output, '['); - decrement('brackets'); - } - - while (state.parens > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')')); - state.output = utils.escapeLast(state.output, '('); - decrement('parens'); - } - - while (state.braces > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}')); - state.output = utils.escapeLast(state.output, '{'); - decrement('braces'); - } - - if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) { - push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` }); - } - - // rebuild the output if we had to backtrack at any point - if (state.backtrack === true) { - state.output = ''; - - for (const token of state.tokens) { - state.output += token.output != null ? token.output : token.value; - - if (token.suffix) { - state.output += token.suffix; - } - } - } - - return state; -}; - -/** - * Fast paths for creating regular expressions for common glob patterns. - * This can significantly speed up processing and has very little downside - * impact when none of the fast paths match. - */ - -parse.fastpaths = (input, options) => { - const opts = { ...options }; - const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; - const len = input.length; - if (len > max) { - throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); - } - - input = REPLACEMENTS[input] || input; - const win32 = utils.isWindows(options); - - // create constants based on platform, for windows or posix - const { - DOT_LITERAL, - SLASH_LITERAL, - ONE_CHAR, - DOTS_SLASH, - NO_DOT, - NO_DOTS, - NO_DOTS_SLASH, - STAR, - START_ANCHOR - } = constants.globChars(win32); - - const nodot = opts.dot ? NO_DOTS : NO_DOT; - const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; - const capture = opts.capture ? '' : '?:'; - const state = { negated: false, prefix: '' }; - let star = opts.bash === true ? '.*?' : STAR; - - if (opts.capture) { - star = `(${star})`; - } - - const globstar = (opts) => { - if (opts.noglobstar === true) return star; - return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; - }; - - const create = str => { - switch (str) { - case '*': - return `${nodot}${ONE_CHAR}${star}`; - - case '.*': - return `${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '*.*': - return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '*/*': - return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; - - case '**': - return nodot + globstar(opts); - - case '**/*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; - - case '**/*.*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '**/.*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; - - default: { - const match = /^(.*?)\.(\w+)$/.exec(str); - if (!match) return; - - const source = create(match[1]); - if (!source) return; - - return source + DOT_LITERAL + match[2]; - } - } - }; - - const output = utils.removePrefix(input, state); - let source = create(output); - - if (source && opts.strictSlashes !== true) { - source += `${SLASH_LITERAL}?`; - } - - return source; -}; - -module.exports = parse; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.merge = void 0; +const merge2 = __webpack_require__(243); +function merge(streams) { + const mergedStream = merge2(streams); + streams.forEach((stream) => { + stream.once('error', (error) => mergedStream.emit('error', error)); + }); + mergedStream.once('close', () => propagateCloseEventToSources(streams)); + mergedStream.once('end', () => propagateCloseEventToSources(streams)); + return mergedStream; +} +exports.merge = merge; +function propagateCloseEventToSources(streams) { + streams.forEach((stream) => stream.emit('close')); +} /***/ }), -/* 291 */ +/* 286 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.merge = void 0; -const merge2 = __webpack_require__(243); -function merge(streams) { - const mergedStream = merge2(streams); - streams.forEach((stream) => { - stream.once('error', (error) => mergedStream.emit('error', error)); - }); - mergedStream.once('close', () => propagateCloseEventToSources(streams)); - mergedStream.once('end', () => propagateCloseEventToSources(streams)); - return mergedStream; -} -exports.merge = merge; -function propagateCloseEventToSources(streams) { - streams.forEach((stream) => stream.emit('close')); -} - - -/***/ }), -/* 292 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEmpty = exports.isString = void 0; -function isString(input) { - return typeof input === 'string'; -} -exports.isString = isString; -function isEmpty(input) { - return input === ''; -} -exports.isEmpty = isEmpty; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEmpty = exports.isString = void 0; +function isString(input) { + return typeof input === 'string'; +} +exports.isString = isString; +function isEmpty(input) { + return input === ''; +} +exports.isEmpty = isEmpty; /***/ }), -/* 293 */ +/* 287 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(294); -const provider_1 = __webpack_require__(321); -class ProviderAsync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = []; - return new Promise((resolve, reject) => { - const stream = this.api(root, task, options); - stream.once('error', reject); - stream.on('data', (entry) => entries.push(options.transform(entry))); - stream.once('end', () => resolve(entries)); - }); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderAsync; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(288); +const provider_1 = __webpack_require__(315); +class ProviderAsync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = []; + return new Promise((resolve, reject) => { + const stream = this.api(root, task, options); + stream.once('error', reject); + stream.on('data', (entry) => entries.push(options.transform(entry))); + stream.once('end', () => resolve(entries)); + }); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderAsync; /***/ }), -/* 294 */ +/* 288 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(173); -const fsStat = __webpack_require__(295); -const fsWalk = __webpack_require__(300); -const reader_1 = __webpack_require__(320); -class ReaderStream extends reader_1.default { - constructor() { - super(...arguments); - this._walkStream = fsWalk.walkStream; - this._stat = fsStat.stat; - } - dynamic(root, options) { - return this._walkStream(root, options); - } - static(patterns, options) { - const filepaths = patterns.map(this._getFullEntryPath, this); - const stream = new stream_1.PassThrough({ objectMode: true }); - stream._write = (index, _enc, done) => { - return this._getEntry(filepaths[index], patterns[index], options) - .then((entry) => { - if (entry !== null && options.entryFilter(entry)) { - stream.push(entry); - } - if (index === filepaths.length - 1) { - stream.end(); - } - done(); - }) - .catch(done); - }; - for (let i = 0; i < filepaths.length; i++) { - stream.write(i); - } - return stream; - } - _getEntry(filepath, pattern, options) { - return this._getStat(filepath) - .then((stats) => this._makeEntry(stats, pattern)) - .catch((error) => { - if (options.errorFilter(error)) { - return null; - } - throw error; - }); - } - _getStat(filepath) { - return new Promise((resolve, reject) => { - this._stat(filepath, this._fsStatSettings, (error, stats) => { - return error === null ? resolve(stats) : reject(error); - }); - }); - } -} -exports.default = ReaderStream; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(173); +const fsStat = __webpack_require__(289); +const fsWalk = __webpack_require__(294); +const reader_1 = __webpack_require__(314); +class ReaderStream extends reader_1.default { + constructor() { + super(...arguments); + this._walkStream = fsWalk.walkStream; + this._stat = fsStat.stat; + } + dynamic(root, options) { + return this._walkStream(root, options); + } + static(patterns, options) { + const filepaths = patterns.map(this._getFullEntryPath, this); + const stream = new stream_1.PassThrough({ objectMode: true }); + stream._write = (index, _enc, done) => { + return this._getEntry(filepaths[index], patterns[index], options) + .then((entry) => { + if (entry !== null && options.entryFilter(entry)) { + stream.push(entry); + } + if (index === filepaths.length - 1) { + stream.end(); + } + done(); + }) + .catch(done); + }; + for (let i = 0; i < filepaths.length; i++) { + stream.write(i); + } + return stream; + } + _getEntry(filepath, pattern, options) { + return this._getStat(filepath) + .then((stats) => this._makeEntry(stats, pattern)) + .catch((error) => { + if (options.errorFilter(error)) { + return null; + } + throw error; + }); + } + _getStat(filepath) { + return new Promise((resolve, reject) => { + this._stat(filepath, this._fsStatSettings, (error, stats) => { + return error === null ? resolve(stats) : reject(error); + }); + }); + } +} +exports.default = ReaderStream; /***/ }), -/* 295 */ +/* 289 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(296); -const sync = __webpack_require__(297); -const settings_1 = __webpack_require__(298); +const async = __webpack_require__(290); +const sync = __webpack_require__(291); +const settings_1 = __webpack_require__(292); exports.Settings = settings_1.default; function stat(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -28689,7 +26641,7 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 296 */ +/* 290 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28727,7 +26679,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 297 */ +/* 291 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28756,13 +26708,13 @@ exports.read = read; /***/ }), -/* 298 */ +/* 292 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(299); +const fs = __webpack_require__(293); class Settings { constructor(_options = {}) { this._options = _options; @@ -28779,7 +26731,7 @@ exports.default = Settings; /***/ }), -/* 299 */ +/* 293 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28802,16 +26754,16 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 300 */ +/* 294 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(301); -const stream_1 = __webpack_require__(316); -const sync_1 = __webpack_require__(317); -const settings_1 = __webpack_require__(319); +const async_1 = __webpack_require__(295); +const stream_1 = __webpack_require__(310); +const sync_1 = __webpack_require__(311); +const settings_1 = __webpack_require__(313); exports.Settings = settings_1.default; function walk(directory, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -28841,13 +26793,13 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 301 */ +/* 295 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(302); +const async_1 = __webpack_require__(296); class AsyncProvider { constructor(_root, _settings) { this._root = _root; @@ -28878,17 +26830,17 @@ function callSuccessCallback(callback, entries) { /***/ }), -/* 302 */ +/* 296 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __webpack_require__(164); -const fsScandir = __webpack_require__(303); -const fastq = __webpack_require__(312); -const common = __webpack_require__(314); -const reader_1 = __webpack_require__(315); +const fsScandir = __webpack_require__(297); +const fastq = __webpack_require__(306); +const common = __webpack_require__(308); +const reader_1 = __webpack_require__(309); class AsyncReader extends reader_1.default { constructor(_root, _settings) { super(_root, _settings); @@ -28978,15 +26930,15 @@ exports.default = AsyncReader; /***/ }), -/* 303 */ +/* 297 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(304); -const sync = __webpack_require__(309); -const settings_1 = __webpack_require__(310); +const async = __webpack_require__(298); +const sync = __webpack_require__(303); +const settings_1 = __webpack_require__(304); exports.Settings = settings_1.default; function scandir(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -29009,16 +26961,16 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 304 */ +/* 298 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(295); -const rpl = __webpack_require__(305); -const constants_1 = __webpack_require__(306); -const utils = __webpack_require__(307); +const fsStat = __webpack_require__(289); +const rpl = __webpack_require__(299); +const constants_1 = __webpack_require__(300); +const utils = __webpack_require__(301); function read(directory, settings, callback) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings, callback); @@ -29106,7 +27058,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 305 */ +/* 299 */ /***/ (function(module, exports) { module.exports = runParallel @@ -29160,7 +27112,7 @@ function runParallel (tasks, cb) { /***/ }), -/* 306 */ +/* 300 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -29180,18 +27132,18 @@ exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_B /***/ }), -/* 307 */ +/* 301 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(308); +const fs = __webpack_require__(302); exports.fs = fs; /***/ }), -/* 308 */ +/* 302 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -29216,15 +27168,15 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 309 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(295); -const constants_1 = __webpack_require__(306); -const utils = __webpack_require__(307); +const fsStat = __webpack_require__(289); +const constants_1 = __webpack_require__(300); +const utils = __webpack_require__(301); function read(directory, settings) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings); @@ -29275,15 +27227,15 @@ exports.readdir = readdir; /***/ }), -/* 310 */ +/* 304 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(295); -const fs = __webpack_require__(311); +const fsStat = __webpack_require__(289); +const fs = __webpack_require__(305); class Settings { constructor(_options = {}) { this._options = _options; @@ -29306,7 +27258,7 @@ exports.default = Settings; /***/ }), -/* 311 */ +/* 305 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -29331,13 +27283,13 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 312 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var reusify = __webpack_require__(313) +var reusify = __webpack_require__(307) function fastqueue (context, worker, concurrency) { if (typeof context === 'function') { @@ -29511,7 +27463,7 @@ module.exports = fastqueue /***/ }), -/* 313 */ +/* 307 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -29551,7 +27503,7 @@ module.exports = reusify /***/ }), -/* 314 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -29582,13 +27534,13 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 315 */ +/* 309 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(314); +const common = __webpack_require__(308); class Reader { constructor(_root, _settings) { this._root = _root; @@ -29600,14 +27552,14 @@ exports.default = Reader; /***/ }), -/* 316 */ +/* 310 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(173); -const async_1 = __webpack_require__(302); +const async_1 = __webpack_require__(296); class StreamProvider { constructor(_root, _settings) { this._root = _root; @@ -29637,13 +27589,13 @@ exports.default = StreamProvider; /***/ }), -/* 317 */ +/* 311 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(318); +const sync_1 = __webpack_require__(312); class SyncProvider { constructor(_root, _settings) { this._root = _root; @@ -29658,15 +27610,15 @@ exports.default = SyncProvider; /***/ }), -/* 318 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(303); -const common = __webpack_require__(314); -const reader_1 = __webpack_require__(315); +const fsScandir = __webpack_require__(297); +const common = __webpack_require__(308); +const reader_1 = __webpack_require__(309); class SyncReader extends reader_1.default { constructor() { super(...arguments); @@ -29724,14 +27676,14 @@ exports.default = SyncReader; /***/ }), -/* 319 */ +/* 313 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsScandir = __webpack_require__(303); +const fsScandir = __webpack_require__(297); class Settings { constructor(_options = {}) { this._options = _options; @@ -29757,579 +27709,579 @@ exports.default = Settings; /***/ }), -/* 320 */ +/* 314 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsStat = __webpack_require__(295); -const utils = __webpack_require__(259); -class Reader { - constructor(_settings) { - this._settings = _settings; - this._fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this._settings.followSymbolicLinks, - fs: this._settings.fs, - throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks - }); - } - _getFullEntryPath(filepath) { - return path.resolve(this._settings.cwd, filepath); - } - _makeEntry(stats, pattern) { - const entry = { - name: pattern, - path: pattern, - dirent: utils.fs.createDirentFromStats(pattern, stats) - }; - if (this._settings.stats) { - entry.stats = stats; - } - return entry; - } - _isFatalError(error) { - return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; - } -} -exports.default = Reader; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsStat = __webpack_require__(289); +const utils = __webpack_require__(259); +class Reader { + constructor(_settings) { + this._settings = _settings; + this._fsStatSettings = new fsStat.Settings({ + followSymbolicLink: this._settings.followSymbolicLinks, + fs: this._settings.fs, + throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks + }); + } + _getFullEntryPath(filepath) { + return path.resolve(this._settings.cwd, filepath); + } + _makeEntry(stats, pattern) { + const entry = { + name: pattern, + path: pattern, + dirent: utils.fs.createDirentFromStats(pattern, stats) + }; + if (this._settings.stats) { + entry.stats = stats; + } + return entry; + } + _isFatalError(error) { + return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; + } +} +exports.default = Reader; /***/ }), -/* 321 */ +/* 315 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const deep_1 = __webpack_require__(322); -const entry_1 = __webpack_require__(325); -const error_1 = __webpack_require__(326); -const entry_2 = __webpack_require__(327); -class Provider { - constructor(_settings) { - this._settings = _settings; - this.errorFilter = new error_1.default(this._settings); - this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); - this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); - this.entryTransformer = new entry_2.default(this._settings); - } - _getRootDirectory(task) { - return path.resolve(this._settings.cwd, task.base); - } - _getReaderOptions(task) { - const basePath = task.base === '.' ? '' : task.base; - return { - basePath, - pathSegmentSeparator: '/', - concurrency: this._settings.concurrency, - deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), - entryFilter: this.entryFilter.getFilter(task.positive, task.negative), - errorFilter: this.errorFilter.getFilter(), - followSymbolicLinks: this._settings.followSymbolicLinks, - fs: this._settings.fs, - stats: this._settings.stats, - throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, - transform: this.entryTransformer.getTransformer() - }; - } - _getMicromatchOptions() { - return { - dot: this._settings.dot, - matchBase: this._settings.baseNameMatch, - nobrace: !this._settings.braceExpansion, - nocase: !this._settings.caseSensitiveMatch, - noext: !this._settings.extglob, - noglobstar: !this._settings.globstar, - posix: true, - strictSlashes: false - }; - } -} -exports.default = Provider; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const deep_1 = __webpack_require__(316); +const entry_1 = __webpack_require__(319); +const error_1 = __webpack_require__(320); +const entry_2 = __webpack_require__(321); +class Provider { + constructor(_settings) { + this._settings = _settings; + this.errorFilter = new error_1.default(this._settings); + this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); + this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); + this.entryTransformer = new entry_2.default(this._settings); + } + _getRootDirectory(task) { + return path.resolve(this._settings.cwd, task.base); + } + _getReaderOptions(task) { + const basePath = task.base === '.' ? '' : task.base; + return { + basePath, + pathSegmentSeparator: '/', + concurrency: this._settings.concurrency, + deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), + entryFilter: this.entryFilter.getFilter(task.positive, task.negative), + errorFilter: this.errorFilter.getFilter(), + followSymbolicLinks: this._settings.followSymbolicLinks, + fs: this._settings.fs, + stats: this._settings.stats, + throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, + transform: this.entryTransformer.getTransformer() + }; + } + _getMicromatchOptions() { + return { + dot: this._settings.dot, + matchBase: this._settings.baseNameMatch, + nobrace: !this._settings.braceExpansion, + nocase: !this._settings.caseSensitiveMatch, + noext: !this._settings.extglob, + noglobstar: !this._settings.globstar, + posix: true, + strictSlashes: false + }; + } +} +exports.default = Provider; /***/ }), -/* 322 */ +/* 316 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -const partial_1 = __webpack_require__(323); -class DeepFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - } - getFilter(basePath, positive, negative) { - const matcher = this._getMatcher(positive); - const negativeRe = this._getNegativePatternsRe(negative); - return (entry) => this._filter(basePath, entry, matcher, negativeRe); - } - _getMatcher(patterns) { - return new partial_1.default(patterns, this._settings, this._micromatchOptions); - } - _getNegativePatternsRe(patterns) { - const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); - return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); - } - _filter(basePath, entry, matcher, negativeRe) { - if (this._isSkippedByDeep(basePath, entry.path)) { - return false; - } - if (this._isSkippedSymbolicLink(entry)) { - return false; - } - const filepath = utils.path.removeLeadingDotSegment(entry.path); - if (this._isSkippedByPositivePatterns(filepath, matcher)) { - return false; - } - return this._isSkippedByNegativePatterns(filepath, negativeRe); - } - _isSkippedByDeep(basePath, entryPath) { - /** - * Avoid unnecessary depth calculations when it doesn't matter. - */ - if (this._settings.deep === Infinity) { - return false; - } - return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; - } - _getEntryLevel(basePath, entryPath) { - const entryPathDepth = entryPath.split('/').length; - if (basePath === '') { - return entryPathDepth; - } - const basePathDepth = basePath.split('/').length; - return entryPathDepth - basePathDepth; - } - _isSkippedSymbolicLink(entry) { - return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); - } - _isSkippedByPositivePatterns(entryPath, matcher) { - return !this._settings.baseNameMatch && !matcher.match(entryPath); - } - _isSkippedByNegativePatterns(entryPath, patternsRe) { - return !utils.pattern.matchAny(entryPath, patternsRe); - } -} -exports.default = DeepFilter; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +const partial_1 = __webpack_require__(317); +class DeepFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + } + getFilter(basePath, positive, negative) { + const matcher = this._getMatcher(positive); + const negativeRe = this._getNegativePatternsRe(negative); + return (entry) => this._filter(basePath, entry, matcher, negativeRe); + } + _getMatcher(patterns) { + return new partial_1.default(patterns, this._settings, this._micromatchOptions); + } + _getNegativePatternsRe(patterns) { + const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); + return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); + } + _filter(basePath, entry, matcher, negativeRe) { + if (this._isSkippedByDeep(basePath, entry.path)) { + return false; + } + if (this._isSkippedSymbolicLink(entry)) { + return false; + } + const filepath = utils.path.removeLeadingDotSegment(entry.path); + if (this._isSkippedByPositivePatterns(filepath, matcher)) { + return false; + } + return this._isSkippedByNegativePatterns(filepath, negativeRe); + } + _isSkippedByDeep(basePath, entryPath) { + /** + * Avoid unnecessary depth calculations when it doesn't matter. + */ + if (this._settings.deep === Infinity) { + return false; + } + return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; + } + _getEntryLevel(basePath, entryPath) { + const entryPathDepth = entryPath.split('/').length; + if (basePath === '') { + return entryPathDepth; + } + const basePathDepth = basePath.split('/').length; + return entryPathDepth - basePathDepth; + } + _isSkippedSymbolicLink(entry) { + return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); + } + _isSkippedByPositivePatterns(entryPath, matcher) { + return !this._settings.baseNameMatch && !matcher.match(entryPath); + } + _isSkippedByNegativePatterns(entryPath, patternsRe) { + return !utils.pattern.matchAny(entryPath, patternsRe); + } +} +exports.default = DeepFilter; /***/ }), -/* 323 */ +/* 317 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(324); -class PartialMatcher extends matcher_1.default { - match(filepath) { - const parts = filepath.split('/'); - const levels = parts.length; - const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); - for (const pattern of patterns) { - const section = pattern.sections[0]; - /** - * In this case, the pattern has a globstar and we must read all directories unconditionally, - * but only if the level has reached the end of the first group. - * - * fixtures/{a,b}/** - * ^ true/false ^ always true - */ - if (!pattern.complete && levels > section.length) { - return true; - } - const match = parts.every((part, index) => { - const segment = pattern.segments[index]; - if (segment.dynamic && segment.patternRe.test(part)) { - return true; - } - if (!segment.dynamic && segment.pattern === part) { - return true; - } - return false; - }); - if (match) { - return true; - } - } - return false; - } -} -exports.default = PartialMatcher; + +Object.defineProperty(exports, "__esModule", { value: true }); +const matcher_1 = __webpack_require__(318); +class PartialMatcher extends matcher_1.default { + match(filepath) { + const parts = filepath.split('/'); + const levels = parts.length; + const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); + for (const pattern of patterns) { + const section = pattern.sections[0]; + /** + * In this case, the pattern has a globstar and we must read all directories unconditionally, + * but only if the level has reached the end of the first group. + * + * fixtures/{a,b}/** + * ^ true/false ^ always true + */ + if (!pattern.complete && levels > section.length) { + return true; + } + const match = parts.every((part, index) => { + const segment = pattern.segments[index]; + if (segment.dynamic && segment.patternRe.test(part)) { + return true; + } + if (!segment.dynamic && segment.pattern === part) { + return true; + } + return false; + }); + if (match) { + return true; + } + } + return false; + } +} +exports.default = PartialMatcher; /***/ }), -/* 324 */ +/* 318 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -class Matcher { - constructor(_patterns, _settings, _micromatchOptions) { - this._patterns = _patterns; - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this._storage = []; - this._fillStorage(); - } - _fillStorage() { - /** - * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). - * So, before expand patterns with brace expansion into separated patterns. - */ - const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); - for (const pattern of patterns) { - const segments = this._getPatternSegments(pattern); - const sections = this._splitSegmentsIntoSections(segments); - this._storage.push({ - complete: sections.length <= 1, - pattern, - segments, - sections - }); - } - } - _getPatternSegments(pattern) { - const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); - return parts.map((part) => { - const dynamic = utils.pattern.isDynamicPattern(part, this._settings); - if (!dynamic) { - return { - dynamic: false, - pattern: part - }; - } - return { - dynamic: true, - pattern: part, - patternRe: utils.pattern.makeRe(part, this._micromatchOptions) - }; - }); - } - _splitSegmentsIntoSections(segments) { - return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); - } -} -exports.default = Matcher; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +class Matcher { + constructor(_patterns, _settings, _micromatchOptions) { + this._patterns = _patterns; + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this._storage = []; + this._fillStorage(); + } + _fillStorage() { + /** + * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). + * So, before expand patterns with brace expansion into separated patterns. + */ + const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); + for (const pattern of patterns) { + const segments = this._getPatternSegments(pattern); + const sections = this._splitSegmentsIntoSections(segments); + this._storage.push({ + complete: sections.length <= 1, + pattern, + segments, + sections + }); + } + } + _getPatternSegments(pattern) { + const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); + return parts.map((part) => { + const dynamic = utils.pattern.isDynamicPattern(part, this._settings); + if (!dynamic) { + return { + dynamic: false, + pattern: part + }; + } + return { + dynamic: true, + pattern: part, + patternRe: utils.pattern.makeRe(part, this._micromatchOptions) + }; + }); + } + _splitSegmentsIntoSections(segments) { + return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); + } +} +exports.default = Matcher; /***/ }), -/* 325 */ +/* 319 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -class EntryFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this.index = new Map(); - } - getFilter(positive, negative) { - const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); - const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); - return (entry) => this._filter(entry, positiveRe, negativeRe); - } - _filter(entry, positiveRe, negativeRe) { - if (this._settings.unique && this._isDuplicateEntry(entry)) { - return false; - } - if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { - return false; - } - if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { - return false; - } - const filepath = this._settings.baseNameMatch ? entry.name : entry.path; - const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); - if (this._settings.unique && isMatched) { - this._createIndexRecord(entry); - } - return isMatched; - } - _isDuplicateEntry(entry) { - return this.index.has(entry.path); - } - _createIndexRecord(entry) { - this.index.set(entry.path, undefined); - } - _onlyFileFilter(entry) { - return this._settings.onlyFiles && !entry.dirent.isFile(); - } - _onlyDirectoryFilter(entry) { - return this._settings.onlyDirectories && !entry.dirent.isDirectory(); - } - _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { - if (!this._settings.absolute) { - return false; - } - const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); - return utils.pattern.matchAny(fullpath, patternsRe); - } - _isMatchToPatterns(entryPath, patternsRe) { - const filepath = utils.path.removeLeadingDotSegment(entryPath); - return utils.pattern.matchAny(filepath, patternsRe); - } -} -exports.default = EntryFilter; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +class EntryFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this.index = new Map(); + } + getFilter(positive, negative) { + const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); + const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); + return (entry) => this._filter(entry, positiveRe, negativeRe); + } + _filter(entry, positiveRe, negativeRe) { + if (this._settings.unique && this._isDuplicateEntry(entry)) { + return false; + } + if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { + return false; + } + if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { + return false; + } + const filepath = this._settings.baseNameMatch ? entry.name : entry.path; + const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); + if (this._settings.unique && isMatched) { + this._createIndexRecord(entry); + } + return isMatched; + } + _isDuplicateEntry(entry) { + return this.index.has(entry.path); + } + _createIndexRecord(entry) { + this.index.set(entry.path, undefined); + } + _onlyFileFilter(entry) { + return this._settings.onlyFiles && !entry.dirent.isFile(); + } + _onlyDirectoryFilter(entry) { + return this._settings.onlyDirectories && !entry.dirent.isDirectory(); + } + _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { + if (!this._settings.absolute) { + return false; + } + const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); + return utils.pattern.matchAny(fullpath, patternsRe); + } + _isMatchToPatterns(entryPath, patternsRe) { + const filepath = utils.path.removeLeadingDotSegment(entryPath); + return utils.pattern.matchAny(filepath, patternsRe); + } +} +exports.default = EntryFilter; /***/ }), -/* 326 */ +/* 320 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -class ErrorFilter { - constructor(_settings) { - this._settings = _settings; - } - getFilter() { - return (error) => this._isNonFatalError(error); - } - _isNonFatalError(error) { - return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; - } -} -exports.default = ErrorFilter; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +class ErrorFilter { + constructor(_settings) { + this._settings = _settings; + } + getFilter() { + return (error) => this._isNonFatalError(error); + } + _isNonFatalError(error) { + return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; + } +} +exports.default = ErrorFilter; /***/ }), -/* 327 */ +/* 321 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -class EntryTransformer { - constructor(_settings) { - this._settings = _settings; - } - getTransformer() { - return (entry) => this._transform(entry); - } - _transform(entry) { - let filepath = entry.path; - if (this._settings.absolute) { - filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); - filepath = utils.path.unixify(filepath); - } - if (this._settings.markDirectories && entry.dirent.isDirectory()) { - filepath += '/'; - } - if (!this._settings.objectMode) { - return filepath; - } - return Object.assign(Object.assign({}, entry), { path: filepath }); - } -} -exports.default = EntryTransformer; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +class EntryTransformer { + constructor(_settings) { + this._settings = _settings; + } + getTransformer() { + return (entry) => this._transform(entry); + } + _transform(entry) { + let filepath = entry.path; + if (this._settings.absolute) { + filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); + filepath = utils.path.unixify(filepath); + } + if (this._settings.markDirectories && entry.dirent.isDirectory()) { + filepath += '/'; + } + if (!this._settings.objectMode) { + return filepath; + } + return Object.assign(Object.assign({}, entry), { path: filepath }); + } +} +exports.default = EntryTransformer; /***/ }), -/* 328 */ +/* 322 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(173); -const stream_2 = __webpack_require__(294); -const provider_1 = __webpack_require__(321); -class ProviderStream extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_2.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const source = this.api(root, task, options); - const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); - source - .once('error', (error) => destination.emit('error', error)) - .on('data', (entry) => destination.emit('data', options.transform(entry))) - .once('end', () => destination.emit('end')); - destination - .once('close', () => source.destroy()); - return destination; - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderStream; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(173); +const stream_2 = __webpack_require__(288); +const provider_1 = __webpack_require__(315); +class ProviderStream extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_2.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const source = this.api(root, task, options); + const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); + source + .once('error', (error) => destination.emit('error', error)) + .on('data', (entry) => destination.emit('data', options.transform(entry))) + .once('end', () => destination.emit('end')); + destination + .once('close', () => source.destroy()); + return destination; + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderStream; /***/ }), -/* 329 */ +/* 323 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(330); -const provider_1 = __webpack_require__(321); -class ProviderSync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new sync_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = this.api(root, task, options); - return entries.map(options.transform); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderSync; + +Object.defineProperty(exports, "__esModule", { value: true }); +const sync_1 = __webpack_require__(324); +const provider_1 = __webpack_require__(315); +class ProviderSync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new sync_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = this.api(root, task, options); + return entries.map(options.transform); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderSync; /***/ }), -/* 330 */ +/* 324 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(295); -const fsWalk = __webpack_require__(300); -const reader_1 = __webpack_require__(320); -class ReaderSync extends reader_1.default { - constructor() { - super(...arguments); - this._walkSync = fsWalk.walkSync; - this._statSync = fsStat.statSync; - } - dynamic(root, options) { - return this._walkSync(root, options); - } - static(patterns, options) { - const entries = []; - for (const pattern of patterns) { - const filepath = this._getFullEntryPath(pattern); - const entry = this._getEntry(filepath, pattern, options); - if (entry === null || !options.entryFilter(entry)) { - continue; - } - entries.push(entry); - } - return entries; - } - _getEntry(filepath, pattern, options) { - try { - const stats = this._getStat(filepath); - return this._makeEntry(stats, pattern); - } - catch (error) { - if (options.errorFilter(error)) { - return null; - } - throw error; - } - } - _getStat(filepath) { - return this._statSync(filepath, this._fsStatSettings); - } -} -exports.default = ReaderSync; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__(289); +const fsWalk = __webpack_require__(294); +const reader_1 = __webpack_require__(314); +class ReaderSync extends reader_1.default { + constructor() { + super(...arguments); + this._walkSync = fsWalk.walkSync; + this._statSync = fsStat.statSync; + } + dynamic(root, options) { + return this._walkSync(root, options); + } + static(patterns, options) { + const entries = []; + for (const pattern of patterns) { + const filepath = this._getFullEntryPath(pattern); + const entry = this._getEntry(filepath, pattern, options); + if (entry === null || !options.entryFilter(entry)) { + continue; + } + entries.push(entry); + } + return entries; + } + _getEntry(filepath, pattern, options) { + try { + const stats = this._getStat(filepath); + return this._makeEntry(stats, pattern); + } + catch (error) { + if (options.errorFilter(error)) { + return null; + } + throw error; + } + } + _getStat(filepath) { + return this._statSync(filepath, this._fsStatSettings); + } +} +exports.default = ReaderSync; /***/ }), -/* 331 */ +/* 325 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; -const fs = __webpack_require__(132); -const os = __webpack_require__(122); -/** - * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. - * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 - */ -const CPU_COUNT = Math.max(os.cpus().length, 1); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - lstatSync: fs.lstatSync, - stat: fs.stat, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync -}; -class Settings { - constructor(_options = {}) { - this._options = _options; - this.absolute = this._getValue(this._options.absolute, false); - this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); - this.braceExpansion = this._getValue(this._options.braceExpansion, true); - this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); - this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); - this.cwd = this._getValue(this._options.cwd, process.cwd()); - this.deep = this._getValue(this._options.deep, Infinity); - this.dot = this._getValue(this._options.dot, false); - this.extglob = this._getValue(this._options.extglob, true); - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); - this.fs = this._getFileSystemMethods(this._options.fs); - this.globstar = this._getValue(this._options.globstar, true); - this.ignore = this._getValue(this._options.ignore, []); - this.markDirectories = this._getValue(this._options.markDirectories, false); - this.objectMode = this._getValue(this._options.objectMode, false); - this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); - this.onlyFiles = this._getValue(this._options.onlyFiles, true); - this.stats = this._getValue(this._options.stats, false); - this.suppressErrors = this._getValue(this._options.suppressErrors, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); - this.unique = this._getValue(this._options.unique, true); - if (this.onlyDirectories) { - this.onlyFiles = false; - } - if (this.stats) { - this.objectMode = true; - } - } - _getValue(option, value) { - return option === undefined ? value : option; - } - _getFileSystemMethods(methods = {}) { - return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); - } -} -exports.default = Settings; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; +const fs = __webpack_require__(132); +const os = __webpack_require__(122); +/** + * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. + * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 + */ +const CPU_COUNT = Math.max(os.cpus().length, 1); +exports.DEFAULT_FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + lstatSync: fs.lstatSync, + stat: fs.stat, + statSync: fs.statSync, + readdir: fs.readdir, + readdirSync: fs.readdirSync +}; +class Settings { + constructor(_options = {}) { + this._options = _options; + this.absolute = this._getValue(this._options.absolute, false); + this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); + this.braceExpansion = this._getValue(this._options.braceExpansion, true); + this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); + this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); + this.cwd = this._getValue(this._options.cwd, process.cwd()); + this.deep = this._getValue(this._options.deep, Infinity); + this.dot = this._getValue(this._options.dot, false); + this.extglob = this._getValue(this._options.extglob, true); + this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); + this.fs = this._getFileSystemMethods(this._options.fs); + this.globstar = this._getValue(this._options.globstar, true); + this.ignore = this._getValue(this._options.ignore, []); + this.markDirectories = this._getValue(this._options.markDirectories, false); + this.objectMode = this._getValue(this._options.objectMode, false); + this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); + this.onlyFiles = this._getValue(this._options.onlyFiles, true); + this.stats = this._getValue(this._options.stats, false); + this.suppressErrors = this._getValue(this._options.suppressErrors, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); + this.unique = this._getValue(this._options.unique, true); + if (this.onlyDirectories) { + this.onlyFiles = false; + } + if (this.stats) { + this.objectMode = true; + } + } + _getValue(option, value) { + return option === undefined ? value : option; + } + _getFileSystemMethods(methods = {}) { + return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); + } +} +exports.default = Settings; /***/ }), -/* 332 */ +/* 326 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(333); +const pathType = __webpack_require__(327); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -30405,7 +28357,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 333 */ +/* 327 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -30455,7 +28407,7 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 334 */ +/* 328 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -30464,8 +28416,8 @@ const {promisify} = __webpack_require__(113); const fs = __webpack_require__(132); const path = __webpack_require__(4); const fastGlob = __webpack_require__(257); -const gitIgnore = __webpack_require__(335); -const slash = __webpack_require__(336); +const gitIgnore = __webpack_require__(329); +const slash = __webpack_require__(330); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -30579,7 +28531,7 @@ module.exports.sync = options => { /***/ }), -/* 335 */ +/* 329 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -31182,7 +29134,7 @@ if ( /***/ }), -/* 336 */ +/* 330 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -31200,7 +29152,7 @@ module.exports = path => { /***/ }), -/* 337 */ +/* 331 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -31253,7 +29205,7 @@ module.exports = { /***/ }), -/* 338 */ +/* 332 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -31275,7 +29227,7 @@ module.exports = path_ => { /***/ }), -/* 339 */ +/* 333 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -31303,7 +29255,7 @@ module.exports = (childPath, parentPath) => { /***/ }), -/* 340 */ +/* 334 */ /***/ (function(module, exports, __webpack_require__) { const assert = __webpack_require__(162) @@ -31669,12 +29621,12 @@ rimraf.sync = rimrafSync /***/ }), -/* 341 */ +/* 335 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(342); +const AggregateError = __webpack_require__(336); module.exports = async ( iterable, @@ -31757,13 +29709,13 @@ module.exports = async ( /***/ }), -/* 342 */ +/* 336 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(343); -const cleanStack = __webpack_require__(344); +const indentString = __webpack_require__(337); +const cleanStack = __webpack_require__(338); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -31811,7 +29763,7 @@ module.exports = AggregateError; /***/ }), -/* 343 */ +/* 337 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -31853,7 +29805,7 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 344 */ +/* 338 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -31900,7 +29852,7 @@ module.exports = (stack, options) => { /***/ }), -/* 345 */ +/* 339 */ /***/ (function(module, exports, __webpack_require__) { var fs = __webpack_require__(132), @@ -32167,7 +30119,7 @@ function ncp (source, dest, options, callback) { /***/ }), -/* 346 */ +/* 340 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -32184,8 +30136,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(113); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(347); -/* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(348); +/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(341); +/* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(342); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -32360,7 +30312,7 @@ function includeTransitiveProjects(subsetOfProjects, allProjects, { } /***/ }), -/* 347 */ +/* 341 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -32382,7 +30334,7 @@ class CliError extends Error { } /***/ }), -/* 348 */ +/* 342 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -32394,10 +30346,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(113); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(347); +/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(341); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(220); -/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(349); -/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(413); +/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(343); +/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(407); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -32590,7 +30542,7 @@ function normalizePath(path) { } /***/ }), -/* 349 */ +/* 343 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -32601,9 +30553,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isLinkDependency", function() { return isLinkDependency; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isBazelPackageDependency", function() { return isBazelPackageDependency; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformDependencies", function() { return transformDependencies; }); -/* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(350); +/* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(344); /* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(read_pkg__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(402); +/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(396); /* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(write_pkg__WEBPACK_IMPORTED_MODULE_1__); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } @@ -32671,7 +30623,7 @@ function transformDependencies(dependencies = {}) { } /***/ }), -/* 350 */ +/* 344 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -32679,7 +30631,7 @@ function transformDependencies(dependencies = {}) { const {promisify} = __webpack_require__(113); const fs = __webpack_require__(132); const path = __webpack_require__(4); -const parseJson = __webpack_require__(351); +const parseJson = __webpack_require__(345); const readFileAsync = promisify(fs.readFile); @@ -32694,7 +30646,7 @@ module.exports = async options => { const json = parseJson(await readFileAsync(filePath, 'utf8')); if (options.normalize) { - __webpack_require__(372)(json); + __webpack_require__(366)(json); } return json; @@ -32711,7 +30663,7 @@ module.exports.sync = options => { const json = parseJson(fs.readFileSync(filePath, 'utf8')); if (options.normalize) { - __webpack_require__(372)(json); + __webpack_require__(366)(json); } return json; @@ -32719,15 +30671,15 @@ module.exports.sync = options => { /***/ }), -/* 351 */ +/* 345 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const errorEx = __webpack_require__(352); -const fallback = __webpack_require__(354); -const {default: LinesAndColumns} = __webpack_require__(355); -const {codeFrameColumns} = __webpack_require__(356); +const errorEx = __webpack_require__(346); +const fallback = __webpack_require__(348); +const {default: LinesAndColumns} = __webpack_require__(349); +const {codeFrameColumns} = __webpack_require__(350); const JSONError = errorEx('JSONError', { fileName: errorEx.append('in %s'), @@ -32776,14 +30728,14 @@ module.exports = (string, reviver, filename) => { /***/ }), -/* 352 */ +/* 346 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(113); -var isArrayish = __webpack_require__(353); +var isArrayish = __webpack_require__(347); var errorEx = function errorEx(name, properties) { if (!name || name.constructor !== String) { @@ -32916,7 +30868,7 @@ module.exports = errorEx; /***/ }), -/* 353 */ +/* 347 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -32933,7 +30885,7 @@ module.exports = function isArrayish(obj) { /***/ }), -/* 354 */ +/* 348 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -32978,7 +30930,7 @@ function parseJson (txt, reviver, context) { /***/ }), -/* 355 */ +/* 349 */ /***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { "use strict"; @@ -33042,7 +30994,7 @@ var LinesAndColumns = (function () { /***/ }), -/* 356 */ +/* 350 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -33054,7 +31006,7 @@ Object.defineProperty(exports, "__esModule", { exports.codeFrameColumns = codeFrameColumns; exports.default = _default; -var _highlight = __webpack_require__(357); +var _highlight = __webpack_require__(351); let deprecationWarningShown = false; @@ -33211,7 +31163,7 @@ function _default(rawLines, lineNumber, colNumber, opts = {}) { } /***/ }), -/* 357 */ +/* 351 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -33224,11 +31176,11 @@ exports.default = highlight; exports.getChalk = getChalk; exports.shouldHighlight = shouldHighlight; -var _jsTokens = __webpack_require__(358); +var _jsTokens = __webpack_require__(352); -var _helperValidatorIdentifier = __webpack_require__(359); +var _helperValidatorIdentifier = __webpack_require__(353); -var _chalk = __webpack_require__(362); +var _chalk = __webpack_require__(356); const sometimesKeywords = new Set(["as", "async", "from", "get", "of", "set"]); @@ -33333,7 +31285,7 @@ function highlight(code, options = {}) { } /***/ }), -/* 358 */ +/* 352 */ /***/ (function(module, exports) { // Copyright 2014, 2015, 2016, 2017, 2018 Simon Lydell @@ -33362,7 +31314,7 @@ exports.matchToToken = function(match) { /***/ }), -/* 359 */ +/* 353 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -33420,12 +31372,12 @@ Object.defineProperty(exports, "isKeyword", { } }); -var _identifier = __webpack_require__(360); +var _identifier = __webpack_require__(354); -var _keyword = __webpack_require__(361); +var _keyword = __webpack_require__(355); /***/ }), -/* 360 */ +/* 354 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -33515,7 +31467,7 @@ function isIdentifierName(name) { } /***/ }), -/* 361 */ +/* 355 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -33559,16 +31511,16 @@ function isKeyword(word) { } /***/ }), -/* 362 */ +/* 356 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const escapeStringRegexp = __webpack_require__(363); -const ansiStyles = __webpack_require__(364); -const stdoutColor = __webpack_require__(369).stdout; +const escapeStringRegexp = __webpack_require__(357); +const ansiStyles = __webpack_require__(358); +const stdoutColor = __webpack_require__(363).stdout; -const template = __webpack_require__(371); +const template = __webpack_require__(365); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -33794,7 +31746,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 363 */ +/* 357 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -33812,12 +31764,12 @@ module.exports = function (str) { /***/ }), -/* 364 */ +/* 358 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(365); +const colorConvert = __webpack_require__(359); const wrapAnsi16 = (fn, offset) => function () { const code = fn.apply(colorConvert, arguments); @@ -33985,11 +31937,11 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) /***/ }), -/* 365 */ +/* 359 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(366); -var route = __webpack_require__(368); +var conversions = __webpack_require__(360); +var route = __webpack_require__(362); var convert = {}; @@ -34069,11 +32021,11 @@ module.exports = convert; /***/ }), -/* 366 */ +/* 360 */ /***/ (function(module, exports, __webpack_require__) { /* MIT license */ -var cssKeywords = __webpack_require__(367); +var cssKeywords = __webpack_require__(361); // NOTE: conversions should only return primitive values (i.e. arrays, or // values that give correct `typeof` results). @@ -34943,7 +32895,7 @@ convert.rgb.gray = function (rgb) { /***/ }), -/* 367 */ +/* 361 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -35102,10 +33054,10 @@ module.exports = { /***/ }), -/* 368 */ +/* 362 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(366); +var conversions = __webpack_require__(360); /* this function routes a model to all other models. @@ -35205,13 +33157,13 @@ module.exports = function (fromModel) { /***/ }), -/* 369 */ +/* 363 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const os = __webpack_require__(122); -const hasFlag = __webpack_require__(370); +const hasFlag = __webpack_require__(364); const env = process.env; @@ -35343,7 +33295,7 @@ module.exports = { /***/ }), -/* 370 */ +/* 364 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -35358,7 +33310,7 @@ module.exports = (flag, argv) => { /***/ }), -/* 371 */ +/* 365 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -35493,15 +33445,15 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 372 */ +/* 366 */ /***/ (function(module, exports, __webpack_require__) { module.exports = normalize -var fixer = __webpack_require__(373) +var fixer = __webpack_require__(367) normalize.fixer = fixer -var makeWarning = __webpack_require__(400) +var makeWarning = __webpack_require__(394) var fieldsToFix = ['name','version','description','repository','modules','scripts' ,'files','bin','man','bugs','keywords','readme','homepage','license'] @@ -35538,17 +33490,17 @@ function ucFirst (string) { /***/ }), -/* 373 */ +/* 367 */ /***/ (function(module, exports, __webpack_require__) { -var semver = __webpack_require__(374) -var validateLicense = __webpack_require__(375); -var hostedGitInfo = __webpack_require__(380) -var isBuiltinModule = __webpack_require__(383).isCore +var semver = __webpack_require__(368) +var validateLicense = __webpack_require__(369); +var hostedGitInfo = __webpack_require__(374) +var isBuiltinModule = __webpack_require__(377).isCore var depTypes = ["dependencies","devDependencies","optionalDependencies"] -var extractDescription = __webpack_require__(398) +var extractDescription = __webpack_require__(392) var url = __webpack_require__(203) -var typos = __webpack_require__(399) +var typos = __webpack_require__(393) var fixer = module.exports = { // default warning function @@ -35962,7 +33914,7 @@ function bugsTypos(bugs, warn) { /***/ }), -/* 374 */ +/* 368 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -37451,11 +35403,11 @@ function coerce (version) { /***/ }), -/* 375 */ +/* 369 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(376); -var correct = __webpack_require__(378); +var parse = __webpack_require__(370); +var correct = __webpack_require__(372); var genericWarning = ( 'license should be ' + @@ -37541,10 +35493,10 @@ module.exports = function(argument) { /***/ }), -/* 376 */ +/* 370 */ /***/ (function(module, exports, __webpack_require__) { -var parser = __webpack_require__(377).parser +var parser = __webpack_require__(371).parser module.exports = function (argument) { return parser.parse(argument) @@ -37552,7 +35504,7 @@ module.exports = function (argument) { /***/ }), -/* 377 */ +/* 371 */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(module) {/* parser generated by jison 0.4.17 */ @@ -38916,10 +36868,10 @@ if ( true && __webpack_require__.c[__webpack_require__.s] === module) { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) /***/ }), -/* 378 */ +/* 372 */ /***/ (function(module, exports, __webpack_require__) { -var licenseIDs = __webpack_require__(379); +var licenseIDs = __webpack_require__(373); function valid(string) { return licenseIDs.indexOf(string) > -1; @@ -39159,20 +37111,20 @@ module.exports = function(identifier) { /***/ }), -/* 379 */ +/* 373 */ /***/ (function(module) { module.exports = JSON.parse("[\"Glide\",\"Abstyles\",\"AFL-1.1\",\"AFL-1.2\",\"AFL-2.0\",\"AFL-2.1\",\"AFL-3.0\",\"AMPAS\",\"APL-1.0\",\"Adobe-Glyph\",\"APAFML\",\"Adobe-2006\",\"AGPL-1.0\",\"Afmparse\",\"Aladdin\",\"ADSL\",\"AMDPLPA\",\"ANTLR-PD\",\"Apache-1.0\",\"Apache-1.1\",\"Apache-2.0\",\"AML\",\"APSL-1.0\",\"APSL-1.1\",\"APSL-1.2\",\"APSL-2.0\",\"Artistic-1.0\",\"Artistic-1.0-Perl\",\"Artistic-1.0-cl8\",\"Artistic-2.0\",\"AAL\",\"Bahyph\",\"Barr\",\"Beerware\",\"BitTorrent-1.0\",\"BitTorrent-1.1\",\"BSL-1.0\",\"Borceux\",\"BSD-2-Clause\",\"BSD-2-Clause-FreeBSD\",\"BSD-2-Clause-NetBSD\",\"BSD-3-Clause\",\"BSD-3-Clause-Clear\",\"BSD-4-Clause\",\"BSD-Protection\",\"BSD-Source-Code\",\"BSD-3-Clause-Attribution\",\"0BSD\",\"BSD-4-Clause-UC\",\"bzip2-1.0.5\",\"bzip2-1.0.6\",\"Caldera\",\"CECILL-1.0\",\"CECILL-1.1\",\"CECILL-2.0\",\"CECILL-2.1\",\"CECILL-B\",\"CECILL-C\",\"ClArtistic\",\"MIT-CMU\",\"CNRI-Jython\",\"CNRI-Python\",\"CNRI-Python-GPL-Compatible\",\"CPOL-1.02\",\"CDDL-1.0\",\"CDDL-1.1\",\"CPAL-1.0\",\"CPL-1.0\",\"CATOSL-1.1\",\"Condor-1.1\",\"CC-BY-1.0\",\"CC-BY-2.0\",\"CC-BY-2.5\",\"CC-BY-3.0\",\"CC-BY-4.0\",\"CC-BY-ND-1.0\",\"CC-BY-ND-2.0\",\"CC-BY-ND-2.5\",\"CC-BY-ND-3.0\",\"CC-BY-ND-4.0\",\"CC-BY-NC-1.0\",\"CC-BY-NC-2.0\",\"CC-BY-NC-2.5\",\"CC-BY-NC-3.0\",\"CC-BY-NC-4.0\",\"CC-BY-NC-ND-1.0\",\"CC-BY-NC-ND-2.0\",\"CC-BY-NC-ND-2.5\",\"CC-BY-NC-ND-3.0\",\"CC-BY-NC-ND-4.0\",\"CC-BY-NC-SA-1.0\",\"CC-BY-NC-SA-2.0\",\"CC-BY-NC-SA-2.5\",\"CC-BY-NC-SA-3.0\",\"CC-BY-NC-SA-4.0\",\"CC-BY-SA-1.0\",\"CC-BY-SA-2.0\",\"CC-BY-SA-2.5\",\"CC-BY-SA-3.0\",\"CC-BY-SA-4.0\",\"CC0-1.0\",\"Crossword\",\"CrystalStacker\",\"CUA-OPL-1.0\",\"Cube\",\"curl\",\"D-FSL-1.0\",\"diffmark\",\"WTFPL\",\"DOC\",\"Dotseqn\",\"DSDP\",\"dvipdfm\",\"EPL-1.0\",\"ECL-1.0\",\"ECL-2.0\",\"eGenix\",\"EFL-1.0\",\"EFL-2.0\",\"MIT-advertising\",\"MIT-enna\",\"Entessa\",\"ErlPL-1.1\",\"EUDatagrid\",\"EUPL-1.0\",\"EUPL-1.1\",\"Eurosym\",\"Fair\",\"MIT-feh\",\"Frameworx-1.0\",\"FreeImage\",\"FTL\",\"FSFAP\",\"FSFUL\",\"FSFULLR\",\"Giftware\",\"GL2PS\",\"Glulxe\",\"AGPL-3.0\",\"GFDL-1.1\",\"GFDL-1.2\",\"GFDL-1.3\",\"GPL-1.0\",\"GPL-2.0\",\"GPL-3.0\",\"LGPL-2.1\",\"LGPL-3.0\",\"LGPL-2.0\",\"gnuplot\",\"gSOAP-1.3b\",\"HaskellReport\",\"HPND\",\"IBM-pibs\",\"IPL-1.0\",\"ICU\",\"ImageMagick\",\"iMatix\",\"Imlib2\",\"IJG\",\"Info-ZIP\",\"Intel-ACPI\",\"Intel\",\"Interbase-1.0\",\"IPA\",\"ISC\",\"JasPer-2.0\",\"JSON\",\"LPPL-1.0\",\"LPPL-1.1\",\"LPPL-1.2\",\"LPPL-1.3a\",\"LPPL-1.3c\",\"Latex2e\",\"BSD-3-Clause-LBNL\",\"Leptonica\",\"LGPLLR\",\"Libpng\",\"libtiff\",\"LAL-1.2\",\"LAL-1.3\",\"LiLiQ-P-1.1\",\"LiLiQ-Rplus-1.1\",\"LiLiQ-R-1.1\",\"LPL-1.02\",\"LPL-1.0\",\"MakeIndex\",\"MTLL\",\"MS-PL\",\"MS-RL\",\"MirOS\",\"MITNFA\",\"MIT\",\"Motosoto\",\"MPL-1.0\",\"MPL-1.1\",\"MPL-2.0\",\"MPL-2.0-no-copyleft-exception\",\"mpich2\",\"Multics\",\"Mup\",\"NASA-1.3\",\"Naumen\",\"NBPL-1.0\",\"NetCDF\",\"NGPL\",\"NOSL\",\"NPL-1.0\",\"NPL-1.1\",\"Newsletr\",\"NLPL\",\"Nokia\",\"NPOSL-3.0\",\"NLOD-1.0\",\"Noweb\",\"NRL\",\"NTP\",\"Nunit\",\"OCLC-2.0\",\"ODbL-1.0\",\"PDDL-1.0\",\"OCCT-PL\",\"OGTSL\",\"OLDAP-2.2.2\",\"OLDAP-1.1\",\"OLDAP-1.2\",\"OLDAP-1.3\",\"OLDAP-1.4\",\"OLDAP-2.0\",\"OLDAP-2.0.1\",\"OLDAP-2.1\",\"OLDAP-2.2\",\"OLDAP-2.2.1\",\"OLDAP-2.3\",\"OLDAP-2.4\",\"OLDAP-2.5\",\"OLDAP-2.6\",\"OLDAP-2.7\",\"OLDAP-2.8\",\"OML\",\"OPL-1.0\",\"OSL-1.0\",\"OSL-1.1\",\"OSL-2.0\",\"OSL-2.1\",\"OSL-3.0\",\"OpenSSL\",\"OSET-PL-2.1\",\"PHP-3.0\",\"PHP-3.01\",\"Plexus\",\"PostgreSQL\",\"psfrag\",\"psutils\",\"Python-2.0\",\"QPL-1.0\",\"Qhull\",\"Rdisc\",\"RPSL-1.0\",\"RPL-1.1\",\"RPL-1.5\",\"RHeCos-1.1\",\"RSCPL\",\"RSA-MD\",\"Ruby\",\"SAX-PD\",\"Saxpath\",\"SCEA\",\"SWL\",\"SMPPL\",\"Sendmail\",\"SGI-B-1.0\",\"SGI-B-1.1\",\"SGI-B-2.0\",\"OFL-1.0\",\"OFL-1.1\",\"SimPL-2.0\",\"Sleepycat\",\"SNIA\",\"Spencer-86\",\"Spencer-94\",\"Spencer-99\",\"SMLNJ\",\"SugarCRM-1.1.3\",\"SISSL\",\"SISSL-1.2\",\"SPL-1.0\",\"Watcom-1.0\",\"TCL\",\"Unlicense\",\"TMate\",\"TORQUE-1.1\",\"TOSL\",\"Unicode-TOU\",\"UPL-1.0\",\"NCSA\",\"Vim\",\"VOSTROM\",\"VSL-1.0\",\"W3C-19980720\",\"W3C\",\"Wsuipa\",\"Xnet\",\"X11\",\"Xerox\",\"XFree86-1.1\",\"xinetd\",\"xpp\",\"XSkat\",\"YPL-1.0\",\"YPL-1.1\",\"Zed\",\"Zend-2.0\",\"Zimbra-1.3\",\"Zimbra-1.4\",\"Zlib\",\"zlib-acknowledgement\",\"ZPL-1.1\",\"ZPL-2.0\",\"ZPL-2.1\",\"BSD-3-Clause-No-Nuclear-License\",\"BSD-3-Clause-No-Nuclear-Warranty\",\"BSD-3-Clause-No-Nuclear-License-2014\",\"eCos-2.0\",\"GPL-2.0-with-autoconf-exception\",\"GPL-2.0-with-bison-exception\",\"GPL-2.0-with-classpath-exception\",\"GPL-2.0-with-font-exception\",\"GPL-2.0-with-GCC-exception\",\"GPL-3.0-with-autoconf-exception\",\"GPL-3.0-with-GCC-exception\",\"StandardML-NJ\",\"WXwindows\"]"); /***/ }), -/* 380 */ +/* 374 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var url = __webpack_require__(203) -var gitHosts = __webpack_require__(381) -var GitHost = module.exports = __webpack_require__(382) +var gitHosts = __webpack_require__(375) +var GitHost = module.exports = __webpack_require__(376) var protocolToRepresentationMap = { 'git+ssh:': 'sshurl', @@ -39320,7 +37272,7 @@ function parseGitUrl (giturl) { /***/ }), -/* 381 */ +/* 375 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -39406,12 +37358,12 @@ function formatHashFragment (fragment) { /***/ }), -/* 382 */ +/* 376 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var gitHosts = __webpack_require__(381) +var gitHosts = __webpack_require__(375) /* eslint-disable node/no-deprecated-api */ // copy-pasta util._extend from node's source, to avoid pulling @@ -39569,27 +37521,27 @@ GitHost.prototype.toString = function (opts) { /***/ }), -/* 383 */ +/* 377 */ /***/ (function(module, exports, __webpack_require__) { -var async = __webpack_require__(384); -async.core = __webpack_require__(394); -async.isCore = __webpack_require__(396); -async.sync = __webpack_require__(397); +var async = __webpack_require__(378); +async.core = __webpack_require__(388); +async.isCore = __webpack_require__(390); +async.sync = __webpack_require__(391); module.exports = async; /***/ }), -/* 384 */ +/* 378 */ /***/ (function(module, exports, __webpack_require__) { var fs = __webpack_require__(132); var path = __webpack_require__(4); -var caller = __webpack_require__(385); -var nodeModulesPaths = __webpack_require__(386); -var normalizeOptions = __webpack_require__(388); -var isCore = __webpack_require__(389); +var caller = __webpack_require__(379); +var nodeModulesPaths = __webpack_require__(380); +var normalizeOptions = __webpack_require__(382); +var isCore = __webpack_require__(383); var realpathFS = fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; @@ -39907,7 +37859,7 @@ module.exports = function resolve(x, options, callback) { /***/ }), -/* 385 */ +/* 379 */ /***/ (function(module, exports) { module.exports = function () { @@ -39921,11 +37873,11 @@ module.exports = function () { /***/ }), -/* 386 */ +/* 380 */ /***/ (function(module, exports, __webpack_require__) { var path = __webpack_require__(4); -var parse = path.parse || __webpack_require__(387); +var parse = path.parse || __webpack_require__(381); var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) { var prefix = '/'; @@ -39969,7 +37921,7 @@ module.exports = function nodeModulesPaths(start, opts, request) { /***/ }), -/* 387 */ +/* 381 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40051,7 +38003,7 @@ module.exports.win32 = win32.parse; /***/ }), -/* 388 */ +/* 382 */ /***/ (function(module, exports) { module.exports = function (x, opts) { @@ -40067,13 +38019,13 @@ module.exports = function (x, opts) { /***/ }), -/* 389 */ +/* 383 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var has = __webpack_require__(390); +var has = __webpack_require__(384); function specifierIncluded(current, specifier) { var nodeParts = current.split('.'); @@ -40135,7 +38087,7 @@ function versionIncluded(nodeVersion, specifierValue) { return matchesRange(current, specifierValue); } -var data = __webpack_require__(393); +var data = __webpack_require__(387); module.exports = function isCore(x, nodeVersion) { return has(data, x) && versionIncluded(nodeVersion, data[x]); @@ -40143,31 +38095,31 @@ module.exports = function isCore(x, nodeVersion) { /***/ }), -/* 390 */ +/* 384 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var bind = __webpack_require__(391); +var bind = __webpack_require__(385); module.exports = bind.call(Function.call, Object.prototype.hasOwnProperty); /***/ }), -/* 391 */ +/* 385 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var implementation = __webpack_require__(392); +var implementation = __webpack_require__(386); module.exports = Function.prototype.bind || implementation; /***/ }), -/* 392 */ +/* 386 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40226,13 +38178,13 @@ module.exports = function bind(that) { /***/ }), -/* 393 */ +/* 387 */ /***/ (function(module) { module.exports = JSON.parse("{\"assert\":true,\"node:assert\":[\">= 14.18 && < 15\",\">= 16\"],\"assert/strict\":\">= 15\",\"node:assert/strict\":\">= 16\",\"async_hooks\":\">= 8\",\"node:async_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"buffer_ieee754\":\"< 0.9.7\",\"buffer\":true,\"node:buffer\":[\">= 14.18 && < 15\",\">= 16\"],\"child_process\":true,\"node:child_process\":[\">= 14.18 && < 15\",\">= 16\"],\"cluster\":true,\"node:cluster\":[\">= 14.18 && < 15\",\">= 16\"],\"console\":true,\"node:console\":[\">= 14.18 && < 15\",\">= 16\"],\"constants\":true,\"node:constants\":[\">= 14.18 && < 15\",\">= 16\"],\"crypto\":true,\"node:crypto\":[\">= 14.18 && < 15\",\">= 16\"],\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"node:dgram\":[\">= 14.18 && < 15\",\">= 16\"],\"diagnostics_channel\":[\">= 14.17 && < 15\",\">= 15.1\"],\"node:diagnostics_channel\":[\">= 14.18 && < 15\",\">= 16\"],\"dns\":true,\"node:dns\":[\">= 14.18 && < 15\",\">= 16\"],\"dns/promises\":\">= 15\",\"node:dns/promises\":\">= 16\",\"domain\":\">= 0.7.12\",\"node:domain\":[\">= 14.18 && < 15\",\">= 16\"],\"events\":true,\"node:events\":[\">= 14.18 && < 15\",\">= 16\"],\"freelist\":\"< 6\",\"fs\":true,\"node:fs\":[\">= 14.18 && < 15\",\">= 16\"],\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"node:fs/promises\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_agent\":\">= 0.11.1\",\"node:_http_agent\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_client\":\">= 0.11.1\",\"node:_http_client\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_common\":\">= 0.11.1\",\"node:_http_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_incoming\":\">= 0.11.1\",\"node:_http_incoming\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_outgoing\":\">= 0.11.1\",\"node:_http_outgoing\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_server\":\">= 0.11.1\",\"node:_http_server\":[\">= 14.18 && < 15\",\">= 16\"],\"http\":true,\"node:http\":[\">= 14.18 && < 15\",\">= 16\"],\"http2\":\">= 8.8\",\"node:http2\":[\">= 14.18 && < 15\",\">= 16\"],\"https\":true,\"node:https\":[\">= 14.18 && < 15\",\">= 16\"],\"inspector\":\">= 8\",\"node:inspector\":[\">= 14.18 && < 15\",\">= 16\"],\"_linklist\":\"< 8\",\"module\":true,\"node:module\":[\">= 14.18 && < 15\",\">= 16\"],\"net\":true,\"node:net\":[\">= 14.18 && < 15\",\">= 16\"],\"node-inspect/lib/_inspect\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6 && < 12\",\"os\":true,\"node:os\":[\">= 14.18 && < 15\",\">= 16\"],\"path\":true,\"node:path\":[\">= 14.18 && < 15\",\">= 16\"],\"path/posix\":\">= 15.3\",\"node:path/posix\":\">= 16\",\"path/win32\":\">= 15.3\",\"node:path/win32\":\">= 16\",\"perf_hooks\":\">= 8.5\",\"node:perf_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"process\":\">= 1\",\"node:process\":[\">= 14.18 && < 15\",\">= 16\"],\"punycode\":true,\"node:punycode\":[\">= 14.18 && < 15\",\">= 16\"],\"querystring\":true,\"node:querystring\":[\">= 14.18 && < 15\",\">= 16\"],\"readline\":true,\"node:readline\":[\">= 14.18 && < 15\",\">= 16\"],\"repl\":true,\"node:repl\":[\">= 14.18 && < 15\",\">= 16\"],\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"node:_stream_duplex\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_transform\":\">= 0.9.4\",\"node:_stream_transform\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_wrap\":\">= 1.4.1\",\"node:_stream_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_passthrough\":\">= 0.9.4\",\"node:_stream_passthrough\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_readable\":\">= 0.9.4\",\"node:_stream_readable\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_writable\":\">= 0.9.4\",\"node:_stream_writable\":[\">= 14.18 && < 15\",\">= 16\"],\"stream\":true,\"node:stream\":[\">= 14.18 && < 15\",\">= 16\"],\"stream/consumers\":\">= 16.7\",\"node:stream/consumers\":\">= 16.7\",\"stream/promises\":\">= 15\",\"node:stream/promises\":\">= 16\",\"stream/web\":\">= 16.5\",\"node:stream/web\":\">= 16.5\",\"string_decoder\":true,\"node:string_decoder\":[\">= 14.18 && < 15\",\">= 16\"],\"sys\":[\">= 0.6 && < 0.7\",\">= 0.8\"],\"node:sys\":[\">= 14.18 && < 15\",\">= 16\"],\"timers\":true,\"node:timers\":[\">= 14.18 && < 15\",\">= 16\"],\"timers/promises\":\">= 15\",\"node:timers/promises\":\">= 16\",\"_tls_common\":\">= 0.11.13\",\"node:_tls_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"node:_tls_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"tls\":true,\"node:tls\":[\">= 14.18 && < 15\",\">= 16\"],\"trace_events\":\">= 10\",\"node:trace_events\":[\">= 14.18 && < 15\",\">= 16\"],\"tty\":true,\"node:tty\":[\">= 14.18 && < 15\",\">= 16\"],\"url\":true,\"node:url\":[\">= 14.18 && < 15\",\">= 16\"],\"util\":true,\"node:util\":[\">= 14.18 && < 15\",\">= 16\"],\"util/types\":\">= 15.3\",\"node:util/types\":\">= 16\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/consarray\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/logreader\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8\":\">= 1\",\"node:v8\":[\">= 14.18 && < 15\",\">= 16\"],\"vm\":true,\"node:vm\":[\">= 14.18 && < 15\",\">= 16\"],\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"node:worker_threads\":[\">= 14.18 && < 15\",\">= 16\"],\"zlib\":true,\"node:zlib\":[\">= 14.18 && < 15\",\">= 16\"]}"); /***/ }), -/* 394 */ +/* 388 */ /***/ (function(module, exports, __webpack_require__) { var current = (process.versions && process.versions.node && process.versions.node.split('.')) || []; @@ -40279,7 +38231,7 @@ function versionIncluded(specifierValue) { return matchesRange(specifierValue); } -var data = __webpack_require__(395); +var data = __webpack_require__(389); var core = {}; for (var mod in data) { // eslint-disable-line no-restricted-syntax @@ -40291,16 +38243,16 @@ module.exports = core; /***/ }), -/* 395 */ +/* 389 */ /***/ (function(module) { module.exports = JSON.parse("{\"assert\":true,\"assert/strict\":\">= 15\",\"async_hooks\":\">= 8\",\"buffer_ieee754\":\"< 0.9.7\",\"buffer\":true,\"child_process\":true,\"cluster\":true,\"console\":true,\"constants\":true,\"crypto\":true,\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"diagnostics_channel\":\">= 15.1\",\"dns\":true,\"dns/promises\":\">= 15\",\"domain\":\">= 0.7.12\",\"events\":true,\"freelist\":\"< 6\",\"fs\":true,\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"_http_agent\":\">= 0.11.1\",\"_http_client\":\">= 0.11.1\",\"_http_common\":\">= 0.11.1\",\"_http_incoming\":\">= 0.11.1\",\"_http_outgoing\":\">= 0.11.1\",\"_http_server\":\">= 0.11.1\",\"http\":true,\"http2\":\">= 8.8\",\"https\":true,\"inspector\":\">= 8.0.0\",\"_linklist\":\"< 8\",\"module\":true,\"net\":true,\"node-inspect/lib/_inspect\":\">= 7.6.0 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6.0 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6.0 && < 12\",\"os\":true,\"path\":true,\"path/posix\":\">= 15.3\",\"path/win32\":\">= 15.3\",\"perf_hooks\":\">= 8.5\",\"process\":\">= 1\",\"punycode\":true,\"querystring\":true,\"readline\":true,\"repl\":true,\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"_stream_transform\":\">= 0.9.4\",\"_stream_wrap\":\">= 1.4.1\",\"_stream_passthrough\":\">= 0.9.4\",\"_stream_readable\":\">= 0.9.4\",\"_stream_writable\":\">= 0.9.4\",\"stream\":true,\"stream/promises\":\">= 15\",\"string_decoder\":true,\"sys\":[\">= 0.6 && < 0.7\",\">= 0.8\"],\"timers\":true,\"timers/promises\":\">= 15\",\"_tls_common\":\">= 0.11.13\",\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"tls\":true,\"trace_events\":\">= 10\",\"tty\":true,\"url\":true,\"util\":true,\"util/types\":\">= 15.3\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/consarray\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/logreader\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8\":\">= 1\",\"vm\":true,\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"zlib\":true}"); /***/ }), -/* 396 */ +/* 390 */ /***/ (function(module, exports, __webpack_require__) { -var isCoreModule = __webpack_require__(389); +var isCoreModule = __webpack_require__(383); module.exports = function isCore(x) { return isCoreModule(x); @@ -40308,15 +38260,15 @@ module.exports = function isCore(x) { /***/ }), -/* 397 */ +/* 391 */ /***/ (function(module, exports, __webpack_require__) { -var isCore = __webpack_require__(389); +var isCore = __webpack_require__(383); var fs = __webpack_require__(132); var path = __webpack_require__(4); -var caller = __webpack_require__(385); -var nodeModulesPaths = __webpack_require__(386); -var normalizeOptions = __webpack_require__(388); +var caller = __webpack_require__(379); +var nodeModulesPaths = __webpack_require__(380); +var normalizeOptions = __webpack_require__(382); var realpathFS = fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; @@ -40513,7 +38465,7 @@ module.exports = function resolveSync(x, options) { /***/ }), -/* 398 */ +/* 392 */ /***/ (function(module, exports) { module.exports = extractDescription @@ -40533,17 +38485,17 @@ function extractDescription (d) { /***/ }), -/* 399 */ +/* 393 */ /***/ (function(module) { module.exports = JSON.parse("{\"topLevel\":{\"dependancies\":\"dependencies\",\"dependecies\":\"dependencies\",\"depdenencies\":\"dependencies\",\"devEependencies\":\"devDependencies\",\"depends\":\"dependencies\",\"dev-dependencies\":\"devDependencies\",\"devDependences\":\"devDependencies\",\"devDepenencies\":\"devDependencies\",\"devdependencies\":\"devDependencies\",\"repostitory\":\"repository\",\"repo\":\"repository\",\"prefereGlobal\":\"preferGlobal\",\"hompage\":\"homepage\",\"hampage\":\"homepage\",\"autohr\":\"author\",\"autor\":\"author\",\"contributers\":\"contributors\",\"publicationConfig\":\"publishConfig\",\"script\":\"scripts\"},\"bugs\":{\"web\":\"url\",\"name\":\"url\"},\"script\":{\"server\":\"start\",\"tests\":\"test\"}}"); /***/ }), -/* 400 */ +/* 394 */ /***/ (function(module, exports, __webpack_require__) { var util = __webpack_require__(113) -var messages = __webpack_require__(401) +var messages = __webpack_require__(395) module.exports = function() { var args = Array.prototype.slice.call(arguments, 0) @@ -40568,20 +38520,20 @@ function makeTypoWarning (providedName, probableName, field) { /***/ }), -/* 401 */ +/* 395 */ /***/ (function(module) { module.exports = JSON.parse("{\"repositories\":\"'repositories' (plural) Not supported. Please pick one as the 'repository' field\",\"missingRepository\":\"No repository field.\",\"brokenGitUrl\":\"Probably broken git url: %s\",\"nonObjectScripts\":\"scripts must be an object\",\"nonStringScript\":\"script values must be string commands\",\"nonArrayFiles\":\"Invalid 'files' member\",\"invalidFilename\":\"Invalid filename in 'files' list: %s\",\"nonArrayBundleDependencies\":\"Invalid 'bundleDependencies' list. Must be array of package names\",\"nonStringBundleDependency\":\"Invalid bundleDependencies member: %s\",\"nonDependencyBundleDependency\":\"Non-dependency in bundleDependencies: %s\",\"nonObjectDependencies\":\"%s field must be an object\",\"nonStringDependency\":\"Invalid dependency: %s %s\",\"deprecatedArrayDependencies\":\"specifying %s as array is deprecated\",\"deprecatedModules\":\"modules field is deprecated\",\"nonArrayKeywords\":\"keywords should be an array of strings\",\"nonStringKeyword\":\"keywords should be an array of strings\",\"conflictingName\":\"%s is also the name of a node core module.\",\"nonStringDescription\":\"'description' field should be a string\",\"missingDescription\":\"No description\",\"missingReadme\":\"No README data\",\"missingLicense\":\"No license field.\",\"nonEmailUrlBugsString\":\"Bug string field must be url, email, or {email,url}\",\"nonUrlBugsUrlField\":\"bugs.url field must be a string url. Deleted.\",\"nonEmailBugsEmailField\":\"bugs.email field must be a string email. Deleted.\",\"emptyNormalizedBugs\":\"Normalized value of bugs field is an empty object. Deleted.\",\"nonUrlHomepage\":\"homepage field must be a string url. Deleted.\",\"invalidLicense\":\"license should be a valid SPDX license expression\",\"typo\":\"%s should probably be %s.\"}"); /***/ }), -/* 402 */ +/* 396 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const writeJsonFile = __webpack_require__(403); -const sortKeys = __webpack_require__(407); +const writeJsonFile = __webpack_require__(397); +const sortKeys = __webpack_require__(401); const dependencyKeys = new Set([ 'dependencies', @@ -40646,18 +38598,18 @@ module.exports.sync = (filePath, data, options) => { /***/ }), -/* 403 */ +/* 397 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const fs = __webpack_require__(233); -const writeFileAtomic = __webpack_require__(404); -const sortKeys = __webpack_require__(407); -const makeDir = __webpack_require__(409); -const pify = __webpack_require__(410); -const detectIndent = __webpack_require__(412); +const writeFileAtomic = __webpack_require__(398); +const sortKeys = __webpack_require__(401); +const makeDir = __webpack_require__(403); +const pify = __webpack_require__(404); +const detectIndent = __webpack_require__(406); const init = (fn, filePath, data, options) => { if (!filePath) { @@ -40729,7 +38681,7 @@ module.exports.sync = (filePath, data, options) => { /***/ }), -/* 404 */ +/* 398 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40740,7 +38692,7 @@ module.exports._getTmpname = getTmpname // for testing module.exports._cleanupOnExit = cleanupOnExit var fs = __webpack_require__(233) -var MurmurHash3 = __webpack_require__(405) +var MurmurHash3 = __webpack_require__(399) var onExit = __webpack_require__(161) var path = __webpack_require__(4) var activeFiles = {} @@ -40749,7 +38701,7 @@ var activeFiles = {} /* istanbul ignore next */ var threadId = (function getId () { try { - var workerThreads = __webpack_require__(406) + var workerThreads = __webpack_require__(400) /// if we are in main thread, this is set to `0` return workerThreads.threadId @@ -40974,7 +38926,7 @@ function writeFileSync (filename, data, options) { /***/ }), -/* 405 */ +/* 399 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -41116,18 +39068,18 @@ function writeFileSync (filename, data, options) { /***/ }), -/* 406 */ +/* 400 */ /***/ (function(module, exports) { module.exports = require(undefined); /***/ }), -/* 407 */ +/* 401 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isPlainObj = __webpack_require__(408); +const isPlainObj = __webpack_require__(402); module.exports = (obj, opts) => { if (!isPlainObj(obj)) { @@ -41184,7 +39136,7 @@ module.exports = (obj, opts) => { /***/ }), -/* 408 */ +/* 402 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41198,15 +39150,15 @@ module.exports = function (x) { /***/ }), -/* 409 */ +/* 403 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); const path = __webpack_require__(4); -const pify = __webpack_require__(410); -const semver = __webpack_require__(411); +const pify = __webpack_require__(404); +const semver = __webpack_require__(405); const defaults = { mode: 0o777 & (~process.umask()), @@ -41344,7 +39296,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 410 */ +/* 404 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41419,7 +39371,7 @@ module.exports = (input, options) => { /***/ }), -/* 411 */ +/* 405 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -42908,7 +40860,7 @@ function coerce (version) { /***/ }), -/* 412 */ +/* 406 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43037,7 +40989,7 @@ module.exports = str => { /***/ }), -/* 413 */ +/* 407 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -43097,14 +41049,14 @@ function runScriptInPackageStreaming({ } /***/ }), -/* 414 */ +/* 408 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readYarnLock", function() { return readYarnLock; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resolveDepsForProject", function() { return resolveDepsForProject; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(415); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(409); /* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(231); /* @@ -43206,7 +41158,7 @@ function resolveDepsForProject({ } /***/ }), -/* 415 */ +/* 409 */ /***/ (function(module, exports, __webpack_require__) { module.exports = @@ -47089,7 +45041,7 @@ function onceStrict (fn) { /* 63 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(416); +module.exports = __webpack_require__(410); /***/ }), /* 64 */, @@ -53484,21 +51436,21 @@ module.exports = process && support(supportLevel); /******/ ]); /***/ }), -/* 416 */ +/* 410 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), -/* 417 */ +/* 411 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sortPackageJson", function() { return sortPackageJson; }); -/* harmony import */ var fs_promises__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(418); +/* harmony import */ var fs_promises__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(412); /* harmony import */ var fs_promises__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs_promises__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var sort_package_json__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(419); +/* harmony import */ var sort_package_json__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(413); /* harmony import */ var sort_package_json__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(sort_package_json__WEBPACK_IMPORTED_MODULE_1__); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one @@ -53519,20 +51471,20 @@ async function sortPackageJson(kbn) { } /***/ }), -/* 418 */ +/* 412 */ /***/ (function(module, exports) { module.exports = require("fs/promises"); /***/ }), -/* 419 */ +/* 413 */ /***/ (function(module, exports, __webpack_require__) { -const sortObjectKeys = __webpack_require__(420) -const detectIndent = __webpack_require__(421) -const detectNewline = __webpack_require__(422).graceful -const gitHooks = __webpack_require__(423) -const isPlainObject = __webpack_require__(424) +const sortObjectKeys = __webpack_require__(414) +const detectIndent = __webpack_require__(415) +const detectNewline = __webpack_require__(416).graceful +const gitHooks = __webpack_require__(417) +const isPlainObject = __webpack_require__(418) const hasOwnProperty = (object, property) => Object.prototype.hasOwnProperty.call(object, property) @@ -53891,7 +51843,7 @@ module.exports.default = sortPackageJson /***/ }), -/* 420 */ +/* 414 */ /***/ (function(module, exports) { module.exports = function sortObjectByKeyNameList(object, sortWith) { @@ -53915,7 +51867,7 @@ module.exports = function sortObjectByKeyNameList(object, sortWith) { /***/ }), -/* 421 */ +/* 415 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54082,7 +52034,7 @@ module.exports = string => { /***/ }), -/* 422 */ +/* 416 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54110,13 +52062,13 @@ module.exports.graceful = string => (typeof string === 'string' && detectNewline /***/ }), -/* 423 */ +/* 417 */ /***/ (function(module) { module.exports = JSON.parse("[\"applypatch-msg\",\"pre-applypatch\",\"post-applypatch\",\"pre-commit\",\"pre-merge-commit\",\"prepare-commit-msg\",\"commit-msg\",\"post-commit\",\"pre-rebase\",\"post-checkout\",\"post-merge\",\"pre-push\",\"pre-receive\",\"update\",\"post-receive\",\"post-update\",\"push-to-checkout\",\"pre-auto-gc\",\"post-rewrite\",\"sendemail-validate\",\"fsmonitor-watchman\",\"p4-pre-submit\",\"post-index-change\"]"); /***/ }), -/* 424 */ +/* 418 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54133,13 +52085,13 @@ module.exports = value => { /***/ }), -/* 425 */ +/* 419 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateDependencies", function() { return validateDependencies; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(415); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(409); /* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_1__); @@ -54149,8 +52101,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(231); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); -/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(349); -/* harmony import */ var _projects_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(426); +/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(343); +/* harmony import */ var _projects_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(420); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -54331,7 +52283,7 @@ function getDevOnlyProductionDepsTree(kbn, projectName) { } /***/ }), -/* 426 */ +/* 420 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54473,27 +52425,27 @@ function addProjectToTree(tree, pathParts, project) { } /***/ }), -/* 427 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(428); +/* harmony import */ var _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(422); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "yarnIntegrityFileExists", function() { return _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__["yarnIntegrityFileExists"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ensureYarnIntegrityFileExists", function() { return _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__["ensureYarnIntegrityFileExists"]; }); -/* harmony import */ var _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(429); +/* harmony import */ var _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(423); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getBazelDiskCacheFolder", function() { return _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__["getBazelDiskCacheFolder"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getBazelRepositoryCacheFolder", function() { return _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__["getBazelRepositoryCacheFolder"]; }); -/* harmony import */ var _install_tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(430); +/* harmony import */ var _install_tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(424); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isBazelBinAvailable", function() { return _install_tools__WEBPACK_IMPORTED_MODULE_2__["isBazelBinAvailable"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "installBazelTools", function() { return _install_tools__WEBPACK_IMPORTED_MODULE_2__["installBazelTools"]; }); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(431); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(425); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "runBazel", function() { return _run__WEBPACK_IMPORTED_MODULE_3__["runBazel"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "runIBazel", function() { return _run__WEBPACK_IMPORTED_MODULE_3__["runIBazel"]; }); @@ -54511,7 +52463,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 428 */ +/* 422 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54558,7 +52510,7 @@ async function ensureYarnIntegrityFileExists(nodeModulesPath) { } /***/ }), -/* 429 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54595,7 +52547,7 @@ async function getBazelRepositoryCacheFolder() { } /***/ }), -/* 430 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54714,7 +52666,7 @@ async function installBazelTools(repoRootPath) { } /***/ }), -/* 431 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54724,12 +52676,12 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(114); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(432); -/* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(530); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(426); +/* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(524); /* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(221); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); -/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(347); +/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(341); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -54791,141 +52743,141 @@ async function runIBazel(bazelArgs, offline = false, runOpts = {}) { } /***/ }), -/* 432 */ +/* 426 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(433); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(427); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); -/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(434); +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(428); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); -/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(435); +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(429); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); -/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(436); +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(430); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); -/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(437); +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(431); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); -/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(438); +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(432); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); -/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(439); +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(433); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); -/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(440); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(434); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); -/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(441); +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(435); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); -/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(442); +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(436); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); -/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(443); +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(437); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); /* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(81); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); -/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(444); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(438); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); -/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(445); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(439); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); -/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(446); +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(440); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); -/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(447); +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(441); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); -/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(448); +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(442); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); -/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(449); +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(443); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__["defaultIfEmpty"]; }); -/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(450); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(444); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__["delay"]; }); -/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(452); +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(446); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); -/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(453); +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(447); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); -/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(454); +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(448); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); -/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(455); +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(449); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); -/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(456); +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(450); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); -/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(457); +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(451); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); -/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(460); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(454); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); -/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(461); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(455); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); -/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(462); +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(456); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); -/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(463); +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(457); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); -/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(464); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(458); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); /* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(106); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); -/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(465); +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(459); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); -/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(466); +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(460); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); -/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(467); +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(461); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); -/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(468); +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(462); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); /* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(32); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); -/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(469); +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(463); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); -/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(470); +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(464); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); -/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(471); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(465); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "last", function() { return _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__["last"]; }); /* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(67); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); -/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(473); +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(467); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); -/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(474); +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(468); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); -/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(475); +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(469); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); -/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(478); +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(472); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); /* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(82); @@ -54936,175 +52888,175 @@ __webpack_require__.r(__webpack_exports__); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["flatMap"]; }); -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(479); +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(473); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); -/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(480); +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(474); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); -/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(481); +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(475); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); -/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(482); +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(476); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); /* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(42); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); -/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(483); +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(477); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); -/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(484); +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(478); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); -/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(485); +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(479); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); -/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(486); +/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(480); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); -/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(487); +/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(481); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); -/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(488); +/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(482); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); -/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(489); +/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(483); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); -/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(490); +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(484); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); -/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(491); +/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(485); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(476); +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(470); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); -/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(492); +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(486); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(493); +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(487); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); -/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(494); +/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(488); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(495); +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(489); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); /* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(31); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); -/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(496); +/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(490); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); -/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(497); +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(491); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(477); +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(471); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); -/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(498); +/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(492); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(499); +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(493); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(500); +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(494); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); -/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(501); +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(495); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(502); +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(496); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); -/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(503); +/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(497); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(504); +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(498); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(505); +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(499); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); -/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(506); +/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(500); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); -/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(507); +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(501); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); -/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(509); +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(503); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); -/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(510); +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(504); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(511); +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(505); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); -/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(459); +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(453); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(472); +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(466); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); -/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(512); +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(506); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); -/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(513); +/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(507); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); -/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(514); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(508); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(515); +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(509); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); -/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(516); +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(510); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); -/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(458); +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(452); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); -/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(517); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(511); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); -/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(518); +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(512); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); -/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(519); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(513); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); -/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(520); +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(514); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(521); +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(515); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(522); +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(516); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); -/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(523); +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(517); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(524); +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(518); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); -/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(525); +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(519); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(526); +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(520); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(527); +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(521); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__["withLatestFrom"]; }); -/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(528); +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(522); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__["zip"]; }); -/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(529); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(523); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__["zipAll"]; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ @@ -55215,7 +53167,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 433 */ +/* 427 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55294,14 +53246,14 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 434 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(433); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(427); /* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(109); /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ @@ -55317,7 +53269,7 @@ function auditTime(duration, scheduler) { /***/ }), -/* 435 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55364,7 +53316,7 @@ var BufferSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 436 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55465,7 +53417,7 @@ var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 437 */ +/* 431 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55626,7 +53578,7 @@ function dispatchBufferClose(arg) { /***/ }), -/* 438 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55745,7 +53697,7 @@ var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 439 */ +/* 433 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55838,7 +53790,7 @@ var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 440 */ +/* 434 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55898,7 +53850,7 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 441 */ +/* 435 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55914,7 +53866,7 @@ function combineAll(project) { /***/ }), -/* 442 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55946,7 +53898,7 @@ function combineLatest() { /***/ }), -/* 443 */ +/* 437 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55966,7 +53918,7 @@ function concat() { /***/ }), -/* 444 */ +/* 438 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55982,13 +53934,13 @@ function concatMap(project, resultSelector) { /***/ }), -/* 445 */ +/* 439 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); -/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(444); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(438); /** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ function concatMapTo(innerObservable, resultSelector) { @@ -55998,7 +53950,7 @@ function concatMapTo(innerObservable, resultSelector) { /***/ }), -/* 446 */ +/* 440 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56063,7 +54015,7 @@ var CountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 447 */ +/* 441 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56148,7 +54100,7 @@ var DebounceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 448 */ +/* 442 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56224,7 +54176,7 @@ function dispatchNext(subscriber) { /***/ }), -/* 449 */ +/* 443 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56274,7 +54226,7 @@ var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 450 */ +/* 444 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56282,7 +54234,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(451); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(445); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); /* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(43); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -56381,7 +54333,7 @@ var DelayMessage = /*@__PURE__*/ (function () { /***/ }), -/* 451 */ +/* 445 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56395,7 +54347,7 @@ function isDate(value) { /***/ }), -/* 452 */ +/* 446 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56541,7 +54493,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 453 */ +/* 447 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56579,7 +54531,7 @@ var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 454 */ +/* 448 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56655,7 +54607,7 @@ var DistinctSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 455 */ +/* 449 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56726,13 +54678,13 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 456 */ +/* 450 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); -/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(455); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(449); /** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ function distinctUntilKeyChanged(key, compare) { @@ -56742,7 +54694,7 @@ function distinctUntilKeyChanged(key, compare) { /***/ }), -/* 457 */ +/* 451 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56750,9 +54702,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return elementAt; }); /* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(458); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(449); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(459); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(452); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(443); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(453); /** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ @@ -56774,7 +54726,7 @@ function elementAt(index, defaultValue) { /***/ }), -/* 458 */ +/* 452 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56840,7 +54792,7 @@ function defaultErrorFactory() { /***/ }), -/* 459 */ +/* 453 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56902,7 +54854,7 @@ var TakeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 460 */ +/* 454 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56924,7 +54876,7 @@ function endWith() { /***/ }), -/* 461 */ +/* 455 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56986,7 +54938,7 @@ var EverySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 462 */ +/* 456 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57040,7 +54992,7 @@ var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 463 */ +/* 457 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57134,7 +55086,7 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 464 */ +/* 458 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57246,7 +55198,7 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 465 */ +/* 459 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57284,7 +55236,7 @@ var FinallySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 466 */ +/* 460 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57356,13 +55308,13 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 467 */ +/* 461 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); -/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(466); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(460); /** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ function findIndex(predicate, thisArg) { @@ -57372,7 +55324,7 @@ function findIndex(predicate, thisArg) { /***/ }), -/* 468 */ +/* 462 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57380,9 +55332,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "first", function() { return first; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(64); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(459); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(449); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(458); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(453); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(443); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(452); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26); /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -57399,7 +55351,7 @@ function first(predicate, defaultValue) { /***/ }), -/* 469 */ +/* 463 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57436,7 +55388,7 @@ var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 470 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57480,7 +55432,7 @@ var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 471 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57488,9 +55440,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(64); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(472); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(458); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(449); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(466); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(452); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(443); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26); /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -57507,7 +55459,7 @@ function last(predicate, defaultValue) { /***/ }), -/* 472 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57584,7 +55536,7 @@ var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 473 */ +/* 467 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57623,7 +55575,7 @@ var MapToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 474 */ +/* 468 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57673,13 +55625,13 @@ var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 475 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(476); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(470); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function max(comparer) { @@ -57692,15 +55644,15 @@ function max(comparer) { /***/ }), -/* 476 */ +/* 470 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(477); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(472); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(449); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(471); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(466); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(443); /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(25); /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ @@ -57721,7 +55673,7 @@ function reduce(accumulator, seed) { /***/ }), -/* 477 */ +/* 471 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57803,7 +55755,7 @@ var ScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 478 */ +/* 472 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57823,7 +55775,7 @@ function merge() { /***/ }), -/* 479 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57848,7 +55800,7 @@ function mergeMapTo(innerObservable, resultSelector, concurrent) { /***/ }), -/* 480 */ +/* 474 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57957,13 +55909,13 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 481 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(476); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(470); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function min(comparer) { @@ -57976,7 +55928,7 @@ function min(comparer) { /***/ }), -/* 482 */ +/* 476 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58025,7 +55977,7 @@ var MulticastOperator = /*@__PURE__*/ (function () { /***/ }), -/* 483 */ +/* 477 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58115,7 +56067,7 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 484 */ +/* 478 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58163,7 +56115,7 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 485 */ +/* 479 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58186,7 +56138,7 @@ function partition(predicate, thisArg) { /***/ }), -/* 486 */ +/* 480 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58226,14 +56178,14 @@ function plucker(props, length) { /***/ }), -/* 487 */ +/* 481 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return publish; }); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(28); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(482); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(476); /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ @@ -58246,14 +56198,14 @@ function publish(selector) { /***/ }), -/* 488 */ +/* 482 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return publishBehavior; }); /* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(482); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(476); /** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ @@ -58264,14 +56216,14 @@ function publishBehavior(value) { /***/ }), -/* 489 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); /* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(482); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(476); /** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ @@ -58282,14 +56234,14 @@ function publishLast() { /***/ }), -/* 490 */ +/* 484 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return publishReplay; }); /* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(34); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(482); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(476); /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ @@ -58305,7 +56257,7 @@ function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { /***/ }), -/* 491 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58332,7 +56284,7 @@ function race() { /***/ }), -/* 492 */ +/* 486 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58397,7 +56349,7 @@ var RepeatSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 493 */ +/* 487 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58491,7 +56443,7 @@ var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 494 */ +/* 488 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58544,7 +56496,7 @@ var RetrySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 495 */ +/* 489 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58630,7 +56582,7 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 496 */ +/* 490 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58685,7 +56637,7 @@ var SampleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 497 */ +/* 491 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58745,7 +56697,7 @@ function dispatchNotification(state) { /***/ }), -/* 498 */ +/* 492 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58868,13 +56820,13 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 499 */ +/* 493 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "share", function() { return share; }); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(482); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(476); /* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(28); /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ @@ -58891,7 +56843,7 @@ function share() { /***/ }), -/* 500 */ +/* 494 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58960,7 +56912,7 @@ function shareReplayOperator(_a) { /***/ }), -/* 501 */ +/* 495 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59040,7 +56992,7 @@ var SingleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 502 */ +/* 496 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59082,7 +57034,7 @@ var SkipSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 503 */ +/* 497 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59144,7 +57096,7 @@ var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 504 */ +/* 498 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59201,7 +57153,7 @@ var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 505 */ +/* 499 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59257,7 +57209,7 @@ var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 506 */ +/* 500 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59286,13 +57238,13 @@ function startWith() { /***/ }), -/* 507 */ +/* 501 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return subscribeOn; }); -/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(508); +/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(502); /** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ function subscribeOn(scheduler, delay) { @@ -59317,7 +57269,7 @@ var SubscribeOnOperator = /*@__PURE__*/ (function () { /***/ }), -/* 508 */ +/* 502 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59381,13 +57333,13 @@ var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { /***/ }), -/* 509 */ +/* 503 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return switchAll; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(510); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(504); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(26); /** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ @@ -59399,7 +57351,7 @@ function switchAll() { /***/ }), -/* 510 */ +/* 504 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59487,13 +57439,13 @@ var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 511 */ +/* 505 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return switchMapTo; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(510); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(504); /** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ function switchMapTo(innerObservable, resultSelector) { @@ -59503,7 +57455,7 @@ function switchMapTo(innerObservable, resultSelector) { /***/ }), -/* 512 */ +/* 506 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59551,7 +57503,7 @@ var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 513 */ +/* 507 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59619,7 +57571,7 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 514 */ +/* 508 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59707,7 +57659,7 @@ var TapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 515 */ +/* 509 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59809,7 +57761,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 516 */ +/* 510 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59818,7 +57770,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(515); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(509); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ @@ -59907,7 +57859,7 @@ function dispatchNext(arg) { /***/ }), -/* 517 */ +/* 511 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59915,7 +57867,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return timeInterval; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeInterval", function() { return TimeInterval; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(477); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(471); /* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(92); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(67); /** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ @@ -59951,7 +57903,7 @@ var TimeInterval = /*@__PURE__*/ (function () { /***/ }), -/* 518 */ +/* 512 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59959,7 +57911,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return timeout; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); /* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(65); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(519); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(513); /* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(50); /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ @@ -59976,7 +57928,7 @@ function timeout(due, scheduler) { /***/ }), -/* 519 */ +/* 513 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59984,7 +57936,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(451); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(445); /* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ @@ -60055,7 +58007,7 @@ var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 520 */ +/* 514 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60085,13 +58037,13 @@ var Timestamp = /*@__PURE__*/ (function () { /***/ }), -/* 521 */ +/* 515 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return toArray; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(476); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(470); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function toArrayReducer(arr, item, index) { @@ -60108,7 +58060,7 @@ function toArray() { /***/ }), -/* 522 */ +/* 516 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60186,7 +58138,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 523 */ +/* 517 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60276,7 +58228,7 @@ var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 524 */ +/* 518 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60446,7 +58398,7 @@ function dispatchWindowClose(state) { /***/ }), -/* 525 */ +/* 519 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60589,7 +58541,7 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 526 */ +/* 520 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60686,7 +58638,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 527 */ +/* 521 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60781,7 +58733,7 @@ var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 528 */ +/* 522 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60803,7 +58755,7 @@ function zip() { /***/ }), -/* 529 */ +/* 523 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -60819,7 +58771,7 @@ function zipAll(project) { /***/ }), -/* 530 */ +/* 524 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60829,7 +58781,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _observe_lines = __webpack_require__(531); +var _observe_lines = __webpack_require__(525); Object.keys(_observe_lines).forEach(function (key) { if (key === "default" || key === "__esModule") return; @@ -60842,7 +58794,7 @@ Object.keys(_observe_lines).forEach(function (key) { }); }); -var _observe_readable = __webpack_require__(532); +var _observe_readable = __webpack_require__(526); Object.keys(_observe_readable).forEach(function (key) { if (key === "default" || key === "__esModule") return; @@ -60856,7 +58808,7 @@ Object.keys(_observe_readable).forEach(function (key) { }); /***/ }), -/* 531 */ +/* 525 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60869,9 +58821,9 @@ exports.observeLines = observeLines; var Rx = _interopRequireWildcard(__webpack_require__(9)); -var _operators = __webpack_require__(432); +var _operators = __webpack_require__(426); -var _observe_readable = __webpack_require__(532); +var _observe_readable = __webpack_require__(526); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } @@ -60934,7 +58886,7 @@ function observeLines(readable) { } /***/ }), -/* 532 */ +/* 526 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60947,7 +58899,7 @@ exports.observeReadable = observeReadable; var Rx = _interopRequireWildcard(__webpack_require__(9)); -var _operators = __webpack_require__(432); +var _operators = __webpack_require__(426); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } @@ -60971,13 +58923,13 @@ function observeReadable(readable) { } /***/ }), -/* 533 */ +/* 527 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BuildCommand", function() { return BuildCommand; }); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(427); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(421); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -61005,7 +58957,7 @@ const BuildCommand = { }; /***/ }), -/* 534 */ +/* 528 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -61015,11 +58967,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(535); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(529); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(427); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); /* @@ -61122,20 +59074,20 @@ const CleanCommand = { }; /***/ }), -/* 535 */ +/* 529 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readline = __webpack_require__(536); -const chalk = __webpack_require__(537); -const cliCursor = __webpack_require__(540); -const cliSpinners = __webpack_require__(542); -const logSymbols = __webpack_require__(544); -const stripAnsi = __webpack_require__(550); -const wcwidth = __webpack_require__(552); -const isInteractive = __webpack_require__(556); -const MuteStream = __webpack_require__(557); +const readline = __webpack_require__(530); +const chalk = __webpack_require__(531); +const cliCursor = __webpack_require__(534); +const cliSpinners = __webpack_require__(536); +const logSymbols = __webpack_require__(538); +const stripAnsi = __webpack_require__(544); +const wcwidth = __webpack_require__(546); +const isInteractive = __webpack_require__(550); +const MuteStream = __webpack_require__(551); const TEXT = Symbol('text'); const PREFIX_TEXT = Symbol('prefixText'); @@ -61488,13 +59440,13 @@ module.exports.promise = (action, options) => { /***/ }), -/* 536 */ +/* 530 */ /***/ (function(module, exports) { module.exports = require("readline"); /***/ }), -/* 537 */ +/* 531 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61504,7 +59456,7 @@ const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(121); const { stringReplaceAll, stringEncaseCRLFWithFirstIndex -} = __webpack_require__(538); +} = __webpack_require__(532); // `supportsColor.level` → `ansiStyles.color[name]` mapping const levelMapping = [ @@ -61705,7 +59657,7 @@ const chalkTag = (chalk, ...strings) => { } if (template === undefined) { - template = __webpack_require__(539); + template = __webpack_require__(533); } return template(chalk, parts.join('')); @@ -61734,7 +59686,7 @@ module.exports = chalk; /***/ }), -/* 538 */ +/* 532 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61780,7 +59732,7 @@ module.exports = { /***/ }), -/* 539 */ +/* 533 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61921,12 +59873,12 @@ module.exports = (chalk, temporary) => { /***/ }), -/* 540 */ +/* 534 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const restoreCursor = __webpack_require__(541); +const restoreCursor = __webpack_require__(535); let isHidden = false; @@ -61963,7 +59915,7 @@ exports.toggle = (force, writableStream) => { /***/ }), -/* 541 */ +/* 535 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61979,13 +59931,13 @@ module.exports = onetime(() => { /***/ }), -/* 542 */ +/* 536 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const spinners = Object.assign({}, __webpack_require__(543)); +const spinners = Object.assign({}, __webpack_require__(537)); const spinnersList = Object.keys(spinners); @@ -62003,18 +59955,18 @@ module.exports.default = spinners; /***/ }), -/* 543 */ +/* 537 */ /***/ (function(module) { module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"dots8Bit\":{\"interval\":80,\"frames\":[\"⠀\",\"⠁\",\"⠂\",\"⠃\",\"⠄\",\"⠅\",\"⠆\",\"⠇\",\"⡀\",\"⡁\",\"⡂\",\"⡃\",\"⡄\",\"⡅\",\"⡆\",\"⡇\",\"⠈\",\"⠉\",\"⠊\",\"⠋\",\"⠌\",\"⠍\",\"⠎\",\"⠏\",\"⡈\",\"⡉\",\"⡊\",\"⡋\",\"⡌\",\"⡍\",\"⡎\",\"⡏\",\"⠐\",\"⠑\",\"⠒\",\"⠓\",\"⠔\",\"⠕\",\"⠖\",\"⠗\",\"⡐\",\"⡑\",\"⡒\",\"⡓\",\"⡔\",\"⡕\",\"⡖\",\"⡗\",\"⠘\",\"⠙\",\"⠚\",\"⠛\",\"⠜\",\"⠝\",\"⠞\",\"⠟\",\"⡘\",\"⡙\",\"⡚\",\"⡛\",\"⡜\",\"⡝\",\"⡞\",\"⡟\",\"⠠\",\"⠡\",\"⠢\",\"⠣\",\"⠤\",\"⠥\",\"⠦\",\"⠧\",\"⡠\",\"⡡\",\"⡢\",\"⡣\",\"⡤\",\"⡥\",\"⡦\",\"⡧\",\"⠨\",\"⠩\",\"⠪\",\"⠫\",\"⠬\",\"⠭\",\"⠮\",\"⠯\",\"⡨\",\"⡩\",\"⡪\",\"⡫\",\"⡬\",\"⡭\",\"⡮\",\"⡯\",\"⠰\",\"⠱\",\"⠲\",\"⠳\",\"⠴\",\"⠵\",\"⠶\",\"⠷\",\"⡰\",\"⡱\",\"⡲\",\"⡳\",\"⡴\",\"⡵\",\"⡶\",\"⡷\",\"⠸\",\"⠹\",\"⠺\",\"⠻\",\"⠼\",\"⠽\",\"⠾\",\"⠿\",\"⡸\",\"⡹\",\"⡺\",\"⡻\",\"⡼\",\"⡽\",\"⡾\",\"⡿\",\"⢀\",\"⢁\",\"⢂\",\"⢃\",\"⢄\",\"⢅\",\"⢆\",\"⢇\",\"⣀\",\"⣁\",\"⣂\",\"⣃\",\"⣄\",\"⣅\",\"⣆\",\"⣇\",\"⢈\",\"⢉\",\"⢊\",\"⢋\",\"⢌\",\"⢍\",\"⢎\",\"⢏\",\"⣈\",\"⣉\",\"⣊\",\"⣋\",\"⣌\",\"⣍\",\"⣎\",\"⣏\",\"⢐\",\"⢑\",\"⢒\",\"⢓\",\"⢔\",\"⢕\",\"⢖\",\"⢗\",\"⣐\",\"⣑\",\"⣒\",\"⣓\",\"⣔\",\"⣕\",\"⣖\",\"⣗\",\"⢘\",\"⢙\",\"⢚\",\"⢛\",\"⢜\",\"⢝\",\"⢞\",\"⢟\",\"⣘\",\"⣙\",\"⣚\",\"⣛\",\"⣜\",\"⣝\",\"⣞\",\"⣟\",\"⢠\",\"⢡\",\"⢢\",\"⢣\",\"⢤\",\"⢥\",\"⢦\",\"⢧\",\"⣠\",\"⣡\",\"⣢\",\"⣣\",\"⣤\",\"⣥\",\"⣦\",\"⣧\",\"⢨\",\"⢩\",\"⢪\",\"⢫\",\"⢬\",\"⢭\",\"⢮\",\"⢯\",\"⣨\",\"⣩\",\"⣪\",\"⣫\",\"⣬\",\"⣭\",\"⣮\",\"⣯\",\"⢰\",\"⢱\",\"⢲\",\"⢳\",\"⢴\",\"⢵\",\"⢶\",\"⢷\",\"⣰\",\"⣱\",\"⣲\",\"⣳\",\"⣴\",\"⣵\",\"⣶\",\"⣷\",\"⢸\",\"⢹\",\"⢺\",\"⢻\",\"⢼\",\"⢽\",\"⢾\",\"⢿\",\"⣸\",\"⣹\",\"⣺\",\"⣻\",\"⣼\",\"⣽\",\"⣾\",\"⣿\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕛 \",\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"material\":{\"interval\":17,\"frames\":[\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███████▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"██████████▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"█████████████▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁██████████████▁▁▁▁\",\"▁▁▁██████████████▁▁▁\",\"▁▁▁▁█████████████▁▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁▁█████████████▁▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁▁███████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁▁█████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]},\"grenade\":{\"interval\":80,\"frames\":[\"، \",\"′ \",\" ´ \",\" ‾ \",\" ⸌\",\" ⸊\",\" |\",\" ⁎\",\" ⁕\",\" ෴ \",\" ⁓\",\" \",\" \",\" \"]},\"point\":{\"interval\":125,\"frames\":[\"∙∙∙\",\"●∙∙\",\"∙●∙\",\"∙∙●\",\"∙∙∙\"]},\"layer\":{\"interval\":150,\"frames\":[\"-\",\"=\",\"≡\"]},\"betaWave\":{\"interval\":80,\"frames\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]},\"aesthetic\":{\"interval\":80,\"frames\":[\"▰▱▱▱▱▱▱\",\"▰▰▱▱▱▱▱\",\"▰▰▰▱▱▱▱\",\"▰▰▰▰▱▱▱\",\"▰▰▰▰▰▱▱\",\"▰▰▰▰▰▰▱\",\"▰▰▰▰▰▰▰\",\"▰▱▱▱▱▱▱\"]}}"); /***/ }), -/* 544 */ +/* 538 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(545); +const chalk = __webpack_require__(539); const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; @@ -62036,16 +59988,16 @@ module.exports = isSupported ? main : fallbacks; /***/ }), -/* 545 */ +/* 539 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const escapeStringRegexp = __webpack_require__(363); -const ansiStyles = __webpack_require__(546); -const stdoutColor = __webpack_require__(547).stdout; +const escapeStringRegexp = __webpack_require__(357); +const ansiStyles = __webpack_require__(540); +const stdoutColor = __webpack_require__(541).stdout; -const template = __webpack_require__(549); +const template = __webpack_require__(543); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -62271,12 +60223,12 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 546 */ +/* 540 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(365); +const colorConvert = __webpack_require__(359); const wrapAnsi16 = (fn, offset) => function () { const code = fn.apply(colorConvert, arguments); @@ -62444,13 +60396,13 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) /***/ }), -/* 547 */ +/* 541 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const os = __webpack_require__(122); -const hasFlag = __webpack_require__(548); +const hasFlag = __webpack_require__(542); const env = process.env; @@ -62582,7 +60534,7 @@ module.exports = { /***/ }), -/* 548 */ +/* 542 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62597,7 +60549,7 @@ module.exports = (flag, argv) => { /***/ }), -/* 549 */ +/* 543 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62732,18 +60684,18 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 550 */ +/* 544 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(551); +const ansiRegex = __webpack_require__(545); module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; /***/ }), -/* 551 */ +/* 545 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62760,14 +60712,14 @@ module.exports = ({onlyFirst = false} = {}) => { /***/ }), -/* 552 */ +/* 546 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var defaults = __webpack_require__(553) -var combining = __webpack_require__(555) +var defaults = __webpack_require__(547) +var combining = __webpack_require__(549) var DEFAULTS = { nul: 0, @@ -62866,10 +60818,10 @@ function bisearch(ucs) { /***/ }), -/* 553 */ +/* 547 */ /***/ (function(module, exports, __webpack_require__) { -var clone = __webpack_require__(554); +var clone = __webpack_require__(548); module.exports = function(options, defaults) { options = options || {}; @@ -62884,7 +60836,7 @@ module.exports = function(options, defaults) { }; /***/ }), -/* 554 */ +/* 548 */ /***/ (function(module, exports, __webpack_require__) { var clone = (function() { @@ -63056,7 +61008,7 @@ if ( true && module.exports) { /***/ }), -/* 555 */ +/* 549 */ /***/ (function(module, exports) { module.exports = [ @@ -63112,7 +61064,7 @@ module.exports = [ /***/ }), -/* 556 */ +/* 550 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63128,7 +61080,7 @@ module.exports = ({stream = process.stdout} = {}) => { /***/ }), -/* 557 */ +/* 551 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(173) @@ -63279,7 +61231,7 @@ MuteStream.prototype.close = proxy('close') /***/ }), -/* 558 */ +/* 552 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -63289,11 +61241,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(535); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(529); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(427); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); /* @@ -63402,7 +61354,7 @@ const ResetCommand = { }; /***/ }), -/* 559 */ +/* 553 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -63410,10 +61362,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RunCommand", function() { return RunCommand; }); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(347); +/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(341); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); -/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(560); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(346); +/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(554); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(340); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -63471,7 +61423,7 @@ const RunCommand = { }; /***/ }), -/* 560 */ +/* 554 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -63526,13 +61478,13 @@ async function parallelize(items, fn, concurrency = 4) { } /***/ }), -/* 561 */ +/* 555 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WatchCommand", function() { return WatchCommand; }); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(427); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(421); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -63563,7 +61515,7 @@ const WatchCommand = { }; /***/ }), -/* 562 */ +/* 556 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -63643,7 +61595,7 @@ const PatchNativeModulesCommand = { }; /***/ }), -/* 563 */ +/* 557 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -63651,11 +61603,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runCommand", function() { return runCommand; }); /* harmony import */ var _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(131); /* harmony import */ var _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(347); +/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(341); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(346); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(426); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(564); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(340); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(420); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(558); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -63774,7 +61726,7 @@ function toArray(value) { } /***/ }), -/* 564 */ +/* 558 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -63784,13 +61736,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(132); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(565); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(559); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(339); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(333); /* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(414); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(346); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(568); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(408); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(340); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(562); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -63954,15 +61906,15 @@ class Kibana { } /***/ }), -/* 565 */ +/* 559 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const minimatch = __webpack_require__(247); const arrayUnion = __webpack_require__(242); -const arrayDiffer = __webpack_require__(566); -const arrify = __webpack_require__(567); +const arrayDiffer = __webpack_require__(560); +const arrify = __webpack_require__(561); module.exports = (list, patterns, options = {}) => { list = arrify(list); @@ -63986,7 +61938,7 @@ module.exports = (list, patterns, options = {}) => { /***/ }), -/* 566 */ +/* 560 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64001,7 +61953,7 @@ module.exports = arrayDiffer; /***/ }), -/* 567 */ +/* 561 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64031,7 +61983,7 @@ module.exports = arrify; /***/ }), -/* 568 */ +/* 562 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -64091,15 +62043,15 @@ function getProjectPaths({ } /***/ }), -/* 569 */ +/* 563 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(570); +/* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(564); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildBazelProductionProjects"]; }); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(817); +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(811); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); /* @@ -64113,24 +62065,24 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 570 */ +/* 564 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return buildBazelProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(571); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(565); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(783); +/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(777); /* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(globby__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(817); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(427); +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(811); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); -/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(349); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(346); +/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(343); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(340); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -64220,7 +62172,7 @@ async function applyCorrectPermissions(project, kibanaRoot, buildRoot) { } /***/ }), -/* 571 */ +/* 565 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64228,14 +62180,14 @@ async function applyCorrectPermissions(project, kibanaRoot, buildRoot) { const EventEmitter = __webpack_require__(164); const path = __webpack_require__(4); const os = __webpack_require__(122); -const pMap = __webpack_require__(572); -const arrify = __webpack_require__(567); -const globby = __webpack_require__(575); -const hasGlob = __webpack_require__(767); -const cpFile = __webpack_require__(769); -const junk = __webpack_require__(779); -const pFilter = __webpack_require__(780); -const CpyError = __webpack_require__(782); +const pMap = __webpack_require__(566); +const arrify = __webpack_require__(561); +const globby = __webpack_require__(569); +const hasGlob = __webpack_require__(761); +const cpFile = __webpack_require__(763); +const junk = __webpack_require__(773); +const pFilter = __webpack_require__(774); +const CpyError = __webpack_require__(776); const defaultOptions = { ignoreJunk: true @@ -64386,12 +62338,12 @@ module.exports = (source, destination, { /***/ }), -/* 572 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(573); +const AggregateError = __webpack_require__(567); module.exports = async ( iterable, @@ -64474,13 +62426,13 @@ module.exports = async ( /***/ }), -/* 573 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(574); -const cleanStack = __webpack_require__(344); +const indentString = __webpack_require__(568); +const cleanStack = __webpack_require__(338); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -64528,7 +62480,7 @@ module.exports = AggregateError; /***/ }), -/* 574 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64570,17 +62522,17 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 575 */ +/* 569 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const arrayUnion = __webpack_require__(576); +const arrayUnion = __webpack_require__(570); const glob = __webpack_require__(244); -const fastGlob = __webpack_require__(578); -const dirGlob = __webpack_require__(761); -const gitignore = __webpack_require__(764); +const fastGlob = __webpack_require__(572); +const dirGlob = __webpack_require__(755); +const gitignore = __webpack_require__(758); const DEFAULT_FILTER = () => false; @@ -64725,12 +62677,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 576 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(577); +var arrayUniq = __webpack_require__(571); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -64738,7 +62690,7 @@ module.exports = function () { /***/ }), -/* 577 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64807,10 +62759,10 @@ if ('Set' in global) { /***/ }), -/* 578 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(579); +const pkg = __webpack_require__(573); module.exports = pkg.async; module.exports.default = pkg.async; @@ -64823,19 +62775,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 579 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(580); -var taskManager = __webpack_require__(581); -var reader_async_1 = __webpack_require__(732); -var reader_stream_1 = __webpack_require__(756); -var reader_sync_1 = __webpack_require__(757); -var arrayUtils = __webpack_require__(759); -var streamUtils = __webpack_require__(760); +var optionsManager = __webpack_require__(574); +var taskManager = __webpack_require__(575); +var reader_async_1 = __webpack_require__(726); +var reader_stream_1 = __webpack_require__(750); +var reader_sync_1 = __webpack_require__(751); +var arrayUtils = __webpack_require__(753); +var streamUtils = __webpack_require__(754); /** * Synchronous API. */ @@ -64901,7 +62853,7 @@ function isString(source) { /***/ }), -/* 580 */ +/* 574 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64939,13 +62891,13 @@ exports.prepare = prepare; /***/ }), -/* 581 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(582); +var patternUtils = __webpack_require__(576); /** * Generate tasks based on parent directory of each pattern. */ @@ -65036,16 +62988,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 582 */ +/* 576 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var globParent = __webpack_require__(583); +var globParent = __webpack_require__(577); var isGlob = __webpack_require__(266); -var micromatch = __webpack_require__(586); +var micromatch = __webpack_require__(580); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -65191,15 +63143,15 @@ exports.matchAny = matchAny; /***/ }), -/* 583 */ +/* 577 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(4); -var isglob = __webpack_require__(584); -var pathDirname = __webpack_require__(585); +var isglob = __webpack_require__(578); +var pathDirname = __webpack_require__(579); var isWin32 = __webpack_require__(122).platform() === 'win32'; module.exports = function globParent(str) { @@ -65222,7 +63174,7 @@ module.exports = function globParent(str) { /***/ }), -/* 584 */ +/* 578 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -65253,7 +63205,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 585 */ +/* 579 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65403,7 +63355,7 @@ module.exports.win32 = win32; /***/ }), -/* 586 */ +/* 580 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65414,18 +63366,18 @@ module.exports.win32 = win32; */ var util = __webpack_require__(113); -var braces = __webpack_require__(587); -var toRegex = __webpack_require__(588); -var extend = __webpack_require__(700); +var braces = __webpack_require__(581); +var toRegex = __webpack_require__(582); +var extend = __webpack_require__(694); /** * Local dependencies */ -var compilers = __webpack_require__(702); -var parsers = __webpack_require__(728); -var cache = __webpack_require__(729); -var utils = __webpack_require__(730); +var compilers = __webpack_require__(696); +var parsers = __webpack_require__(722); +var cache = __webpack_require__(723); +var utils = __webpack_require__(724); var MAX_LENGTH = 1024 * 64; /** @@ -66287,7 +64239,7 @@ module.exports = micromatch; /***/ }), -/* 587 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66297,18 +64249,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(588); -var unique = __webpack_require__(608); -var extend = __webpack_require__(609); +var toRegex = __webpack_require__(582); +var unique = __webpack_require__(602); +var extend = __webpack_require__(603); /** * Local dependencies */ -var compilers = __webpack_require__(611); -var parsers = __webpack_require__(626); -var Braces = __webpack_require__(631); -var utils = __webpack_require__(612); +var compilers = __webpack_require__(605); +var parsers = __webpack_require__(620); +var Braces = __webpack_require__(625); +var utils = __webpack_require__(606); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -66612,16 +64564,16 @@ module.exports = braces; /***/ }), -/* 588 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(589); -var define = __webpack_require__(595); -var extend = __webpack_require__(601); -var not = __webpack_require__(605); +var safe = __webpack_require__(583); +var define = __webpack_require__(589); +var extend = __webpack_require__(595); +var not = __webpack_require__(599); var MAX_LENGTH = 1024 * 64; /** @@ -66774,10 +64726,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 589 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(590); +var parse = __webpack_require__(584); var types = parse.types; module.exports = function (re, opts) { @@ -66823,13 +64775,13 @@ function isRegExp (x) { /***/ }), -/* 590 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(591); -var types = __webpack_require__(592); -var sets = __webpack_require__(593); -var positions = __webpack_require__(594); +var util = __webpack_require__(585); +var types = __webpack_require__(586); +var sets = __webpack_require__(587); +var positions = __webpack_require__(588); module.exports = function(regexpStr) { @@ -67111,11 +65063,11 @@ module.exports.types = types; /***/ }), -/* 591 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(592); -var sets = __webpack_require__(593); +var types = __webpack_require__(586); +var sets = __webpack_require__(587); // All of these are private and only used by randexp. @@ -67228,7 +65180,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 592 */ +/* 586 */ /***/ (function(module, exports) { module.exports = { @@ -67244,10 +65196,10 @@ module.exports = { /***/ }), -/* 593 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(592); +var types = __webpack_require__(586); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -67332,10 +65284,10 @@ exports.anyChar = function() { /***/ }), -/* 594 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(592); +var types = __webpack_require__(586); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -67355,7 +65307,7 @@ exports.end = function() { /***/ }), -/* 595 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67368,8 +65320,8 @@ exports.end = function() { -var isobject = __webpack_require__(596); -var isDescriptor = __webpack_require__(597); +var isobject = __webpack_require__(590); +var isDescriptor = __webpack_require__(591); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -67400,7 +65352,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 596 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67419,7 +65371,7 @@ module.exports = function isObject(val) { /***/ }), -/* 597 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67432,9 +65384,9 @@ module.exports = function isObject(val) { -var typeOf = __webpack_require__(598); -var isAccessor = __webpack_require__(599); -var isData = __webpack_require__(600); +var typeOf = __webpack_require__(592); +var isAccessor = __webpack_require__(593); +var isData = __webpack_require__(594); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -67448,7 +65400,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 598 */ +/* 592 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -67583,7 +65535,7 @@ function isBuffer(val) { /***/ }), -/* 599 */ +/* 593 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67596,7 +65548,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(598); +var typeOf = __webpack_require__(592); // accessor descriptor properties var accessor = { @@ -67659,7 +65611,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 600 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67672,7 +65624,7 @@ module.exports = isAccessorDescriptor; -var typeOf = __webpack_require__(598); +var typeOf = __webpack_require__(592); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -67715,14 +65667,14 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 601 */ +/* 595 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(602); -var assignSymbols = __webpack_require__(604); +var isExtendable = __webpack_require__(596); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -67782,7 +65734,7 @@ function isEnum(obj, key) { /***/ }), -/* 602 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67795,7 +65747,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(603); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -67803,7 +65755,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 603 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67816,7 +65768,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(596); +var isObject = __webpack_require__(590); function isObjectObject(o) { return isObject(o) === true @@ -67847,7 +65799,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 604 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67894,14 +65846,14 @@ module.exports = function(receiver, objects) { /***/ }), -/* 605 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(606); -var safe = __webpack_require__(589); +var extend = __webpack_require__(600); +var safe = __webpack_require__(583); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -67973,14 +65925,14 @@ module.exports = toRegex; /***/ }), -/* 606 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(607); -var assignSymbols = __webpack_require__(604); +var isExtendable = __webpack_require__(601); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -68040,7 +65992,7 @@ function isEnum(obj, key) { /***/ }), -/* 607 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68053,7 +66005,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(603); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -68061,7 +66013,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 608 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68111,13 +66063,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 609 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(610); +var isObject = __webpack_require__(604); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -68151,7 +66103,7 @@ function hasOwn(obj, key) { /***/ }), -/* 610 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68171,13 +66123,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 611 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(612); +var utils = __webpack_require__(606); module.exports = function(braces, options) { braces.compiler @@ -68460,25 +66412,25 @@ function hasQueue(node) { /***/ }), -/* 612 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(613); +var splitString = __webpack_require__(607); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(609); -utils.flatten = __webpack_require__(616); -utils.isObject = __webpack_require__(596); -utils.fillRange = __webpack_require__(617); -utils.repeat = __webpack_require__(625); -utils.unique = __webpack_require__(608); +utils.extend = __webpack_require__(603); +utils.flatten = __webpack_require__(610); +utils.isObject = __webpack_require__(590); +utils.fillRange = __webpack_require__(611); +utils.repeat = __webpack_require__(619); +utils.unique = __webpack_require__(602); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -68810,7 +66762,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 613 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68823,7 +66775,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(614); +var extend = __webpack_require__(608); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -68988,14 +66940,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 614 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(615); -var assignSymbols = __webpack_require__(604); +var isExtendable = __webpack_require__(609); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -69055,7 +67007,7 @@ function isEnum(obj, key) { /***/ }), -/* 615 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69068,7 +67020,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(603); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -69076,7 +67028,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 616 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69105,7 +67057,7 @@ function flat(arr, res) { /***/ }), -/* 617 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69119,10 +67071,10 @@ function flat(arr, res) { var util = __webpack_require__(113); -var isNumber = __webpack_require__(618); -var extend = __webpack_require__(621); -var repeat = __webpack_require__(623); -var toRegex = __webpack_require__(624); +var isNumber = __webpack_require__(612); +var extend = __webpack_require__(615); +var repeat = __webpack_require__(617); +var toRegex = __webpack_require__(618); /** * Return a range of numbers or letters. @@ -69320,7 +67272,7 @@ module.exports = fillRange; /***/ }), -/* 618 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69333,7 +67285,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(619); +var typeOf = __webpack_require__(613); module.exports = function isNumber(num) { var type = typeOf(num); @@ -69349,10 +67301,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 619 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(620); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -69471,7 +67423,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 620 */ +/* 614 */ /***/ (function(module, exports) { /*! @@ -69498,13 +67450,13 @@ function isSlowBuffer (obj) { /***/ }), -/* 621 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(622); +var isObject = __webpack_require__(616); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -69538,7 +67490,7 @@ function hasOwn(obj, key) { /***/ }), -/* 622 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69558,7 +67510,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 623 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69635,7 +67587,7 @@ function repeat(str, num) { /***/ }), -/* 624 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69648,8 +67600,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(623); -var isNumber = __webpack_require__(618); +var repeat = __webpack_require__(617); +var isNumber = __webpack_require__(612); var cache = {}; function toRegexRange(min, max, options) { @@ -69936,7 +67888,7 @@ module.exports = toRegexRange; /***/ }), -/* 625 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69961,14 +67913,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 626 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(627); -var utils = __webpack_require__(612); +var Node = __webpack_require__(621); +var utils = __webpack_require__(606); /** * Braces parsers @@ -70328,15 +68280,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 627 */ +/* 621 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(596); -var define = __webpack_require__(628); -var utils = __webpack_require__(629); +var isObject = __webpack_require__(590); +var define = __webpack_require__(622); +var utils = __webpack_require__(623); var ownNames; /** @@ -70827,7 +68779,7 @@ exports = module.exports = Node; /***/ }), -/* 628 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70840,7 +68792,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(597); +var isDescriptor = __webpack_require__(591); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -70865,13 +68817,13 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 629 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(630); +var typeOf = __webpack_require__(624); var utils = module.exports; /** @@ -71891,10 +69843,10 @@ function assert(val, message) { /***/ }), -/* 630 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(620); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -72013,17 +69965,17 @@ module.exports = function kindOf(val) { /***/ }), -/* 631 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(609); -var Snapdragon = __webpack_require__(632); -var compilers = __webpack_require__(611); -var parsers = __webpack_require__(626); -var utils = __webpack_require__(612); +var extend = __webpack_require__(603); +var Snapdragon = __webpack_require__(626); +var compilers = __webpack_require__(605); +var parsers = __webpack_require__(620); +var utils = __webpack_require__(606); /** * Customize Snapdragon parser and renderer @@ -72124,17 +70076,17 @@ module.exports = Braces; /***/ }), -/* 632 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(633); -var define = __webpack_require__(663); -var Compiler = __webpack_require__(674); -var Parser = __webpack_require__(697); -var utils = __webpack_require__(677); +var Base = __webpack_require__(627); +var define = __webpack_require__(657); +var Compiler = __webpack_require__(668); +var Parser = __webpack_require__(691); +var utils = __webpack_require__(671); var regexCache = {}; var cache = {}; @@ -72305,20 +70257,20 @@ module.exports.Parser = Parser; /***/ }), -/* 633 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(113); -var define = __webpack_require__(634); -var CacheBase = __webpack_require__(635); -var Emitter = __webpack_require__(636); -var isObject = __webpack_require__(596); -var merge = __webpack_require__(657); -var pascal = __webpack_require__(660); -var cu = __webpack_require__(661); +var define = __webpack_require__(628); +var CacheBase = __webpack_require__(629); +var Emitter = __webpack_require__(630); +var isObject = __webpack_require__(590); +var merge = __webpack_require__(651); +var pascal = __webpack_require__(654); +var cu = __webpack_require__(655); /** * Optionally define a custom `cache` namespace to use. @@ -72747,7 +70699,7 @@ module.exports.namespace = namespace; /***/ }), -/* 634 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72760,7 +70712,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(597); +var isDescriptor = __webpack_require__(591); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -72785,21 +70737,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 635 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(596); -var Emitter = __webpack_require__(636); -var visit = __webpack_require__(637); -var toPath = __webpack_require__(640); -var union = __webpack_require__(642); -var del = __webpack_require__(648); -var get = __webpack_require__(645); -var has = __webpack_require__(653); -var set = __webpack_require__(656); +var isObject = __webpack_require__(590); +var Emitter = __webpack_require__(630); +var visit = __webpack_require__(631); +var toPath = __webpack_require__(634); +var union = __webpack_require__(636); +var del = __webpack_require__(642); +var get = __webpack_require__(639); +var has = __webpack_require__(647); +var set = __webpack_require__(650); /** * Create a `Cache` constructor that when instantiated will @@ -73053,7 +71005,7 @@ module.exports.namespace = namespace; /***/ }), -/* 636 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { @@ -73222,7 +71174,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 637 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73235,8 +71187,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(638); -var mapVisit = __webpack_require__(639); +var visit = __webpack_require__(632); +var mapVisit = __webpack_require__(633); module.exports = function(collection, method, val) { var result; @@ -73259,7 +71211,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 638 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73272,7 +71224,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(596); +var isObject = __webpack_require__(590); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -73299,14 +71251,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 639 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(113); -var visit = __webpack_require__(638); +var visit = __webpack_require__(632); /** * Map `visit` over an array of objects. @@ -73343,7 +71295,7 @@ function isObject(val) { /***/ }), -/* 640 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73356,7 +71308,7 @@ function isObject(val) { -var typeOf = __webpack_require__(641); +var typeOf = __webpack_require__(635); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -73383,10 +71335,10 @@ function filter(arr) { /***/ }), -/* 641 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(620); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -73505,16 +71457,16 @@ module.exports = function kindOf(val) { /***/ }), -/* 642 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(643); -var union = __webpack_require__(644); -var get = __webpack_require__(645); -var set = __webpack_require__(646); +var isObject = __webpack_require__(637); +var union = __webpack_require__(638); +var get = __webpack_require__(639); +var set = __webpack_require__(640); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -73542,7 +71494,7 @@ function arrayify(val) { /***/ }), -/* 643 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73562,7 +71514,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 644 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73598,7 +71550,7 @@ module.exports = function union(init) { /***/ }), -/* 645 */ +/* 639 */ /***/ (function(module, exports) { /*! @@ -73654,7 +71606,7 @@ function toString(val) { /***/ }), -/* 646 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73667,10 +71619,10 @@ function toString(val) { -var split = __webpack_require__(613); -var extend = __webpack_require__(647); -var isPlainObject = __webpack_require__(603); -var isObject = __webpack_require__(643); +var split = __webpack_require__(607); +var extend = __webpack_require__(641); +var isPlainObject = __webpack_require__(597); +var isObject = __webpack_require__(637); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -73716,13 +71668,13 @@ function isValidKey(key) { /***/ }), -/* 647 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(643); +var isObject = __webpack_require__(637); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -73756,7 +71708,7 @@ function hasOwn(obj, key) { /***/ }), -/* 648 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73769,8 +71721,8 @@ function hasOwn(obj, key) { -var isObject = __webpack_require__(596); -var has = __webpack_require__(649); +var isObject = __webpack_require__(590); +var has = __webpack_require__(643); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -73795,7 +71747,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 649 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73808,9 +71760,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(650); -var hasValues = __webpack_require__(652); -var get = __webpack_require__(645); +var isObject = __webpack_require__(644); +var hasValues = __webpack_require__(646); +var get = __webpack_require__(639); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -73821,7 +71773,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 650 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73834,7 +71786,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(651); +var isArray = __webpack_require__(645); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -73842,7 +71794,7 @@ module.exports = function isObject(val) { /***/ }), -/* 651 */ +/* 645 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -73853,7 +71805,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 652 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73896,7 +71848,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 653 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73909,9 +71861,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(596); -var hasValues = __webpack_require__(654); -var get = __webpack_require__(645); +var isObject = __webpack_require__(590); +var hasValues = __webpack_require__(648); +var get = __webpack_require__(639); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -73919,7 +71871,7 @@ module.exports = function(val, prop) { /***/ }), -/* 654 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73932,8 +71884,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(655); -var isNumber = __webpack_require__(618); +var typeOf = __webpack_require__(649); +var isNumber = __webpack_require__(612); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -73986,10 +71938,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 655 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(620); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -74111,7 +72063,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 656 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74124,10 +72076,10 @@ module.exports = function kindOf(val) { -var split = __webpack_require__(613); -var extend = __webpack_require__(647); -var isPlainObject = __webpack_require__(603); -var isObject = __webpack_require__(643); +var split = __webpack_require__(607); +var extend = __webpack_require__(641); +var isPlainObject = __webpack_require__(597); +var isObject = __webpack_require__(637); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -74173,14 +72125,14 @@ function isValidKey(key) { /***/ }), -/* 657 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(658); -var forIn = __webpack_require__(659); +var isExtendable = __webpack_require__(652); +var forIn = __webpack_require__(653); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -74244,7 +72196,7 @@ module.exports = mixinDeep; /***/ }), -/* 658 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74257,7 +72209,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(603); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -74265,7 +72217,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 659 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74288,7 +72240,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 660 */ +/* 654 */ /***/ (function(module, exports) { /*! @@ -74315,14 +72267,14 @@ module.exports = pascalcase; /***/ }), -/* 661 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(113); -var utils = __webpack_require__(662); +var utils = __webpack_require__(656); /** * Expose class utils @@ -74687,7 +72639,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 662 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74701,10 +72653,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(644); -utils.define = __webpack_require__(663); -utils.isObj = __webpack_require__(596); -utils.staticExtend = __webpack_require__(670); +utils.union = __webpack_require__(638); +utils.define = __webpack_require__(657); +utils.isObj = __webpack_require__(590); +utils.staticExtend = __webpack_require__(664); /** @@ -74715,7 +72667,7 @@ module.exports = utils; /***/ }), -/* 663 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74728,7 +72680,7 @@ module.exports = utils; -var isDescriptor = __webpack_require__(664); +var isDescriptor = __webpack_require__(658); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -74753,7 +72705,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 664 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74766,9 +72718,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(665); -var isAccessor = __webpack_require__(666); -var isData = __webpack_require__(668); +var typeOf = __webpack_require__(659); +var isAccessor = __webpack_require__(660); +var isData = __webpack_require__(662); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -74782,7 +72734,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 665 */ +/* 659 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -74935,7 +72887,7 @@ function isBuffer(val) { /***/ }), -/* 666 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74948,7 +72900,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(667); +var typeOf = __webpack_require__(661); // accessor descriptor properties var accessor = { @@ -75011,10 +72963,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 667 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(620); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -75133,7 +73085,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 668 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75146,7 +73098,7 @@ module.exports = function kindOf(val) { -var typeOf = __webpack_require__(669); +var typeOf = __webpack_require__(663); // data descriptor properties var data = { @@ -75195,10 +73147,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 669 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(620); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -75317,7 +73269,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 670 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75330,8 +73282,8 @@ module.exports = function kindOf(val) { -var copy = __webpack_require__(671); -var define = __webpack_require__(663); +var copy = __webpack_require__(665); +var define = __webpack_require__(657); var util = __webpack_require__(113); /** @@ -75414,15 +73366,15 @@ module.exports = extend; /***/ }), -/* 671 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(672); -var copyDescriptor = __webpack_require__(673); -var define = __webpack_require__(663); +var typeOf = __webpack_require__(666); +var copyDescriptor = __webpack_require__(667); +var define = __webpack_require__(657); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -75595,10 +73547,10 @@ module.exports.has = has; /***/ }), -/* 672 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(620); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -75717,7 +73669,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 673 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75805,16 +73757,16 @@ function isObject(val) { /***/ }), -/* 674 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(675); -var define = __webpack_require__(663); +var use = __webpack_require__(669); +var define = __webpack_require__(657); var debug = __webpack_require__(205)('snapdragon:compiler'); -var utils = __webpack_require__(677); +var utils = __webpack_require__(671); /** * Create a new `Compiler` with the given `options`. @@ -75968,7 +73920,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(696); + var sourcemaps = __webpack_require__(690); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -75989,7 +73941,7 @@ module.exports = Compiler; /***/ }), -/* 675 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76002,7 +73954,7 @@ module.exports = Compiler; -var utils = __webpack_require__(676); +var utils = __webpack_require__(670); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -76117,7 +74069,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 676 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76131,8 +74083,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(663); -utils.isObject = __webpack_require__(596); +utils.define = __webpack_require__(657); +utils.isObject = __webpack_require__(590); utils.isString = function(val) { @@ -76147,7 +74099,7 @@ module.exports = utils; /***/ }), -/* 677 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76157,9 +74109,9 @@ module.exports = utils; * Module dependencies */ -exports.extend = __webpack_require__(647); -exports.SourceMap = __webpack_require__(678); -exports.sourceMapResolve = __webpack_require__(689); +exports.extend = __webpack_require__(641); +exports.SourceMap = __webpack_require__(672); +exports.sourceMapResolve = __webpack_require__(683); /** * Convert backslash in the given string to forward slashes @@ -76202,7 +74154,7 @@ exports.last = function(arr, n) { /***/ }), -/* 678 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -76210,13 +74162,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(679).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(685).SourceMapConsumer; -exports.SourceNode = __webpack_require__(688).SourceNode; +exports.SourceMapGenerator = __webpack_require__(673).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(679).SourceMapConsumer; +exports.SourceNode = __webpack_require__(682).SourceNode; /***/ }), -/* 679 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -76226,10 +74178,10 @@ exports.SourceNode = __webpack_require__(688).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(680); -var util = __webpack_require__(682); -var ArraySet = __webpack_require__(683).ArraySet; -var MappingList = __webpack_require__(684).MappingList; +var base64VLQ = __webpack_require__(674); +var util = __webpack_require__(676); +var ArraySet = __webpack_require__(677).ArraySet; +var MappingList = __webpack_require__(678).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -76638,7 +74590,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 680 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -76678,7 +74630,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(681); +var base64 = __webpack_require__(675); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -76784,7 +74736,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 681 */ +/* 675 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -76857,7 +74809,7 @@ exports.decode = function (charCode) { /***/ }), -/* 682 */ +/* 676 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -77280,7 +75232,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 683 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -77290,7 +75242,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(682); +var util = __webpack_require__(676); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -77407,7 +75359,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 684 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -77417,7 +75369,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(682); +var util = __webpack_require__(676); /** * Determine whether mappingB is after mappingA with respect to generated @@ -77492,7 +75444,7 @@ exports.MappingList = MappingList; /***/ }), -/* 685 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -77502,11 +75454,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(682); -var binarySearch = __webpack_require__(686); -var ArraySet = __webpack_require__(683).ArraySet; -var base64VLQ = __webpack_require__(680); -var quickSort = __webpack_require__(687).quickSort; +var util = __webpack_require__(676); +var binarySearch = __webpack_require__(680); +var ArraySet = __webpack_require__(677).ArraySet; +var base64VLQ = __webpack_require__(674); +var quickSort = __webpack_require__(681).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -78580,7 +76532,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 686 */ +/* 680 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -78697,7 +76649,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 687 */ +/* 681 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -78817,7 +76769,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 688 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -78827,8 +76779,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(679).SourceMapGenerator; -var util = __webpack_require__(682); +var SourceMapGenerator = __webpack_require__(673).SourceMapGenerator; +var util = __webpack_require__(676); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -79236,17 +77188,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 689 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(690) -var resolveUrl = __webpack_require__(691) -var decodeUriComponent = __webpack_require__(692) -var urix = __webpack_require__(694) -var atob = __webpack_require__(695) +var sourceMappingURL = __webpack_require__(684) +var resolveUrl = __webpack_require__(685) +var decodeUriComponent = __webpack_require__(686) +var urix = __webpack_require__(688) +var atob = __webpack_require__(689) @@ -79544,7 +77496,7 @@ module.exports = { /***/ }), -/* 690 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -79607,7 +77559,7 @@ void (function(root, factory) { /***/ }), -/* 691 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -79625,13 +77577,13 @@ module.exports = resolveUrl /***/ }), -/* 692 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(693) +var decodeUriComponent = __webpack_require__(687) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -79642,7 +77594,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 693 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79743,7 +77695,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 694 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -79766,7 +77718,7 @@ module.exports = urix /***/ }), -/* 695 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79780,7 +77732,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 696 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79788,8 +77740,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(132); var path = __webpack_require__(4); -var define = __webpack_require__(663); -var utils = __webpack_require__(677); +var define = __webpack_require__(657); +var utils = __webpack_require__(671); /** * Expose `mixin()`. @@ -79932,19 +77884,19 @@ exports.comment = function(node) { /***/ }), -/* 697 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(675); +var use = __webpack_require__(669); var util = __webpack_require__(113); -var Cache = __webpack_require__(698); -var define = __webpack_require__(663); +var Cache = __webpack_require__(692); +var define = __webpack_require__(657); var debug = __webpack_require__(205)('snapdragon:parser'); -var Position = __webpack_require__(699); -var utils = __webpack_require__(677); +var Position = __webpack_require__(693); +var utils = __webpack_require__(671); /** * Create a new `Parser` with the given `input` and `options`. @@ -80472,7 +78424,7 @@ module.exports = Parser; /***/ }), -/* 698 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80579,13 +78531,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 699 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(663); +var define = __webpack_require__(657); /** * Store position for a node @@ -80600,14 +78552,14 @@ module.exports = function Position(start, parser) { /***/ }), -/* 700 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(701); -var assignSymbols = __webpack_require__(604); +var isExtendable = __webpack_require__(695); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -80667,7 +78619,7 @@ function isEnum(obj, key) { /***/ }), -/* 701 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80680,7 +78632,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(603); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -80688,14 +78640,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 702 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(703); -var extglob = __webpack_require__(717); +var nanomatch = __webpack_require__(697); +var extglob = __webpack_require__(711); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -80772,7 +78724,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 703 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80783,17 +78735,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(113); -var toRegex = __webpack_require__(588); -var extend = __webpack_require__(704); +var toRegex = __webpack_require__(582); +var extend = __webpack_require__(698); /** * Local dependencies */ -var compilers = __webpack_require__(706); -var parsers = __webpack_require__(707); -var cache = __webpack_require__(710); -var utils = __webpack_require__(712); +var compilers = __webpack_require__(700); +var parsers = __webpack_require__(701); +var cache = __webpack_require__(704); +var utils = __webpack_require__(706); var MAX_LENGTH = 1024 * 64; /** @@ -81617,14 +79569,14 @@ module.exports = nanomatch; /***/ }), -/* 704 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(705); -var assignSymbols = __webpack_require__(604); +var isExtendable = __webpack_require__(699); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -81684,7 +79636,7 @@ function isEnum(obj, key) { /***/ }), -/* 705 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81697,7 +79649,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(603); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -81705,7 +79657,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 706 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82051,15 +80003,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 707 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(605); -var toRegex = __webpack_require__(588); -var isOdd = __webpack_require__(708); +var regexNot = __webpack_require__(599); +var toRegex = __webpack_require__(582); +var isOdd = __webpack_require__(702); /** * Characters to use in negation regex (we want to "not" match @@ -82445,7 +80397,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 708 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82458,7 +80410,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(709); +var isNumber = __webpack_require__(703); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -82472,7 +80424,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 709 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82500,14 +80452,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 710 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(711))(); +module.exports = new (__webpack_require__(705))(); /***/ }), -/* 711 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82520,7 +80472,7 @@ module.exports = new (__webpack_require__(711))(); -var MapCache = __webpack_require__(698); +var MapCache = __webpack_require__(692); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -82642,7 +80594,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 712 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82655,14 +80607,14 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(713)(); -var Snapdragon = __webpack_require__(632); -utils.define = __webpack_require__(714); -utils.diff = __webpack_require__(715); -utils.extend = __webpack_require__(704); -utils.pick = __webpack_require__(716); -utils.typeOf = __webpack_require__(598); -utils.unique = __webpack_require__(608); +var isWindows = __webpack_require__(707)(); +var Snapdragon = __webpack_require__(626); +utils.define = __webpack_require__(708); +utils.diff = __webpack_require__(709); +utils.extend = __webpack_require__(698); +utils.pick = __webpack_require__(710); +utils.typeOf = __webpack_require__(592); +utils.unique = __webpack_require__(602); /** * Returns true if the given value is effectively an empty string @@ -83028,7 +80980,7 @@ utils.unixify = function(options) { /***/ }), -/* 713 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -83056,7 +81008,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 714 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83069,8 +81021,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(596); -var isDescriptor = __webpack_require__(597); +var isobject = __webpack_require__(590); +var isDescriptor = __webpack_require__(591); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -83101,7 +81053,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 715 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83155,7 +81107,7 @@ function diffArray(one, two) { /***/ }), -/* 716 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83168,7 +81120,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(596); +var isObject = __webpack_require__(590); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -83197,7 +81149,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 717 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83207,18 +81159,18 @@ module.exports = function pick(obj, keys) { * Module dependencies */ -var extend = __webpack_require__(647); -var unique = __webpack_require__(608); -var toRegex = __webpack_require__(588); +var extend = __webpack_require__(641); +var unique = __webpack_require__(602); +var toRegex = __webpack_require__(582); /** * Local dependencies */ -var compilers = __webpack_require__(718); -var parsers = __webpack_require__(724); -var Extglob = __webpack_require__(727); -var utils = __webpack_require__(726); +var compilers = __webpack_require__(712); +var parsers = __webpack_require__(718); +var Extglob = __webpack_require__(721); +var utils = __webpack_require__(720); var MAX_LENGTH = 1024 * 64; /** @@ -83535,13 +81487,13 @@ module.exports = extglob; /***/ }), -/* 718 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(719); +var brackets = __webpack_require__(713); /** * Extglob compilers @@ -83711,7 +81663,7 @@ module.exports = function(extglob) { /***/ }), -/* 719 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83721,17 +81673,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(720); -var parsers = __webpack_require__(722); +var compilers = __webpack_require__(714); +var parsers = __webpack_require__(716); /** * Module dependencies */ var debug = __webpack_require__(205)('expand-brackets'); -var extend = __webpack_require__(647); -var Snapdragon = __webpack_require__(632); -var toRegex = __webpack_require__(588); +var extend = __webpack_require__(641); +var Snapdragon = __webpack_require__(626); +var toRegex = __webpack_require__(582); /** * Parses the given POSIX character class `pattern` and returns a @@ -83929,13 +81881,13 @@ module.exports = brackets; /***/ }), -/* 720 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(721); +var posix = __webpack_require__(715); module.exports = function(brackets) { brackets.compiler @@ -84023,7 +81975,7 @@ module.exports = function(brackets) { /***/ }), -/* 721 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84052,14 +82004,14 @@ module.exports = { /***/ }), -/* 722 */ +/* 716 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(723); -var define = __webpack_require__(663); +var utils = __webpack_require__(717); +var define = __webpack_require__(657); /** * Text regex @@ -84278,14 +82230,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 723 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(588); -var regexNot = __webpack_require__(605); +var toRegex = __webpack_require__(582); +var regexNot = __webpack_require__(599); var cached; /** @@ -84319,15 +82271,15 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 724 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(719); -var define = __webpack_require__(725); -var utils = __webpack_require__(726); +var brackets = __webpack_require__(713); +var define = __webpack_require__(719); +var utils = __webpack_require__(720); /** * Characters to use in text regex (we want to "not" match @@ -84482,7 +82434,7 @@ module.exports = parsers; /***/ }), -/* 725 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84495,7 +82447,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(597); +var isDescriptor = __webpack_require__(591); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -84520,14 +82472,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 726 */ +/* 720 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(605); -var Cache = __webpack_require__(711); +var regex = __webpack_require__(599); +var Cache = __webpack_require__(705); /** * Utils @@ -84596,7 +82548,7 @@ utils.createRegex = function(str) { /***/ }), -/* 727 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84606,16 +82558,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(632); -var define = __webpack_require__(725); -var extend = __webpack_require__(647); +var Snapdragon = __webpack_require__(626); +var define = __webpack_require__(719); +var extend = __webpack_require__(641); /** * Local dependencies */ -var compilers = __webpack_require__(718); -var parsers = __webpack_require__(724); +var compilers = __webpack_require__(712); +var parsers = __webpack_require__(718); /** * Customize Snapdragon parser and renderer @@ -84681,16 +82633,16 @@ module.exports = Extglob; /***/ }), -/* 728 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(717); -var nanomatch = __webpack_require__(703); -var regexNot = __webpack_require__(605); -var toRegex = __webpack_require__(588); +var extglob = __webpack_require__(711); +var nanomatch = __webpack_require__(697); +var regexNot = __webpack_require__(599); +var toRegex = __webpack_require__(582); var not; /** @@ -84771,14 +82723,14 @@ function textRegex(pattern) { /***/ }), -/* 729 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(711))(); +module.exports = new (__webpack_require__(705))(); /***/ }), -/* 730 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84791,13 +82743,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(632); -utils.define = __webpack_require__(731); -utils.diff = __webpack_require__(715); -utils.extend = __webpack_require__(700); -utils.pick = __webpack_require__(716); -utils.typeOf = __webpack_require__(598); -utils.unique = __webpack_require__(608); +var Snapdragon = __webpack_require__(626); +utils.define = __webpack_require__(725); +utils.diff = __webpack_require__(709); +utils.extend = __webpack_require__(694); +utils.pick = __webpack_require__(710); +utils.typeOf = __webpack_require__(592); +utils.unique = __webpack_require__(602); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -85094,7 +83046,7 @@ utils.unixify = function(options) { /***/ }), -/* 731 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85107,8 +83059,8 @@ utils.unixify = function(options) { -var isobject = __webpack_require__(596); -var isDescriptor = __webpack_require__(597); +var isobject = __webpack_require__(590); +var isDescriptor = __webpack_require__(591); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -85139,7 +83091,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 732 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85158,9 +83110,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(733); -var reader_1 = __webpack_require__(746); -var fs_stream_1 = __webpack_require__(750); +var readdir = __webpack_require__(727); +var reader_1 = __webpack_require__(740); +var fs_stream_1 = __webpack_require__(744); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -85221,15 +83173,15 @@ exports.default = ReaderAsync; /***/ }), -/* 733 */ +/* 727 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(734); -const readdirAsync = __webpack_require__(742); -const readdirStream = __webpack_require__(745); +const readdirSync = __webpack_require__(728); +const readdirAsync = __webpack_require__(736); +const readdirStream = __webpack_require__(739); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -85313,7 +83265,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 734 */ +/* 728 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85321,11 +83273,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(735); +const DirectoryReader = __webpack_require__(729); let syncFacade = { - fs: __webpack_require__(740), - forEach: __webpack_require__(741), + fs: __webpack_require__(734), + forEach: __webpack_require__(735), sync: true }; @@ -85354,7 +83306,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 735 */ +/* 729 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85363,9 +83315,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(173).Readable; const EventEmitter = __webpack_require__(164).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(736); -const stat = __webpack_require__(738); -const call = __webpack_require__(739); +const normalizeOptions = __webpack_require__(730); +const stat = __webpack_require__(732); +const call = __webpack_require__(733); /** * Asynchronously reads the contents of a directory and streams the results @@ -85741,14 +83693,14 @@ module.exports = DirectoryReader; /***/ }), -/* 736 */ +/* 730 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(737); +const globToRegExp = __webpack_require__(731); module.exports = normalizeOptions; @@ -85925,7 +83877,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 737 */ +/* 731 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -86062,13 +84014,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 738 */ +/* 732 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(739); +const call = __webpack_require__(733); module.exports = stat; @@ -86143,7 +84095,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 739 */ +/* 733 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86204,14 +84156,14 @@ function callOnce (fn) { /***/ }), -/* 740 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const call = __webpack_require__(739); +const call = __webpack_require__(733); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -86275,7 +84227,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 741 */ +/* 735 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86304,7 +84256,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 742 */ +/* 736 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86312,12 +84264,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(743); -const DirectoryReader = __webpack_require__(735); +const maybe = __webpack_require__(737); +const DirectoryReader = __webpack_require__(729); let asyncFacade = { fs: __webpack_require__(132), - forEach: __webpack_require__(744), + forEach: __webpack_require__(738), async: true }; @@ -86359,7 +84311,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 743 */ +/* 737 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86386,7 +84338,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 744 */ +/* 738 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86422,7 +84374,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 745 */ +/* 739 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86430,11 +84382,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(735); +const DirectoryReader = __webpack_require__(729); let streamFacade = { fs: __webpack_require__(132), - forEach: __webpack_require__(744), + forEach: __webpack_require__(738), async: true }; @@ -86454,16 +84406,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 746 */ +/* 740 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var deep_1 = __webpack_require__(747); -var entry_1 = __webpack_require__(749); -var pathUtil = __webpack_require__(748); +var deep_1 = __webpack_require__(741); +var entry_1 = __webpack_require__(743); +var pathUtil = __webpack_require__(742); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -86529,14 +84481,14 @@ exports.default = Reader; /***/ }), -/* 747 */ +/* 741 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(748); -var patternUtils = __webpack_require__(582); +var pathUtils = __webpack_require__(742); +var patternUtils = __webpack_require__(576); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -86619,7 +84571,7 @@ exports.default = DeepFilter; /***/ }), -/* 748 */ +/* 742 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86650,14 +84602,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 749 */ +/* 743 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(748); -var patternUtils = __webpack_require__(582); +var pathUtils = __webpack_require__(742); +var patternUtils = __webpack_require__(576); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -86742,7 +84694,7 @@ exports.default = EntryFilter; /***/ }), -/* 750 */ +/* 744 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86762,8 +84714,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(173); -var fsStat = __webpack_require__(751); -var fs_1 = __webpack_require__(755); +var fsStat = __webpack_require__(745); +var fs_1 = __webpack_require__(749); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -86813,14 +84765,14 @@ exports.default = FileSystemStream; /***/ }), -/* 751 */ +/* 745 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(752); -const statProvider = __webpack_require__(754); +const optionsManager = __webpack_require__(746); +const statProvider = __webpack_require__(748); /** * Asynchronous API. */ @@ -86851,13 +84803,13 @@ exports.statSync = statSync; /***/ }), -/* 752 */ +/* 746 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(753); +const fsAdapter = __webpack_require__(747); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -86870,7 +84822,7 @@ exports.prepare = prepare; /***/ }), -/* 753 */ +/* 747 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86893,7 +84845,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 754 */ +/* 748 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86945,7 +84897,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 755 */ +/* 749 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86976,7 +84928,7 @@ exports.default = FileSystem; /***/ }), -/* 756 */ +/* 750 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86996,9 +84948,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(173); -var readdir = __webpack_require__(733); -var reader_1 = __webpack_require__(746); -var fs_stream_1 = __webpack_require__(750); +var readdir = __webpack_require__(727); +var reader_1 = __webpack_require__(740); +var fs_stream_1 = __webpack_require__(744); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -87066,7 +85018,7 @@ exports.default = ReaderStream; /***/ }), -/* 757 */ +/* 751 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87085,9 +85037,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(733); -var reader_1 = __webpack_require__(746); -var fs_sync_1 = __webpack_require__(758); +var readdir = __webpack_require__(727); +var reader_1 = __webpack_require__(740); +var fs_sync_1 = __webpack_require__(752); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -87147,7 +85099,7 @@ exports.default = ReaderSync; /***/ }), -/* 758 */ +/* 752 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87166,8 +85118,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(751); -var fs_1 = __webpack_require__(755); +var fsStat = __webpack_require__(745); +var fs_1 = __webpack_require__(749); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -87213,7 +85165,7 @@ exports.default = FileSystemSync; /***/ }), -/* 759 */ +/* 753 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87229,7 +85181,7 @@ exports.flatten = flatten; /***/ }), -/* 760 */ +/* 754 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87250,13 +85202,13 @@ exports.merge = merge; /***/ }), -/* 761 */ +/* 755 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(762); +const pathType = __webpack_require__(756); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -87322,13 +85274,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 762 */ +/* 756 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const pify = __webpack_require__(763); +const pify = __webpack_require__(757); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -87371,7 +85323,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 763 */ +/* 757 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87462,17 +85414,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 764 */ +/* 758 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(578); -const gitIgnore = __webpack_require__(765); -const pify = __webpack_require__(410); -const slash = __webpack_require__(766); +const fastGlob = __webpack_require__(572); +const gitIgnore = __webpack_require__(759); +const pify = __webpack_require__(404); +const slash = __webpack_require__(760); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -87570,7 +85522,7 @@ module.exports.sync = options => { /***/ }), -/* 765 */ +/* 759 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -88039,7 +85991,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 766 */ +/* 760 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88057,7 +86009,7 @@ module.exports = input => { /***/ }), -/* 767 */ +/* 761 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88070,7 +86022,7 @@ module.exports = input => { -var isGlob = __webpack_require__(768); +var isGlob = __webpack_require__(762); module.exports = function hasGlob(val) { if (val == null) return false; @@ -88090,7 +86042,7 @@ module.exports = function hasGlob(val) { /***/ }), -/* 768 */ +/* 762 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -88121,17 +86073,17 @@ module.exports = function isGlob(str) { /***/ }), -/* 769 */ +/* 763 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const {constants: fsConstants} = __webpack_require__(132); -const pEvent = __webpack_require__(770); -const CpFileError = __webpack_require__(773); -const fs = __webpack_require__(775); -const ProgressEmitter = __webpack_require__(778); +const pEvent = __webpack_require__(764); +const CpFileError = __webpack_require__(767); +const fs = __webpack_require__(769); +const ProgressEmitter = __webpack_require__(772); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -88245,12 +86197,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 770 */ +/* 764 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(771); +const pTimeout = __webpack_require__(765); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -88541,12 +86493,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 771 */ +/* 765 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(772); +const pFinally = __webpack_require__(766); class TimeoutError extends Error { constructor(message) { @@ -88592,7 +86544,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 772 */ +/* 766 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88614,12 +86566,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 773 */ +/* 767 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(774); +const NestedError = __webpack_require__(768); class CpFileError extends NestedError { constructor(message, nested) { @@ -88633,7 +86585,7 @@ module.exports = CpFileError; /***/ }), -/* 774 */ +/* 768 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(113).inherits; @@ -88689,16 +86641,16 @@ module.exports = NestedError; /***/ }), -/* 775 */ +/* 769 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(113); const fs = __webpack_require__(233); -const makeDir = __webpack_require__(776); -const pEvent = __webpack_require__(770); -const CpFileError = __webpack_require__(773); +const makeDir = __webpack_require__(770); +const pEvent = __webpack_require__(764); +const CpFileError = __webpack_require__(767); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -88795,7 +86747,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 776 */ +/* 770 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88803,7 +86755,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(132); const path = __webpack_require__(4); const {promisify} = __webpack_require__(113); -const semver = __webpack_require__(777); +const semver = __webpack_require__(771); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -88958,7 +86910,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 777 */ +/* 771 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -90560,7 +88512,7 @@ function coerce (version, options) { /***/ }), -/* 778 */ +/* 772 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90601,7 +88553,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 779 */ +/* 773 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90647,12 +88599,12 @@ exports.default = module.exports; /***/ }), -/* 780 */ +/* 774 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(781); +const pMap = __webpack_require__(775); const pFilter = async (iterable, filterer, options) => { const values = await pMap( @@ -90669,7 +88621,7 @@ module.exports.default = pFilter; /***/ }), -/* 781 */ +/* 775 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90748,12 +88700,12 @@ module.exports.default = pMap; /***/ }), -/* 782 */ +/* 776 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(774); +const NestedError = __webpack_require__(768); class CpyError extends NestedError { constructor(message, nested) { @@ -90767,7 +88719,7 @@ module.exports = CpyError; /***/ }), -/* 783 */ +/* 777 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90775,10 +88727,10 @@ module.exports = CpyError; const fs = __webpack_require__(132); const arrayUnion = __webpack_require__(242); const merge2 = __webpack_require__(243); -const fastGlob = __webpack_require__(784); -const dirGlob = __webpack_require__(332); -const gitignore = __webpack_require__(815); -const {FilterStream, UniqueStream} = __webpack_require__(816); +const fastGlob = __webpack_require__(778); +const dirGlob = __webpack_require__(326); +const gitignore = __webpack_require__(809); +const {FilterStream, UniqueStream} = __webpack_require__(810); const DEFAULT_FILTER = () => false; @@ -90955,425 +88907,465 @@ module.exports.gitignore = gitignore; /***/ }), -/* 784 */ +/* 778 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -const taskManager = __webpack_require__(785); -const async_1 = __webpack_require__(801); -const stream_1 = __webpack_require__(811); -const sync_1 = __webpack_require__(812); -const settings_1 = __webpack_require__(814); -const utils = __webpack_require__(786); -async function FastGlob(source, options) { - assertPatternsInput(source); - const works = getWorks(source, async_1.default, options); - const result = await Promise.all(works); - return utils.array.flatten(result); -} -// https://github.com/typescript-eslint/typescript-eslint/issues/60 -// eslint-disable-next-line no-redeclare -(function (FastGlob) { - function sync(source, options) { - assertPatternsInput(source); - const works = getWorks(source, sync_1.default, options); - return utils.array.flatten(works); - } - FastGlob.sync = sync; - function stream(source, options) { - assertPatternsInput(source); - const works = getWorks(source, stream_1.default, options); - /** - * The stream returned by the provider cannot work with an asynchronous iterator. - * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. - * This affects performance (+25%). I don't see best solution right now. - */ - return utils.stream.merge(works); - } - FastGlob.stream = stream; - function generateTasks(source, options) { - assertPatternsInput(source); - const patterns = [].concat(source); - const settings = new settings_1.default(options); - return taskManager.generate(patterns, settings); - } - FastGlob.generateTasks = generateTasks; - function isDynamicPattern(source, options) { - assertPatternsInput(source); - const settings = new settings_1.default(options); - return utils.pattern.isDynamicPattern(source, settings); - } - FastGlob.isDynamicPattern = isDynamicPattern; - function escapePath(source) { - assertPatternsInput(source); - return utils.path.escape(source); - } - FastGlob.escapePath = escapePath; -})(FastGlob || (FastGlob = {})); -function getWorks(source, _Provider, options) { - const patterns = [].concat(source); - const settings = new settings_1.default(options); - const tasks = taskManager.generate(patterns, settings); - const provider = new _Provider(settings); - return tasks.map(provider.read, provider); -} -function assertPatternsInput(input) { - const source = [].concat(input); - const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); - if (!isValidSource) { - throw new TypeError('Patterns must be a string (non empty) or an array of strings'); - } -} -module.exports = FastGlob; + +const taskManager = __webpack_require__(779); +const async_1 = __webpack_require__(795); +const stream_1 = __webpack_require__(805); +const sync_1 = __webpack_require__(806); +const settings_1 = __webpack_require__(808); +const utils = __webpack_require__(780); +async function FastGlob(source, options) { + assertPatternsInput(source); + const works = getWorks(source, async_1.default, options); + const result = await Promise.all(works); + return utils.array.flatten(result); +} +// https://github.com/typescript-eslint/typescript-eslint/issues/60 +// eslint-disable-next-line no-redeclare +(function (FastGlob) { + function sync(source, options) { + assertPatternsInput(source); + const works = getWorks(source, sync_1.default, options); + return utils.array.flatten(works); + } + FastGlob.sync = sync; + function stream(source, options) { + assertPatternsInput(source); + const works = getWorks(source, stream_1.default, options); + /** + * The stream returned by the provider cannot work with an asynchronous iterator. + * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. + * This affects performance (+25%). I don't see best solution right now. + */ + return utils.stream.merge(works); + } + FastGlob.stream = stream; + function generateTasks(source, options) { + assertPatternsInput(source); + const patterns = [].concat(source); + const settings = new settings_1.default(options); + return taskManager.generate(patterns, settings); + } + FastGlob.generateTasks = generateTasks; + function isDynamicPattern(source, options) { + assertPatternsInput(source); + const settings = new settings_1.default(options); + return utils.pattern.isDynamicPattern(source, settings); + } + FastGlob.isDynamicPattern = isDynamicPattern; + function escapePath(source) { + assertPatternsInput(source); + return utils.path.escape(source); + } + FastGlob.escapePath = escapePath; +})(FastGlob || (FastGlob = {})); +function getWorks(source, _Provider, options) { + const patterns = [].concat(source); + const settings = new settings_1.default(options); + const tasks = taskManager.generate(patterns, settings); + const provider = new _Provider(settings); + return tasks.map(provider.read, provider); +} +function assertPatternsInput(input) { + const source = [].concat(input); + const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); + if (!isValidSource) { + throw new TypeError('Patterns must be a string (non empty) or an array of strings'); + } +} +module.exports = FastGlob; /***/ }), -/* 785 */ +/* 779 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; -const utils = __webpack_require__(786); -function generate(patterns, settings) { - const positivePatterns = getPositivePatterns(patterns); - const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); - const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); - const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); - const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -exports.generate = generate; -function convertPatternsToTasks(positive, negative, dynamic) { - const positivePatternsGroup = groupPatternsByBaseDirectory(positive); - // When we have a global group – there is no reason to divide the patterns into independent tasks. - // In this case, the global task covers the rest. - if ('.' in positivePatternsGroup) { - const task = convertPatternGroupToTask('.', positive, negative, dynamic); - return [task]; - } - return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); -} -exports.convertPatternsToTasks = convertPatternsToTasks; -function getPositivePatterns(patterns) { - return utils.pattern.getPositivePatterns(patterns); -} -exports.getPositivePatterns = getPositivePatterns; -function getNegativePatternsAsPositive(patterns, ignore) { - const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); - const positive = negative.map(utils.pattern.convertToPositivePattern); - return positive; -} -exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -function groupPatternsByBaseDirectory(patterns) { - const group = {}; - return patterns.reduce((collection, pattern) => { - const base = utils.pattern.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, group); -} -exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map((base) => { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - dynamic, - positive, - negative, - base, - patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) - }; -} -exports.convertPatternGroupToTask = convertPatternGroupToTask; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; +const utils = __webpack_require__(780); +function generate(patterns, settings) { + const positivePatterns = getPositivePatterns(patterns); + const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); + const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); + const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); + const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); + const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); + return staticTasks.concat(dynamicTasks); +} +exports.generate = generate; +/** + * Returns tasks grouped by basic pattern directories. + * + * Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately. + * This is necessary because directory traversal starts at the base directory and goes deeper. + */ +function convertPatternsToTasks(positive, negative, dynamic) { + const tasks = []; + const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive); + const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive); + const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory); + const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory); + tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic)); + /* + * For the sake of reducing future accesses to the file system, we merge all tasks within the current directory + * into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest. + */ + if ('.' in insideCurrentDirectoryGroup) { + tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic)); + } + else { + tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic)); + } + return tasks; +} +exports.convertPatternsToTasks = convertPatternsToTasks; +function getPositivePatterns(patterns) { + return utils.pattern.getPositivePatterns(patterns); +} +exports.getPositivePatterns = getPositivePatterns; +function getNegativePatternsAsPositive(patterns, ignore) { + const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); + const positive = negative.map(utils.pattern.convertToPositivePattern); + return positive; +} +exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; +function groupPatternsByBaseDirectory(patterns) { + const group = {}; + return patterns.reduce((collection, pattern) => { + const base = utils.pattern.getBaseDirectory(pattern); + if (base in collection) { + collection[base].push(pattern); + } + else { + collection[base] = [pattern]; + } + return collection; + }, group); +} +exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; +function convertPatternGroupsToTasks(positive, negative, dynamic) { + return Object.keys(positive).map((base) => { + return convertPatternGroupToTask(base, positive[base], negative, dynamic); + }); +} +exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; +function convertPatternGroupToTask(base, positive, negative, dynamic) { + return { + dynamic, + positive, + negative, + base, + patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) + }; +} +exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 786 */ +/* 780 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; -const array = __webpack_require__(787); -exports.array = array; -const errno = __webpack_require__(788); -exports.errno = errno; -const fs = __webpack_require__(789); -exports.fs = fs; -const path = __webpack_require__(790); -exports.path = path; -const pattern = __webpack_require__(791); -exports.pattern = pattern; -const stream = __webpack_require__(799); -exports.stream = stream; -const string = __webpack_require__(800); -exports.string = string; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; +const array = __webpack_require__(781); +exports.array = array; +const errno = __webpack_require__(782); +exports.errno = errno; +const fs = __webpack_require__(783); +exports.fs = fs; +const path = __webpack_require__(784); +exports.path = path; +const pattern = __webpack_require__(785); +exports.pattern = pattern; +const stream = __webpack_require__(793); +exports.stream = stream; +const string = __webpack_require__(794); +exports.string = string; /***/ }), -/* 787 */ +/* 781 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.splitWhen = exports.flatten = void 0; -function flatten(items) { - return items.reduce((collection, item) => [].concat(collection, item), []); -} -exports.flatten = flatten; -function splitWhen(items, predicate) { - const result = [[]]; - let groupIndex = 0; - for (const item of items) { - if (predicate(item)) { - groupIndex++; - result[groupIndex] = []; - } - else { - result[groupIndex].push(item); - } - } - return result; -} -exports.splitWhen = splitWhen; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.splitWhen = exports.flatten = void 0; +function flatten(items) { + return items.reduce((collection, item) => [].concat(collection, item), []); +} +exports.flatten = flatten; +function splitWhen(items, predicate) { + const result = [[]]; + let groupIndex = 0; + for (const item of items) { + if (predicate(item)) { + groupIndex++; + result[groupIndex] = []; + } + else { + result[groupIndex].push(item); + } + } + return result; +} +exports.splitWhen = splitWhen; /***/ }), -/* 788 */ +/* 782 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEnoentCodeError = void 0; -function isEnoentCodeError(error) { - return error.code === 'ENOENT'; -} -exports.isEnoentCodeError = isEnoentCodeError; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEnoentCodeError = void 0; +function isEnoentCodeError(error) { + return error.code === 'ENOENT'; +} +exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 789 */ +/* 783 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createDirentFromStats = void 0; -class DirentFromStats { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats(name, stats) { - return new DirentFromStats(name, stats); -} -exports.createDirentFromStats = createDirentFromStats; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createDirentFromStats = void 0; +class DirentFromStats { + constructor(name, stats) { + this.name = name; + this.isBlockDevice = stats.isBlockDevice.bind(stats); + this.isCharacterDevice = stats.isCharacterDevice.bind(stats); + this.isDirectory = stats.isDirectory.bind(stats); + this.isFIFO = stats.isFIFO.bind(stats); + this.isFile = stats.isFile.bind(stats); + this.isSocket = stats.isSocket.bind(stats); + this.isSymbolicLink = stats.isSymbolicLink.bind(stats); + } +} +function createDirentFromStats(name, stats) { + return new DirentFromStats(name, stats); +} +exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 790 */ +/* 784 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; -const path = __webpack_require__(4); -const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ -const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; -/** - * Designed to work only with simple paths: `dir\\file`. - */ -function unixify(filepath) { - return filepath.replace(/\\/g, '/'); -} -exports.unixify = unixify; -function makeAbsolute(cwd, filepath) { - return path.resolve(cwd, filepath); -} -exports.makeAbsolute = makeAbsolute; -function escape(pattern) { - return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); -} -exports.escape = escape; -function removeLeadingDotSegment(entry) { - // We do not use `startsWith` because this is 10x slower than current implementation for some cases. - // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with - if (entry.charAt(0) === '.') { - const secondCharactery = entry.charAt(1); - if (secondCharactery === '/' || secondCharactery === '\\') { - return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); - } - } - return entry; -} -exports.removeLeadingDotSegment = removeLeadingDotSegment; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; +const path = __webpack_require__(4); +const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ +const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; +/** + * Designed to work only with simple paths: `dir\\file`. + */ +function unixify(filepath) { + return filepath.replace(/\\/g, '/'); +} +exports.unixify = unixify; +function makeAbsolute(cwd, filepath) { + return path.resolve(cwd, filepath); +} +exports.makeAbsolute = makeAbsolute; +function escape(pattern) { + return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); +} +exports.escape = escape; +function removeLeadingDotSegment(entry) { + // We do not use `startsWith` because this is 10x slower than current implementation for some cases. + // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with + if (entry.charAt(0) === '.') { + const secondCharactery = entry.charAt(1); + if (secondCharactery === '/' || secondCharactery === '\\') { + return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); + } + } + return entry; +} +exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), -/* 791 */ +/* 785 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; -const path = __webpack_require__(4); -const globParent = __webpack_require__(265); -const micromatch = __webpack_require__(792); -const picomatch = __webpack_require__(285); -const GLOBSTAR = '**'; -const ESCAPE_SYMBOL = '\\'; -const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; -const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; -const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; -const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; -const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; -function isStaticPattern(pattern, options = {}) { - return !isDynamicPattern(pattern, options); -} -exports.isStaticPattern = isStaticPattern; -function isDynamicPattern(pattern, options = {}) { - /** - * A special case with an empty string is necessary for matching patterns that start with a forward slash. - * An empty string cannot be a dynamic pattern. - * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. - */ - if (pattern === '') { - return false; - } - /** - * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check - * filepath directly (without read directory). - */ - if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { - return true; - } - if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { - return true; - } - return false; -} -exports.isDynamicPattern = isDynamicPattern; -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -exports.convertToPositivePattern = convertToPositivePattern; -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -exports.convertToNegativePattern = convertToNegativePattern; -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -exports.isNegativePattern = isNegativePattern; -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -exports.isPositivePattern = isPositivePattern; -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -exports.getNegativePatterns = getNegativePatterns; -function getPositivePatterns(patterns) { - return patterns.filter(isPositivePattern); -} -exports.getPositivePatterns = getPositivePatterns; -function getBaseDirectory(pattern) { - return globParent(pattern, { flipBackslashes: false }); -} -exports.getBaseDirectory = getBaseDirectory; -function hasGlobStar(pattern) { - return pattern.includes(GLOBSTAR); -} -exports.hasGlobStar = hasGlobStar; -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR); -} -exports.endsWithSlashGlobStar = endsWithSlashGlobStar; -function isAffectDepthOfReadingPattern(pattern) { - const basename = path.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -function expandPatternsWithBraceExpansion(patterns) { - return patterns.reduce((collection, pattern) => { - return collection.concat(expandBraceExpansion(pattern)); - }, []); -} -exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; -function expandBraceExpansion(pattern) { - return micromatch.braces(pattern, { - expand: true, - nodupes: true - }); -} -exports.expandBraceExpansion = expandBraceExpansion; -function getPatternParts(pattern, options) { - let { parts } = picomatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); - /** - * The scan method returns an empty array in some cases. - * See micromatch/picomatch#58 for more details. - */ - if (parts.length === 0) { - parts = [pattern]; - } - /** - * The scan method does not return an empty part for the pattern with a forward slash. - * This is another part of micromatch/picomatch#58. - */ - if (parts[0].startsWith('/')) { - parts[0] = parts[0].slice(1); - parts.unshift(''); - } - return parts; -} -exports.getPatternParts = getPatternParts; -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -exports.makeRe = makeRe; -function convertPatternsToRe(patterns, options) { - return patterns.map((pattern) => makeRe(pattern, options)); -} -exports.convertPatternsToRe = convertPatternsToRe; -function matchAny(entry, patternsRe) { - return patternsRe.some((patternRe) => patternRe.test(entry)); -} -exports.matchAny = matchAny; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; +const path = __webpack_require__(4); +const globParent = __webpack_require__(265); +const micromatch = __webpack_require__(786); +const GLOBSTAR = '**'; +const ESCAPE_SYMBOL = '\\'; +const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; +const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; +const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; +const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; +const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; +function isStaticPattern(pattern, options = {}) { + return !isDynamicPattern(pattern, options); +} +exports.isStaticPattern = isStaticPattern; +function isDynamicPattern(pattern, options = {}) { + /** + * A special case with an empty string is necessary for matching patterns that start with a forward slash. + * An empty string cannot be a dynamic pattern. + * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. + */ + if (pattern === '') { + return false; + } + /** + * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check + * filepath directly (without read directory). + */ + if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { + return true; + } + if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { + return true; + } + return false; +} +exports.isDynamicPattern = isDynamicPattern; +function convertToPositivePattern(pattern) { + return isNegativePattern(pattern) ? pattern.slice(1) : pattern; +} +exports.convertToPositivePattern = convertToPositivePattern; +function convertToNegativePattern(pattern) { + return '!' + pattern; +} +exports.convertToNegativePattern = convertToNegativePattern; +function isNegativePattern(pattern) { + return pattern.startsWith('!') && pattern[1] !== '('; +} +exports.isNegativePattern = isNegativePattern; +function isPositivePattern(pattern) { + return !isNegativePattern(pattern); +} +exports.isPositivePattern = isPositivePattern; +function getNegativePatterns(patterns) { + return patterns.filter(isNegativePattern); +} +exports.getNegativePatterns = getNegativePatterns; +function getPositivePatterns(patterns) { + return patterns.filter(isPositivePattern); +} +exports.getPositivePatterns = getPositivePatterns; +/** + * Returns patterns that can be applied inside the current directory. + * + * @example + * // ['./*', '*', 'a/*'] + * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) + */ +function getPatternsInsideCurrentDirectory(patterns) { + return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern)); +} +exports.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory; +/** + * Returns patterns to be expanded relative to (outside) the current directory. + * + * @example + * // ['../*', './../*'] + * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) + */ +function getPatternsOutsideCurrentDirectory(patterns) { + return patterns.filter(isPatternRelatedToParentDirectory); +} +exports.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory; +function isPatternRelatedToParentDirectory(pattern) { + return pattern.startsWith('..') || pattern.startsWith('./..'); +} +exports.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory; +function getBaseDirectory(pattern) { + return globParent(pattern, { flipBackslashes: false }); +} +exports.getBaseDirectory = getBaseDirectory; +function hasGlobStar(pattern) { + return pattern.includes(GLOBSTAR); +} +exports.hasGlobStar = hasGlobStar; +function endsWithSlashGlobStar(pattern) { + return pattern.endsWith('/' + GLOBSTAR); +} +exports.endsWithSlashGlobStar = endsWithSlashGlobStar; +function isAffectDepthOfReadingPattern(pattern) { + const basename = path.basename(pattern); + return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); +} +exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; +function expandPatternsWithBraceExpansion(patterns) { + return patterns.reduce((collection, pattern) => { + return collection.concat(expandBraceExpansion(pattern)); + }, []); +} +exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; +function expandBraceExpansion(pattern) { + return micromatch.braces(pattern, { + expand: true, + nodupes: true + }); +} +exports.expandBraceExpansion = expandBraceExpansion; +function getPatternParts(pattern, options) { + let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); + /** + * The scan method returns an empty array in some cases. + * See micromatch/picomatch#58 for more details. + */ + if (parts.length === 0) { + parts = [pattern]; + } + /** + * The scan method does not return an empty part for the pattern with a forward slash. + * This is another part of micromatch/picomatch#58. + */ + if (parts[0].startsWith('/')) { + parts[0] = parts[0].slice(1); + parts.unshift(''); + } + return parts; +} +exports.getPatternParts = getPatternParts; +function makeRe(pattern, options) { + return micromatch.makeRe(pattern, options); +} +exports.makeRe = makeRe; +function convertPatternsToRe(patterns, options) { + return patterns.map((pattern) => makeRe(pattern, options)); +} +exports.convertPatternsToRe = convertPatternsToRe; +function matchAny(entry, patternsRe) { + return patternsRe.some((patternRe) => patternRe.test(entry)); +} +exports.matchAny = matchAny; /***/ }), -/* 792 */ +/* 786 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91381,8 +89373,8 @@ exports.matchAny = matchAny; const util = __webpack_require__(113); const braces = __webpack_require__(269); -const picomatch = __webpack_require__(793); -const utils = __webpack_require__(796); +const picomatch = __webpack_require__(787); +const utils = __webpack_require__(790); const isEmptyString = val => val === '' || val === './'; /** @@ -91847,27 +89839,27 @@ module.exports = micromatch; /***/ }), -/* 793 */ +/* 787 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(794); +module.exports = __webpack_require__(788); /***/ }), -/* 794 */ +/* 788 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const scan = __webpack_require__(795); -const parse = __webpack_require__(798); -const utils = __webpack_require__(796); -const constants = __webpack_require__(797); +const scan = __webpack_require__(789); +const parse = __webpack_require__(792); +const utils = __webpack_require__(790); +const constants = __webpack_require__(791); const isObject = val => val && typeof val === 'object' && !Array.isArray(val); /** @@ -92206,13 +90198,13 @@ module.exports = picomatch; /***/ }), -/* 795 */ +/* 789 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(796); +const utils = __webpack_require__(790); const { CHAR_ASTERISK, /* * */ CHAR_AT, /* @ */ @@ -92229,7 +90221,7 @@ const { CHAR_RIGHT_CURLY_BRACE, /* } */ CHAR_RIGHT_PARENTHESES, /* ) */ CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(797); +} = __webpack_require__(791); const isPathSeparator = code => { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; @@ -92604,7 +90596,7 @@ module.exports = scan; /***/ }), -/* 796 */ +/* 790 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92617,7 +90609,7 @@ const { REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(797); +} = __webpack_require__(791); exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); @@ -92675,7 +90667,7 @@ exports.wrapOutput = (input, state = {}, options = {}) => { /***/ }), -/* 797 */ +/* 791 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92861,14 +90853,14 @@ module.exports = { /***/ }), -/* 798 */ +/* 792 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const constants = __webpack_require__(797); -const utils = __webpack_require__(796); +const constants = __webpack_require__(791); +const utils = __webpack_require__(790); /** * Constants @@ -93952,712 +91944,712 @@ module.exports = parse; /***/ }), -/* 799 */ +/* 793 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.merge = void 0; -const merge2 = __webpack_require__(243); -function merge(streams) { - const mergedStream = merge2(streams); - streams.forEach((stream) => { - stream.once('error', (error) => mergedStream.emit('error', error)); - }); - mergedStream.once('close', () => propagateCloseEventToSources(streams)); - mergedStream.once('end', () => propagateCloseEventToSources(streams)); - return mergedStream; -} -exports.merge = merge; -function propagateCloseEventToSources(streams) { - streams.forEach((stream) => stream.emit('close')); -} + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.merge = void 0; +const merge2 = __webpack_require__(243); +function merge(streams) { + const mergedStream = merge2(streams); + streams.forEach((stream) => { + stream.once('error', (error) => mergedStream.emit('error', error)); + }); + mergedStream.once('close', () => propagateCloseEventToSources(streams)); + mergedStream.once('end', () => propagateCloseEventToSources(streams)); + return mergedStream; +} +exports.merge = merge; +function propagateCloseEventToSources(streams) { + streams.forEach((stream) => stream.emit('close')); +} /***/ }), -/* 800 */ +/* 794 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEmpty = exports.isString = void 0; -function isString(input) { - return typeof input === 'string'; -} -exports.isString = isString; -function isEmpty(input) { - return input === ''; -} -exports.isEmpty = isEmpty; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEmpty = exports.isString = void 0; +function isString(input) { + return typeof input === 'string'; +} +exports.isString = isString; +function isEmpty(input) { + return input === ''; +} +exports.isEmpty = isEmpty; /***/ }), -/* 801 */ +/* 795 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(802); -const provider_1 = __webpack_require__(804); -class ProviderAsync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = []; - return new Promise((resolve, reject) => { - const stream = this.api(root, task, options); - stream.once('error', reject); - stream.on('data', (entry) => entries.push(options.transform(entry))); - stream.once('end', () => resolve(entries)); - }); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderAsync; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(796); +const provider_1 = __webpack_require__(798); +class ProviderAsync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = []; + return new Promise((resolve, reject) => { + const stream = this.api(root, task, options); + stream.once('error', reject); + stream.on('data', (entry) => entries.push(options.transform(entry))); + stream.once('end', () => resolve(entries)); + }); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderAsync; /***/ }), -/* 802 */ +/* 796 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(173); -const fsStat = __webpack_require__(295); -const fsWalk = __webpack_require__(300); -const reader_1 = __webpack_require__(803); -class ReaderStream extends reader_1.default { - constructor() { - super(...arguments); - this._walkStream = fsWalk.walkStream; - this._stat = fsStat.stat; - } - dynamic(root, options) { - return this._walkStream(root, options); - } - static(patterns, options) { - const filepaths = patterns.map(this._getFullEntryPath, this); - const stream = new stream_1.PassThrough({ objectMode: true }); - stream._write = (index, _enc, done) => { - return this._getEntry(filepaths[index], patterns[index], options) - .then((entry) => { - if (entry !== null && options.entryFilter(entry)) { - stream.push(entry); - } - if (index === filepaths.length - 1) { - stream.end(); - } - done(); - }) - .catch(done); - }; - for (let i = 0; i < filepaths.length; i++) { - stream.write(i); - } - return stream; - } - _getEntry(filepath, pattern, options) { - return this._getStat(filepath) - .then((stats) => this._makeEntry(stats, pattern)) - .catch((error) => { - if (options.errorFilter(error)) { - return null; - } - throw error; - }); - } - _getStat(filepath) { - return new Promise((resolve, reject) => { - this._stat(filepath, this._fsStatSettings, (error, stats) => { - return error === null ? resolve(stats) : reject(error); - }); - }); - } -} -exports.default = ReaderStream; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(173); +const fsStat = __webpack_require__(289); +const fsWalk = __webpack_require__(294); +const reader_1 = __webpack_require__(797); +class ReaderStream extends reader_1.default { + constructor() { + super(...arguments); + this._walkStream = fsWalk.walkStream; + this._stat = fsStat.stat; + } + dynamic(root, options) { + return this._walkStream(root, options); + } + static(patterns, options) { + const filepaths = patterns.map(this._getFullEntryPath, this); + const stream = new stream_1.PassThrough({ objectMode: true }); + stream._write = (index, _enc, done) => { + return this._getEntry(filepaths[index], patterns[index], options) + .then((entry) => { + if (entry !== null && options.entryFilter(entry)) { + stream.push(entry); + } + if (index === filepaths.length - 1) { + stream.end(); + } + done(); + }) + .catch(done); + }; + for (let i = 0; i < filepaths.length; i++) { + stream.write(i); + } + return stream; + } + _getEntry(filepath, pattern, options) { + return this._getStat(filepath) + .then((stats) => this._makeEntry(stats, pattern)) + .catch((error) => { + if (options.errorFilter(error)) { + return null; + } + throw error; + }); + } + _getStat(filepath) { + return new Promise((resolve, reject) => { + this._stat(filepath, this._fsStatSettings, (error, stats) => { + return error === null ? resolve(stats) : reject(error); + }); + }); + } +} +exports.default = ReaderStream; /***/ }), -/* 803 */ +/* 797 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsStat = __webpack_require__(295); -const utils = __webpack_require__(786); -class Reader { - constructor(_settings) { - this._settings = _settings; - this._fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this._settings.followSymbolicLinks, - fs: this._settings.fs, - throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks - }); - } - _getFullEntryPath(filepath) { - return path.resolve(this._settings.cwd, filepath); - } - _makeEntry(stats, pattern) { - const entry = { - name: pattern, - path: pattern, - dirent: utils.fs.createDirentFromStats(pattern, stats) - }; - if (this._settings.stats) { - entry.stats = stats; - } - return entry; - } - _isFatalError(error) { - return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; - } -} -exports.default = Reader; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsStat = __webpack_require__(289); +const utils = __webpack_require__(780); +class Reader { + constructor(_settings) { + this._settings = _settings; + this._fsStatSettings = new fsStat.Settings({ + followSymbolicLink: this._settings.followSymbolicLinks, + fs: this._settings.fs, + throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks + }); + } + _getFullEntryPath(filepath) { + return path.resolve(this._settings.cwd, filepath); + } + _makeEntry(stats, pattern) { + const entry = { + name: pattern, + path: pattern, + dirent: utils.fs.createDirentFromStats(pattern, stats) + }; + if (this._settings.stats) { + entry.stats = stats; + } + return entry; + } + _isFatalError(error) { + return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; + } +} +exports.default = Reader; /***/ }), -/* 804 */ +/* 798 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const deep_1 = __webpack_require__(805); -const entry_1 = __webpack_require__(808); -const error_1 = __webpack_require__(809); -const entry_2 = __webpack_require__(810); -class Provider { - constructor(_settings) { - this._settings = _settings; - this.errorFilter = new error_1.default(this._settings); - this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); - this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); - this.entryTransformer = new entry_2.default(this._settings); - } - _getRootDirectory(task) { - return path.resolve(this._settings.cwd, task.base); - } - _getReaderOptions(task) { - const basePath = task.base === '.' ? '' : task.base; - return { - basePath, - pathSegmentSeparator: '/', - concurrency: this._settings.concurrency, - deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), - entryFilter: this.entryFilter.getFilter(task.positive, task.negative), - errorFilter: this.errorFilter.getFilter(), - followSymbolicLinks: this._settings.followSymbolicLinks, - fs: this._settings.fs, - stats: this._settings.stats, - throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, - transform: this.entryTransformer.getTransformer() - }; - } - _getMicromatchOptions() { - return { - dot: this._settings.dot, - matchBase: this._settings.baseNameMatch, - nobrace: !this._settings.braceExpansion, - nocase: !this._settings.caseSensitiveMatch, - noext: !this._settings.extglob, - noglobstar: !this._settings.globstar, - posix: true, - strictSlashes: false - }; - } -} -exports.default = Provider; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const deep_1 = __webpack_require__(799); +const entry_1 = __webpack_require__(802); +const error_1 = __webpack_require__(803); +const entry_2 = __webpack_require__(804); +class Provider { + constructor(_settings) { + this._settings = _settings; + this.errorFilter = new error_1.default(this._settings); + this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); + this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); + this.entryTransformer = new entry_2.default(this._settings); + } + _getRootDirectory(task) { + return path.resolve(this._settings.cwd, task.base); + } + _getReaderOptions(task) { + const basePath = task.base === '.' ? '' : task.base; + return { + basePath, + pathSegmentSeparator: '/', + concurrency: this._settings.concurrency, + deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), + entryFilter: this.entryFilter.getFilter(task.positive, task.negative), + errorFilter: this.errorFilter.getFilter(), + followSymbolicLinks: this._settings.followSymbolicLinks, + fs: this._settings.fs, + stats: this._settings.stats, + throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, + transform: this.entryTransformer.getTransformer() + }; + } + _getMicromatchOptions() { + return { + dot: this._settings.dot, + matchBase: this._settings.baseNameMatch, + nobrace: !this._settings.braceExpansion, + nocase: !this._settings.caseSensitiveMatch, + noext: !this._settings.extglob, + noglobstar: !this._settings.globstar, + posix: true, + strictSlashes: false + }; + } +} +exports.default = Provider; /***/ }), -/* 805 */ +/* 799 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(786); -const partial_1 = __webpack_require__(806); -class DeepFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - } - getFilter(basePath, positive, negative) { - const matcher = this._getMatcher(positive); - const negativeRe = this._getNegativePatternsRe(negative); - return (entry) => this._filter(basePath, entry, matcher, negativeRe); - } - _getMatcher(patterns) { - return new partial_1.default(patterns, this._settings, this._micromatchOptions); - } - _getNegativePatternsRe(patterns) { - const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); - return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); - } - _filter(basePath, entry, matcher, negativeRe) { - if (this._isSkippedByDeep(basePath, entry.path)) { - return false; - } - if (this._isSkippedSymbolicLink(entry)) { - return false; - } - const filepath = utils.path.removeLeadingDotSegment(entry.path); - if (this._isSkippedByPositivePatterns(filepath, matcher)) { - return false; - } - return this._isSkippedByNegativePatterns(filepath, negativeRe); - } - _isSkippedByDeep(basePath, entryPath) { - /** - * Avoid unnecessary depth calculations when it doesn't matter. - */ - if (this._settings.deep === Infinity) { - return false; - } - return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; - } - _getEntryLevel(basePath, entryPath) { - const entryPathDepth = entryPath.split('/').length; - if (basePath === '') { - return entryPathDepth; - } - const basePathDepth = basePath.split('/').length; - return entryPathDepth - basePathDepth; - } - _isSkippedSymbolicLink(entry) { - return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); - } - _isSkippedByPositivePatterns(entryPath, matcher) { - return !this._settings.baseNameMatch && !matcher.match(entryPath); - } - _isSkippedByNegativePatterns(entryPath, patternsRe) { - return !utils.pattern.matchAny(entryPath, patternsRe); - } -} -exports.default = DeepFilter; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(780); +const partial_1 = __webpack_require__(800); +class DeepFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + } + getFilter(basePath, positive, negative) { + const matcher = this._getMatcher(positive); + const negativeRe = this._getNegativePatternsRe(negative); + return (entry) => this._filter(basePath, entry, matcher, negativeRe); + } + _getMatcher(patterns) { + return new partial_1.default(patterns, this._settings, this._micromatchOptions); + } + _getNegativePatternsRe(patterns) { + const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); + return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); + } + _filter(basePath, entry, matcher, negativeRe) { + if (this._isSkippedByDeep(basePath, entry.path)) { + return false; + } + if (this._isSkippedSymbolicLink(entry)) { + return false; + } + const filepath = utils.path.removeLeadingDotSegment(entry.path); + if (this._isSkippedByPositivePatterns(filepath, matcher)) { + return false; + } + return this._isSkippedByNegativePatterns(filepath, negativeRe); + } + _isSkippedByDeep(basePath, entryPath) { + /** + * Avoid unnecessary depth calculations when it doesn't matter. + */ + if (this._settings.deep === Infinity) { + return false; + } + return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; + } + _getEntryLevel(basePath, entryPath) { + const entryPathDepth = entryPath.split('/').length; + if (basePath === '') { + return entryPathDepth; + } + const basePathDepth = basePath.split('/').length; + return entryPathDepth - basePathDepth; + } + _isSkippedSymbolicLink(entry) { + return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); + } + _isSkippedByPositivePatterns(entryPath, matcher) { + return !this._settings.baseNameMatch && !matcher.match(entryPath); + } + _isSkippedByNegativePatterns(entryPath, patternsRe) { + return !utils.pattern.matchAny(entryPath, patternsRe); + } +} +exports.default = DeepFilter; /***/ }), -/* 806 */ +/* 800 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(807); -class PartialMatcher extends matcher_1.default { - match(filepath) { - const parts = filepath.split('/'); - const levels = parts.length; - const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); - for (const pattern of patterns) { - const section = pattern.sections[0]; - /** - * In this case, the pattern has a globstar and we must read all directories unconditionally, - * but only if the level has reached the end of the first group. - * - * fixtures/{a,b}/** - * ^ true/false ^ always true - */ - if (!pattern.complete && levels > section.length) { - return true; - } - const match = parts.every((part, index) => { - const segment = pattern.segments[index]; - if (segment.dynamic && segment.patternRe.test(part)) { - return true; - } - if (!segment.dynamic && segment.pattern === part) { - return true; - } - return false; - }); - if (match) { - return true; - } - } - return false; - } -} -exports.default = PartialMatcher; + +Object.defineProperty(exports, "__esModule", { value: true }); +const matcher_1 = __webpack_require__(801); +class PartialMatcher extends matcher_1.default { + match(filepath) { + const parts = filepath.split('/'); + const levels = parts.length; + const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); + for (const pattern of patterns) { + const section = pattern.sections[0]; + /** + * In this case, the pattern has a globstar and we must read all directories unconditionally, + * but only if the level has reached the end of the first group. + * + * fixtures/{a,b}/** + * ^ true/false ^ always true + */ + if (!pattern.complete && levels > section.length) { + return true; + } + const match = parts.every((part, index) => { + const segment = pattern.segments[index]; + if (segment.dynamic && segment.patternRe.test(part)) { + return true; + } + if (!segment.dynamic && segment.pattern === part) { + return true; + } + return false; + }); + if (match) { + return true; + } + } + return false; + } +} +exports.default = PartialMatcher; /***/ }), -/* 807 */ +/* 801 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(786); -class Matcher { - constructor(_patterns, _settings, _micromatchOptions) { - this._patterns = _patterns; - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this._storage = []; - this._fillStorage(); - } - _fillStorage() { - /** - * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). - * So, before expand patterns with brace expansion into separated patterns. - */ - const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); - for (const pattern of patterns) { - const segments = this._getPatternSegments(pattern); - const sections = this._splitSegmentsIntoSections(segments); - this._storage.push({ - complete: sections.length <= 1, - pattern, - segments, - sections - }); - } - } - _getPatternSegments(pattern) { - const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); - return parts.map((part) => { - const dynamic = utils.pattern.isDynamicPattern(part, this._settings); - if (!dynamic) { - return { - dynamic: false, - pattern: part - }; - } - return { - dynamic: true, - pattern: part, - patternRe: utils.pattern.makeRe(part, this._micromatchOptions) - }; - }); - } - _splitSegmentsIntoSections(segments) { - return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); - } -} -exports.default = Matcher; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(780); +class Matcher { + constructor(_patterns, _settings, _micromatchOptions) { + this._patterns = _patterns; + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this._storage = []; + this._fillStorage(); + } + _fillStorage() { + /** + * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). + * So, before expand patterns with brace expansion into separated patterns. + */ + const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); + for (const pattern of patterns) { + const segments = this._getPatternSegments(pattern); + const sections = this._splitSegmentsIntoSections(segments); + this._storage.push({ + complete: sections.length <= 1, + pattern, + segments, + sections + }); + } + } + _getPatternSegments(pattern) { + const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); + return parts.map((part) => { + const dynamic = utils.pattern.isDynamicPattern(part, this._settings); + if (!dynamic) { + return { + dynamic: false, + pattern: part + }; + } + return { + dynamic: true, + pattern: part, + patternRe: utils.pattern.makeRe(part, this._micromatchOptions) + }; + }); + } + _splitSegmentsIntoSections(segments) { + return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); + } +} +exports.default = Matcher; /***/ }), -/* 808 */ +/* 802 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(786); -class EntryFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this.index = new Map(); - } - getFilter(positive, negative) { - const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); - const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); - return (entry) => this._filter(entry, positiveRe, negativeRe); - } - _filter(entry, positiveRe, negativeRe) { - if (this._settings.unique && this._isDuplicateEntry(entry)) { - return false; - } - if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { - return false; - } - if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { - return false; - } - const filepath = this._settings.baseNameMatch ? entry.name : entry.path; - const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); - if (this._settings.unique && isMatched) { - this._createIndexRecord(entry); - } - return isMatched; - } - _isDuplicateEntry(entry) { - return this.index.has(entry.path); - } - _createIndexRecord(entry) { - this.index.set(entry.path, undefined); - } - _onlyFileFilter(entry) { - return this._settings.onlyFiles && !entry.dirent.isFile(); - } - _onlyDirectoryFilter(entry) { - return this._settings.onlyDirectories && !entry.dirent.isDirectory(); - } - _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { - if (!this._settings.absolute) { - return false; - } - const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); - return utils.pattern.matchAny(fullpath, patternsRe); - } - _isMatchToPatterns(entryPath, patternsRe) { - const filepath = utils.path.removeLeadingDotSegment(entryPath); - return utils.pattern.matchAny(filepath, patternsRe); - } -} -exports.default = EntryFilter; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(780); +class EntryFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this.index = new Map(); + } + getFilter(positive, negative) { + const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); + const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); + return (entry) => this._filter(entry, positiveRe, negativeRe); + } + _filter(entry, positiveRe, negativeRe) { + if (this._settings.unique && this._isDuplicateEntry(entry)) { + return false; + } + if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { + return false; + } + if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { + return false; + } + const filepath = this._settings.baseNameMatch ? entry.name : entry.path; + const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); + if (this._settings.unique && isMatched) { + this._createIndexRecord(entry); + } + return isMatched; + } + _isDuplicateEntry(entry) { + return this.index.has(entry.path); + } + _createIndexRecord(entry) { + this.index.set(entry.path, undefined); + } + _onlyFileFilter(entry) { + return this._settings.onlyFiles && !entry.dirent.isFile(); + } + _onlyDirectoryFilter(entry) { + return this._settings.onlyDirectories && !entry.dirent.isDirectory(); + } + _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { + if (!this._settings.absolute) { + return false; + } + const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); + return utils.pattern.matchAny(fullpath, patternsRe); + } + _isMatchToPatterns(entryPath, patternsRe) { + const filepath = utils.path.removeLeadingDotSegment(entryPath); + return utils.pattern.matchAny(filepath, patternsRe); + } +} +exports.default = EntryFilter; /***/ }), -/* 809 */ +/* 803 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(786); -class ErrorFilter { - constructor(_settings) { - this._settings = _settings; - } - getFilter() { - return (error) => this._isNonFatalError(error); - } - _isNonFatalError(error) { - return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; - } -} -exports.default = ErrorFilter; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(780); +class ErrorFilter { + constructor(_settings) { + this._settings = _settings; + } + getFilter() { + return (error) => this._isNonFatalError(error); + } + _isNonFatalError(error) { + return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; + } +} +exports.default = ErrorFilter; /***/ }), -/* 810 */ +/* 804 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(786); -class EntryTransformer { - constructor(_settings) { - this._settings = _settings; - } - getTransformer() { - return (entry) => this._transform(entry); - } - _transform(entry) { - let filepath = entry.path; - if (this._settings.absolute) { - filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); - filepath = utils.path.unixify(filepath); - } - if (this._settings.markDirectories && entry.dirent.isDirectory()) { - filepath += '/'; - } - if (!this._settings.objectMode) { - return filepath; - } - return Object.assign(Object.assign({}, entry), { path: filepath }); - } -} -exports.default = EntryTransformer; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(780); +class EntryTransformer { + constructor(_settings) { + this._settings = _settings; + } + getTransformer() { + return (entry) => this._transform(entry); + } + _transform(entry) { + let filepath = entry.path; + if (this._settings.absolute) { + filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); + filepath = utils.path.unixify(filepath); + } + if (this._settings.markDirectories && entry.dirent.isDirectory()) { + filepath += '/'; + } + if (!this._settings.objectMode) { + return filepath; + } + return Object.assign(Object.assign({}, entry), { path: filepath }); + } +} +exports.default = EntryTransformer; /***/ }), -/* 811 */ +/* 805 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(173); -const stream_2 = __webpack_require__(802); -const provider_1 = __webpack_require__(804); -class ProviderStream extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_2.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const source = this.api(root, task, options); - const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); - source - .once('error', (error) => destination.emit('error', error)) - .on('data', (entry) => destination.emit('data', options.transform(entry))) - .once('end', () => destination.emit('end')); - destination - .once('close', () => source.destroy()); - return destination; - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderStream; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(173); +const stream_2 = __webpack_require__(796); +const provider_1 = __webpack_require__(798); +class ProviderStream extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_2.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const source = this.api(root, task, options); + const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); + source + .once('error', (error) => destination.emit('error', error)) + .on('data', (entry) => destination.emit('data', options.transform(entry))) + .once('end', () => destination.emit('end')); + destination + .once('close', () => source.destroy()); + return destination; + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderStream; /***/ }), -/* 812 */ +/* 806 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(813); -const provider_1 = __webpack_require__(804); -class ProviderSync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new sync_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = this.api(root, task, options); - return entries.map(options.transform); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderSync; + +Object.defineProperty(exports, "__esModule", { value: true }); +const sync_1 = __webpack_require__(807); +const provider_1 = __webpack_require__(798); +class ProviderSync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new sync_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = this.api(root, task, options); + return entries.map(options.transform); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderSync; /***/ }), -/* 813 */ +/* 807 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(295); -const fsWalk = __webpack_require__(300); -const reader_1 = __webpack_require__(803); -class ReaderSync extends reader_1.default { - constructor() { - super(...arguments); - this._walkSync = fsWalk.walkSync; - this._statSync = fsStat.statSync; - } - dynamic(root, options) { - return this._walkSync(root, options); - } - static(patterns, options) { - const entries = []; - for (const pattern of patterns) { - const filepath = this._getFullEntryPath(pattern); - const entry = this._getEntry(filepath, pattern, options); - if (entry === null || !options.entryFilter(entry)) { - continue; - } - entries.push(entry); - } - return entries; - } - _getEntry(filepath, pattern, options) { - try { - const stats = this._getStat(filepath); - return this._makeEntry(stats, pattern); - } - catch (error) { - if (options.errorFilter(error)) { - return null; - } - throw error; - } - } - _getStat(filepath) { - return this._statSync(filepath, this._fsStatSettings); - } -} -exports.default = ReaderSync; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__(289); +const fsWalk = __webpack_require__(294); +const reader_1 = __webpack_require__(797); +class ReaderSync extends reader_1.default { + constructor() { + super(...arguments); + this._walkSync = fsWalk.walkSync; + this._statSync = fsStat.statSync; + } + dynamic(root, options) { + return this._walkSync(root, options); + } + static(patterns, options) { + const entries = []; + for (const pattern of patterns) { + const filepath = this._getFullEntryPath(pattern); + const entry = this._getEntry(filepath, pattern, options); + if (entry === null || !options.entryFilter(entry)) { + continue; + } + entries.push(entry); + } + return entries; + } + _getEntry(filepath, pattern, options) { + try { + const stats = this._getStat(filepath); + return this._makeEntry(stats, pattern); + } + catch (error) { + if (options.errorFilter(error)) { + return null; + } + throw error; + } + } + _getStat(filepath) { + return this._statSync(filepath, this._fsStatSettings); + } +} +exports.default = ReaderSync; /***/ }), -/* 814 */ +/* 808 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; -const fs = __webpack_require__(132); -const os = __webpack_require__(122); -/** - * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. - * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 - */ -const CPU_COUNT = Math.max(os.cpus().length, 1); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - lstatSync: fs.lstatSync, - stat: fs.stat, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync -}; -class Settings { - constructor(_options = {}) { - this._options = _options; - this.absolute = this._getValue(this._options.absolute, false); - this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); - this.braceExpansion = this._getValue(this._options.braceExpansion, true); - this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); - this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); - this.cwd = this._getValue(this._options.cwd, process.cwd()); - this.deep = this._getValue(this._options.deep, Infinity); - this.dot = this._getValue(this._options.dot, false); - this.extglob = this._getValue(this._options.extglob, true); - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); - this.fs = this._getFileSystemMethods(this._options.fs); - this.globstar = this._getValue(this._options.globstar, true); - this.ignore = this._getValue(this._options.ignore, []); - this.markDirectories = this._getValue(this._options.markDirectories, false); - this.objectMode = this._getValue(this._options.objectMode, false); - this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); - this.onlyFiles = this._getValue(this._options.onlyFiles, true); - this.stats = this._getValue(this._options.stats, false); - this.suppressErrors = this._getValue(this._options.suppressErrors, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); - this.unique = this._getValue(this._options.unique, true); - if (this.onlyDirectories) { - this.onlyFiles = false; - } - if (this.stats) { - this.objectMode = true; - } - } - _getValue(option, value) { - return option === undefined ? value : option; - } - _getFileSystemMethods(methods = {}) { - return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); - } -} -exports.default = Settings; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; +const fs = __webpack_require__(132); +const os = __webpack_require__(122); +/** + * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. + * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 + */ +const CPU_COUNT = Math.max(os.cpus().length, 1); +exports.DEFAULT_FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + lstatSync: fs.lstatSync, + stat: fs.stat, + statSync: fs.statSync, + readdir: fs.readdir, + readdirSync: fs.readdirSync +}; +class Settings { + constructor(_options = {}) { + this._options = _options; + this.absolute = this._getValue(this._options.absolute, false); + this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); + this.braceExpansion = this._getValue(this._options.braceExpansion, true); + this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); + this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); + this.cwd = this._getValue(this._options.cwd, process.cwd()); + this.deep = this._getValue(this._options.deep, Infinity); + this.dot = this._getValue(this._options.dot, false); + this.extglob = this._getValue(this._options.extglob, true); + this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); + this.fs = this._getFileSystemMethods(this._options.fs); + this.globstar = this._getValue(this._options.globstar, true); + this.ignore = this._getValue(this._options.ignore, []); + this.markDirectories = this._getValue(this._options.markDirectories, false); + this.objectMode = this._getValue(this._options.objectMode, false); + this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); + this.onlyFiles = this._getValue(this._options.onlyFiles, true); + this.stats = this._getValue(this._options.stats, false); + this.suppressErrors = this._getValue(this._options.suppressErrors, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); + this.unique = this._getValue(this._options.unique, true); + if (this.onlyDirectories) { + this.onlyFiles = false; + } + if (this.stats) { + this.objectMode = true; + } + } + _getValue(option, value) { + return option === undefined ? value : option; + } + _getFileSystemMethods(methods = {}) { + return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); + } +} +exports.default = Settings; /***/ }), -/* 815 */ +/* 809 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -94665,9 +92657,9 @@ exports.default = Settings; const {promisify} = __webpack_require__(113); const fs = __webpack_require__(132); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(784); -const gitIgnore = __webpack_require__(335); -const slash = __webpack_require__(336); +const fastGlob = __webpack_require__(778); +const gitIgnore = __webpack_require__(329); +const slash = __webpack_require__(330); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -94784,7 +92776,7 @@ module.exports.sync = options => { /***/ }), -/* 816 */ +/* 810 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -94837,7 +92829,7 @@ module.exports = { /***/ }), -/* 817 */ +/* 811 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -94845,17 +92837,17 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return buildNonBazelProductionProjects; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProductionProjects", function() { return getProductionProjects; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProject", function() { return buildProject; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(571); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(565); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(568); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(562); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); -/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(349); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(346); +/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(343); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(340); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License diff --git a/packages/kbn-react-field/BUILD.bazel b/packages/kbn-react-field/BUILD.bazel index d7645d86dbd1a..5165ca2fc90fc 100644 --- a/packages/kbn-react-field/BUILD.bazel +++ b/packages/kbn-react-field/BUILD.bazel @@ -49,7 +49,6 @@ TYPES_DEPS = [ "@npm//@types/classnames", "@npm//@types/react", "@npm//@elastic/eui", - "@npm//resize-observer-polyfill", ] jsts_transpiler( diff --git a/packages/kbn-react-field/tsconfig.json b/packages/kbn-react-field/tsconfig.json index 90c8a63c746f1..4d37e1825c85a 100644 --- a/packages/kbn-react-field/tsconfig.json +++ b/packages/kbn-react-field/tsconfig.json @@ -10,7 +10,6 @@ "types": [ "jest", "node", - "resize-observer-polyfill" ] }, "include": [ diff --git a/packages/kbn-securitysolution-autocomplete/BUILD.bazel b/packages/kbn-securitysolution-autocomplete/BUILD.bazel index ac90a0479ce2a..57ac8c62273e0 100644 --- a/packages/kbn-securitysolution-autocomplete/BUILD.bazel +++ b/packages/kbn-securitysolution-autocomplete/BUILD.bazel @@ -42,7 +42,6 @@ RUNTIME_DEPS = [ "@npm//enzyme", "@npm//moment", "@npm//react", - "@npm//resize-observer-polyfill", ] TYPES_DEPS = [ @@ -55,7 +54,6 @@ TYPES_DEPS = [ "@npm//@testing-library/react", "@npm//@testing-library/react-hooks", "@npm//moment", - "@npm//resize-observer-polyfill", "@npm//@types/enzyme", "@npm//@types/jest", "@npm//@types/node", diff --git a/packages/kbn-securitysolution-autocomplete/tsconfig.json b/packages/kbn-securitysolution-autocomplete/tsconfig.json index fa7eff8234011..b2e24676cdbd4 100644 --- a/packages/kbn-securitysolution-autocomplete/tsconfig.json +++ b/packages/kbn-securitysolution-autocomplete/tsconfig.json @@ -8,7 +8,7 @@ "sourceMap": true, "sourceRoot": "../../../../packages/kbn-securitysolution-autocomplete/src", "rootDir": "src", - "types": ["jest", "node", "resize-observer-polyfill"] + "types": ["jest", "node"] }, "include": ["src/**/*"], } diff --git a/packages/kbn-storybook/src/webpack.config.ts b/packages/kbn-storybook/src/webpack.config.ts index 53f9c82b86815..94b1a34728779 100644 --- a/packages/kbn-storybook/src/webpack.config.ts +++ b/packages/kbn-storybook/src/webpack.config.ts @@ -32,9 +32,11 @@ function isHtmlPlugin(plugin: any): plugin is { options: { template: string } } return !!(typeof plugin.options?.template === 'string'); } -function isBabelLoaderRule(rule: webpack.RuleSetRule): rule is webpack.RuleSetRule & { +interface BabelLoaderRule extends webpack.RuleSetRule { use: webpack.RuleSetLoader[]; -} { +} + +function isBabelLoaderRule(rule: webpack.RuleSetRule): rule is BabelLoaderRule { return !!( rule.use && Array.isArray(rule.use) && diff --git a/packages/kbn-test/src/jest/run.ts b/packages/kbn-test/src/jest/run.ts index 697402adf3dd1..26fa12497357c 100644 --- a/packages/kbn-test/src/jest/run.ts +++ b/packages/kbn-test/src/jest/run.ts @@ -36,8 +36,7 @@ declare global { interface Global {} interface InspectOptions {} - interface ConsoleConstructor - extends console.ConsoleConstructor {} + interface ConsoleConstructor extends console.ConsoleConstructor {} } } /* eslint-enable */ @@ -59,7 +58,7 @@ export function runJest(configName = 'jest.config.js') { const cwd: string = process.env.INIT_CWD || process.cwd(); if (!argv.config) { - testFiles = argv._.splice(2).map((p) => resolve(cwd, p)); + testFiles = argv._.splice(2).map((p) => resolve(cwd, p.toString())); const commonTestFiles = commonBasePath(testFiles); const testFilesProvided = testFiles.length > 0; diff --git a/packages/kbn-typed-react-router-config/BUILD.bazel b/packages/kbn-typed-react-router-config/BUILD.bazel index 7fccc53bd7449..b347915ae3310 100644 --- a/packages/kbn-typed-react-router-config/BUILD.bazel +++ b/packages/kbn-typed-react-router-config/BUILD.bazel @@ -41,6 +41,7 @@ TYPES_DEPS = [ "@npm//query-string", "@npm//utility-types", "@npm//@types/jest", + "@npm//@types/history", "@npm//@types/node", "@npm//@types/react-router-config", "@npm//@types/react-router-dom", diff --git a/packages/kbn-typed-react-router-config/src/create_router.test.tsx b/packages/kbn-typed-react-router-config/src/create_router.test.tsx index ac337f8bb5b87..e82fcf791804e 100644 --- a/packages/kbn-typed-react-router-config/src/create_router.test.tsx +++ b/packages/kbn-typed-react-router-config/src/create_router.test.tsx @@ -267,6 +267,7 @@ describe('createRouter', () => { const matches = router.matchRoutes('/', history.location); + // @ts-expect-error 4.3.5 upgrade - router doesn't seem able to merge properly when two routes match expect(matches[1]?.match.params).toEqual({ query: { rangeFrom: 'now-30m', @@ -285,6 +286,7 @@ describe('createRouter', () => { expect(matchedRoutes.length).toEqual(4); + // @ts-expect-error 4.3.5 upgrade - router doesn't seem able to merge properly when two routes match expect(matchedRoutes[matchedRoutes.length - 1].match).toEqual({ isExact: true, params: { diff --git a/packages/kbn-typed-react-router-config/src/create_router.ts b/packages/kbn-typed-react-router-config/src/create_router.ts index 89ff4fc6b0c6c..186f949d9c8e8 100644 --- a/packages/kbn-typed-react-router-config/src/create_router.ts +++ b/packages/kbn-typed-react-router-config/src/create_router.ts @@ -23,7 +23,7 @@ function toReactRouterPath(path: string) { return path.replace(/(?:{([^\/]+)})/g, ':$1'); } -export function createRouter(routes: TRoutes): Router { +export function createRouter(routes: TRoute[]): Router { const routesByReactRouterConfig = new Map(); const reactRouterConfigsByRoute = new Map(); @@ -181,10 +181,8 @@ export function createRouter(routes: TRoutes): Router { - return link(path, ...args); - }, + const router = { + link, getParams: (...args: any[]) => { const matches = matchRoutes(...args); return matches.length @@ -197,11 +195,13 @@ export function createRouter(routes: TRoutes): Router { return matchRoutes(...args) as any; }, - getRoutePath: (route) => { + getRoutePath: (route: Route) => { return reactRouterConfigsByRoute.get(route)!.path as string; }, getRoutesToMatch: (path: string) => { - return getRoutesToMatch(path) as unknown as FlattenRoutesOf; + return getRoutesToMatch(path) as unknown as FlattenRoutesOf; }, }; + + return router; } diff --git a/packages/kbn-typed-react-router-config/src/types/index.ts b/packages/kbn-typed-react-router-config/src/types/index.ts index c1ae5afd816ee..3c09b60054a0c 100644 --- a/packages/kbn-typed-react-router-config/src/types/index.ts +++ b/packages/kbn-typed-react-router-config/src/types/index.ts @@ -115,7 +115,7 @@ export interface RouteMatch { params: t.Type; } ? t.TypeOf - : {}; + : AnyObj; }; } @@ -160,10 +160,11 @@ interface ReadonlyPlainRoute { } export type Route = PlainRoute | ReadonlyPlainRoute; +type AnyObj = Record; interface DefaultOutput { - path: {}; - query: {}; + path: AnyObj; + query: AnyObj; } type OutputOfRouteMatch = TRouteMatch extends { @@ -190,20 +191,21 @@ type TypeOfRouteMatch = TRouteMatch extends { route: { params: t.Type }; } ? t.TypeOf - : {}; + : AnyObj; type TypeOfMatches = TRouteMatches extends [RouteMatch] ? TypeOfRouteMatch : TRouteMatches extends [RouteMatch, ...infer TNextRouteMatches] ? TypeOfRouteMatch & - (TNextRouteMatches extends RouteMatch[] ? TypeOfMatches : {}) - : {}; + (TNextRouteMatches extends RouteMatch[] ? TypeOfMatches : AnyObj) + : AnyObj; export type TypeOf< TRoutes extends Route[], TPath extends PathsOf, TWithDefaultOutput extends boolean = true -> = TypeOfMatches> & (TWithDefaultOutput extends true ? DefaultOutput : {}); +> = TypeOfMatches> & + (TWithDefaultOutput extends true ? DefaultOutput : AnyObj); export type TypeAsArgs = keyof TObject extends never ? [] @@ -276,7 +278,7 @@ type MapRoute = MaybeUnion< >; } > - : {} + : AnyObj >; type MapRoutes = TRoutes extends [Route] @@ -341,7 +343,7 @@ type MapRoutes = TRoutes extends [Route] MapRoute & MapRoute & MapRoute - : {}; + : AnyObj; // const element = null as any; diff --git a/packages/kbn-ui-shared-deps-npm/BUILD.bazel b/packages/kbn-ui-shared-deps-npm/BUILD.bazel index b75315120e90d..2beedafd699fd 100644 --- a/packages/kbn-ui-shared-deps-npm/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-npm/BUILD.bazel @@ -53,7 +53,6 @@ RUNTIME_DEPS = [ "@npm//react-router", "@npm//react", "@npm//regenerator-runtime", - "@npm//resize-observer-polyfill", "@npm//rison-node", "@npm//rxjs", "@npm//styled-components", @@ -90,7 +89,6 @@ TYPES_DEPS = [ "@npm//react-router", "@npm//react-router-dom", "@npm//regenerator-runtime", - "@npm//resize-observer-polyfill", "@npm//rison-node", "@npm//rxjs", "@npm//styled-components", diff --git a/packages/kbn-ui-shared-deps-npm/tsconfig.json b/packages/kbn-ui-shared-deps-npm/tsconfig.json index be9a9462f76d8..107d82aa59ee8 100644 --- a/packages/kbn-ui-shared-deps-npm/tsconfig.json +++ b/packages/kbn-ui-shared-deps-npm/tsconfig.json @@ -11,7 +11,6 @@ "sourceRoot": "../../../../packages/kbn-ui-shared-deps-npm/src", "types": [ "node", - "resize-observer-polyfill" ] }, "include": [ diff --git a/packages/kbn-ui-shared-deps-src/tsconfig.json b/packages/kbn-ui-shared-deps-src/tsconfig.json index bfee34694748d..521fb122e4659 100644 --- a/packages/kbn-ui-shared-deps-src/tsconfig.json +++ b/packages/kbn-ui-shared-deps-src/tsconfig.json @@ -11,7 +11,6 @@ "sourceRoot": "../../../../packages/kbn-ui-shared-deps-src/src", "types": [ "node", - "resize-observer-polyfill" ] }, "include": [ diff --git a/src/core/public/application/scoped_history.ts b/src/core/public/application/scoped_history.ts index 2ab60e66b860f..284465c8f305b 100644 --- a/src/core/public/application/scoped_history.ts +++ b/src/core/public/application/scoped_history.ts @@ -57,7 +57,10 @@ export class ScopedHistory */ private blockUnregisterCallbacks: Set = new Set(); - constructor(private readonly parentHistory: History, private readonly basePath: string) { + constructor( + private readonly parentHistory: History, + private readonly basePath: string + ) { const parentPath = this.parentHistory.location.pathname; if (!parentPath.startsWith(basePath)) { throw new Error( @@ -75,10 +78,8 @@ export class ScopedHistory * * @param basePath the URL path scope for the sub history */ - public createSubHistory = ( - basePath: string - ): ScopedHistory => { - return new ScopedHistory(this, basePath); + public createSubHistory = (basePath: string) => { + return new ScopedHistory(this, basePath); }; /** diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index ceb65827cd9a4..1a2f9d4296f8a 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -43,7 +43,7 @@ interface ConstructorParams { kibanaVersion: string; } -interface StartDeps { +export interface StartDeps { application: InternalApplicationStart; docLinks: DocLinksStart; http: HttpStart; diff --git a/src/core/public/core_app/core_app.ts b/src/core/public/core_app/core_app.ts index 00532b9150aef..648677e67e1cf 100644 --- a/src/core/public/core_app/core_app.ts +++ b/src/core/public/core_app/core_app.ts @@ -26,14 +26,14 @@ import { import { renderApp as renderStatusApp } from './status'; import { DocLinksStart } from '../doc_links'; -interface SetupDeps { +export interface SetupDeps { application: InternalApplicationSetup; http: HttpSetup; injectedMetadata: InjectedMetadataSetup; notifications: NotificationsSetup; } -interface StartDeps { +export interface StartDeps { application: InternalApplicationStart; docLinks: DocLinksStart; http: HttpStart; diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index f99f621a52c83..24c085ef64de3 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -9,7 +9,7 @@ import { deepFreeze } from '@kbn/std'; import { InjectedMetadataSetup } from '../injected_metadata'; -interface StartDeps { +export interface StartDeps { injectedMetadata: InjectedMetadataSetup; } diff --git a/src/core/public/fatal_errors/fatal_errors_service.tsx b/src/core/public/fatal_errors/fatal_errors_service.tsx index 975c0160d83b2..262ee9e702f40 100644 --- a/src/core/public/fatal_errors/fatal_errors_service.tsx +++ b/src/core/public/fatal_errors/fatal_errors_service.tsx @@ -16,7 +16,7 @@ import { InjectedMetadataSetup } from '../injected_metadata'; import { FatalErrorsScreen } from './fatal_errors_screen'; import { FatalErrorInfo, getErrorInfo } from './get_error_info'; -interface Deps { +export interface Deps { i18n: I18nStart; injectedMetadata: InjectedMetadataSetup; } diff --git a/src/core/public/integrations/integrations_service.ts b/src/core/public/integrations/integrations_service.ts index d6f2b8c5fb2f7..7c52205022440 100644 --- a/src/core/public/integrations/integrations_service.ts +++ b/src/core/public/integrations/integrations_service.ts @@ -12,7 +12,7 @@ import { CoreService } from '../../types'; import { MomentService } from './moment'; import { StylesService } from './styles'; -interface Deps { +export interface Deps { uiSettings: IUiSettingsClient; } diff --git a/src/core/public/notifications/notifications_service.ts b/src/core/public/notifications/notifications_service.ts index 383fa2d1914cc..d31909a19ff75 100644 --- a/src/core/public/notifications/notifications_service.ts +++ b/src/core/public/notifications/notifications_service.ts @@ -15,11 +15,11 @@ import { ToastsService, ToastsSetup, ToastsStart } from './toasts'; import { IUiSettingsClient } from '../ui_settings'; import { OverlayStart } from '../overlays'; -interface SetupDeps { +export interface SetupDeps { uiSettings: IUiSettingsClient; } -interface StartDeps { +export interface StartDeps { i18n: I18nStart; overlays: OverlayStart; theme: ThemeServiceStart; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 9bb65a9dd0b57..30225acb3dd8d 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -42,6 +42,7 @@ import { Request as Request_2 } from '@hapi/hapi'; import * as Rx from 'rxjs'; import { SchemaTypeError } from '@kbn/config-schema'; import type { ThemeVersion } from '@kbn/ui-shared-deps-npm'; +import { TransitionPromptHook } from 'history'; import type { TransportRequestOptions } from '@elastic/elasticsearch'; import type { TransportRequestParams } from '@elastic/elasticsearch'; import type { TransportResult } from '@elastic/elasticsearch'; @@ -1680,13 +1681,13 @@ export interface SavedObjectsUpdateOptions { // @public export class ScopedHistory implements History_2 { - constructor(parentHistory: History_2, basePath: string); + constructor(parentHistory: History_2, basePath: string); get action(): Action; - block: (prompt?: string | boolean | History_2.TransitionPromptHook | undefined) => UnregisterCallback; + block: (prompt?: string | boolean | TransitionPromptHook | undefined) => UnregisterCallback; createHref: (location: LocationDescriptorObject, { prependBasePath }?: { prependBasePath?: boolean | undefined; }) => Href; - createSubHistory: (basePath: string) => ScopedHistory; + createSubHistory: (basePath: string) => ScopedHistory; go: (n: number) => void; goBack: () => void; goForward: () => void; diff --git a/src/core/public/rendering/rendering_service.tsx b/src/core/public/rendering/rendering_service.tsx index 7c84146d1aa86..92c0f2a9b7fca 100644 --- a/src/core/public/rendering/rendering_service.tsx +++ b/src/core/public/rendering/rendering_service.tsx @@ -18,7 +18,7 @@ import type { I18nStart } from '../i18n'; import { CoreContextProvider } from '../utils'; import { AppWrapper } from './app_containers'; -interface StartDeps { +export interface StartDeps { application: InternalApplicationStart; chrome: InternalChromeStart; overlays: OverlayStart; diff --git a/src/core/public/theme/theme_service.ts b/src/core/public/theme/theme_service.ts index fc67ac4a595eb..bdb00ee4fdce9 100644 --- a/src/core/public/theme/theme_service.ts +++ b/src/core/public/theme/theme_service.ts @@ -11,7 +11,7 @@ import { shareReplay, takeUntil } from 'rxjs/operators'; import { InjectedMetadataSetup } from '../injected_metadata'; import type { CoreTheme, ThemeServiceSetup, ThemeServiceStart } from './types'; -interface SetupDeps { +export interface SetupDeps { injectedMetadata: InjectedMetadataSetup; } diff --git a/src/core/public/ui_settings/ui_settings_service.ts b/src/core/public/ui_settings/ui_settings_service.ts index 6bbbed9a1ca60..1a3f275aa31ed 100644 --- a/src/core/public/ui_settings/ui_settings_service.ts +++ b/src/core/public/ui_settings/ui_settings_service.ts @@ -15,7 +15,7 @@ import { UiSettingsApi } from './ui_settings_api'; import { UiSettingsClient } from './ui_settings_client'; import { IUiSettingsClient } from './types'; -interface UiSettingsServiceDeps { +export interface UiSettingsServiceDeps { http: HttpSetup; injectedMetadata: InjectedMetadataSetup; } diff --git a/src/core/server/context/container/context.mock.ts b/src/core/server/context/container/context.mock.ts index 9996d609fc074..c8fcea83eae45 100644 --- a/src/core/server/context/container/context.mock.ts +++ b/src/core/server/context/container/context.mock.ts @@ -12,7 +12,6 @@ export type ContextContainerMock = jest.Mocked; const createContextMock = (mockContext: any = {}) => { const contextMock: ContextContainerMock = { - // @ts-expect-error since ContextContainerMock cannot infer ContextName and fallsback to never registerContext: jest.fn(), createHandler: jest.fn(), }; diff --git a/src/core/server/context/context_service.ts b/src/core/server/context/context_service.ts index 9e77786c1562c..b2948a4d59129 100644 --- a/src/core/server/context/context_service.ts +++ b/src/core/server/context/context_service.ts @@ -12,7 +12,7 @@ import { CoreContext } from '../core_context'; type PrebootDeps = SetupDeps; -interface SetupDeps { +export interface SetupDeps { pluginDependencies: ReadonlyMap; } diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 894ff38e12a35..1b6aa2de3c192 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -81,7 +81,6 @@ const createAuthMock = () => { const createInternalPrebootContractMock = () => { const mock: InternalHttpServicePrebootMock = { registerRoutes: jest.fn(), - // @ts-expect-error tsc cannot infer ContextName and uses never registerRouteHandlerContext: jest.fn(), registerStaticDir: jest.fn(), basePath: createBasePathMock(), @@ -89,6 +88,15 @@ const createInternalPrebootContractMock = () => { externalUrl: ExternalUrlConfig.DEFAULT, auth: createAuthMock(), getServerInfo: jest.fn(), + server: { + name: 'http-preboot-server-test', + version: 'kibana', + route: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + config: jest.fn().mockReturnValue(configMock.create()), + // @ts-expect-error somehow it thinks that `Server` isn't a `Construtable` + } as unknown as jest.MockedClass, }; return mock; }; @@ -122,7 +130,6 @@ const createInternalSetupContractMock = () => { registerOnPreAuth: jest.fn(), registerAuth: jest.fn(), registerOnPostAuth: jest.fn(), - // @ts-expect-error tsc cannot infer ContextName and uses never registerRouteHandlerContext: jest.fn(), registerOnPreResponse: jest.fn(), createRouter: jest.fn().mockImplementation(() => mockRouter.create({})), @@ -134,6 +141,7 @@ const createInternalSetupContractMock = () => { getAuthHeaders: jest.fn(), getServerInfo: jest.fn(), registerPrebootRoutes: jest.fn(), + registerRouterAfterListening: jest.fn(), }; mock.createCookieSessionStorageFactory.mockResolvedValue(sessionStorageMock.createFactory()); mock.createRouter.mockImplementation(() => mockRouter.create()); @@ -160,7 +168,6 @@ const createSetupContractMock = () => { basePath: internalMock.basePath, csp: CspConfig.DEFAULT, createRouter: jest.fn(), - // @ts-expect-error tsc cannot infer ContextName and uses never registerRouteHandlerContext: jest.fn(), auth: { get: internalMock.auth.get, diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index 98ae0f8b81aa6..ca031e8ab282b 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -40,11 +40,11 @@ import { ExternalUrlConfig, } from '../external_url'; -interface PrebootDeps { +export interface PrebootDeps { context: InternalContextPreboot; } -interface SetupDeps { +export interface SetupDeps { context: ContextSetup; executionContext: InternalExecutionContextSetup; } diff --git a/src/core/server/http/router/headers.ts b/src/core/server/http/router/headers.ts index 715337ba813b5..89a32d6dc5d2f 100644 --- a/src/core/server/http/router/headers.ts +++ b/src/core/server/http/router/headers.ts @@ -9,6 +9,16 @@ import { IncomingHttpHeaders } from 'http'; import { pick } from '@kbn/std'; +/** + * Converts an object type to a new object type where each string + * key is copied to the values of the object, and non string keys are + * given a `never` value. This allows us to map over the values and + * get the list of all string keys on a type in `KnownKeys` + */ +type StringKeysAsVals = { + [K in keyof T]: string extends K ? never : number extends K ? never : K; +}; + /** * Creates a Union type of all known keys of a given interface. * @example @@ -21,11 +31,7 @@ import { pick } from '@kbn/std'; * type PersonKnownKeys = KnownKeys; // "age" | "name" * ``` */ -type KnownKeys = { - [K in keyof T]: string extends K ? never : number extends K ? never : K; -} extends { [_ in keyof T]: infer U } - ? U - : never; +type KnownKeys = StringKeysAsVals extends { [_ in keyof T]: infer U } ? U : never; /** * Set of well-known HTTP headers. diff --git a/src/core/server/i18n/i18n_service.ts b/src/core/server/i18n/i18n_service.ts index 02a809b3c2c36..5772703a5a6b8 100644 --- a/src/core/server/i18n/i18n_service.ts +++ b/src/core/server/i18n/i18n_service.ts @@ -16,12 +16,12 @@ import { getKibanaTranslationFiles } from './get_kibana_translation_files'; import { initTranslations } from './init_translations'; import { registerRoutes } from './routes'; -interface PrebootDeps { +export interface PrebootDeps { http: InternalHttpServicePreboot; pluginPaths: string[]; } -interface SetupDeps { +export interface SetupDeps { http: InternalHttpServiceSetup; pluginPaths: string[]; } diff --git a/src/core/server/logging/logging_service.ts b/src/core/server/logging/logging_service.ts index 6c3eee4981725..f5c6bbf8e9422 100644 --- a/src/core/server/logging/logging_service.ts +++ b/src/core/server/logging/logging_service.ts @@ -49,7 +49,7 @@ export interface InternalLoggingServicePreboot { /** @internal */ export type InternalLoggingServiceSetup = InternalLoggingServicePreboot; -interface PrebootDeps { +export interface PrebootDeps { loggingSystem: ILoggingSystem; } diff --git a/src/core/server/metrics/metrics_service.ts b/src/core/server/metrics/metrics_service.ts index 8e9604b50fb57..30609837447d1 100644 --- a/src/core/server/metrics/metrics_service.ts +++ b/src/core/server/metrics/metrics_service.ts @@ -17,7 +17,7 @@ import { OpsMetricsCollector } from './ops_metrics_collector'; import { opsConfig, OpsConfigType } from './ops_config'; import { getEcsOpsMetricsLog } from './logging'; -interface MetricsServiceSetupDeps { +export interface MetricsServiceSetupDeps { http: InternalHttpServiceSetup; } diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index fa461946d397f..d4393791a74fa 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -33,7 +33,6 @@ import type { KibanaClient } from '@elastic/elasticsearch/lib/api/kibana'; import { Logger } from '@kbn/logging'; import { LoggerFactory } from '@kbn/logging'; import { LogLevel as LogLevel_2 } from '@kbn/logging'; -import { LogLevelId } from '@kbn/logging'; import { LogMeta } from '@kbn/logging'; import { LogRecord } from '@kbn/logging'; import type { MaybePromise } from '@kbn/utility-types'; @@ -1372,14 +1371,32 @@ export type KibanaResponseFactory = typeof kibanaResponseFactory; export const kibanaResponseFactory: { custom: | Error | Buffer | Stream | { message: string | Error; - attributes?: Record | undefined; + attributes?: ResponseErrorAttributes | undefined; } | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; - badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse; - unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse; - forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse; - notFound: (options?: ErrorHttpResponseOptions) => KibanaResponse; - conflict: (options?: ErrorHttpResponseOptions) => KibanaResponse; - customError: (options: CustomHttpResponseOptions) => KibanaResponse; + badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse; + unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse; + forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse; + notFound: (options?: ErrorHttpResponseOptions) => KibanaResponse; + conflict: (options?: ErrorHttpResponseOptions) => KibanaResponse; + customError: (options: CustomHttpResponseOptions) => KibanaResponse; redirected: (options: RedirectResponseOptions) => KibanaResponse | Buffer | Stream>; ok: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; accepted: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index 29cc01da3f63d..63a1b02d5b2e7 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -32,7 +32,7 @@ interface StatusLogMeta extends LogMeta { kibana: { status: ServiceStatus }; } -interface SetupDeps { +export interface SetupDeps { elasticsearch: Pick; environment: InternalEnvironmentServiceSetup; pluginDependencies: ReadonlyMap; diff --git a/src/core/types/elasticsearch/search.ts b/src/core/types/elasticsearch/search.ts index 2e79bf6fea57c..c28bf3c258f77 100644 --- a/src/core/types/elasticsearch/search.ts +++ b/src/core/types/elasticsearch/search.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { ValuesType } from 'utility-types'; +import { ValuesType, UnionToIntersection } from 'utility-types'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; type InvalidAggregationRequest = unknown; @@ -21,11 +21,15 @@ type KeyOfSource = Record< (T extends Record ? null : never) | string | number >; -type KeysOfSources = T extends [infer U, ...infer V] - ? KeyOfSource & KeysOfSources - : T extends Array - ? KeyOfSource - : {}; +type KeysOfSources = T extends [any] + ? KeyOfSource + : T extends [any, any] + ? KeyOfSource & KeyOfSource + : T extends [any, any, any] + ? KeyOfSource & KeyOfSource & KeyOfSource + : T extends [any, any, any, any] + ? KeyOfSource & KeyOfSource & KeyOfSource & KeyOfSource + : Record; type CompositeKeysOf = TAggregationContainer extends { @@ -36,6 +40,13 @@ type CompositeKeysOf = + TAggregationContainer extends { top_metrics: { metrics: { field: infer TField } } } + ? TField + : TAggregationContainer extends { top_metrics: { metrics: Array<{ field: infer TField }> } } + ? TField + : string; + type ValueTypeOfField = T extends Record ? ValuesType : T extends Array @@ -532,12 +543,7 @@ export type AggregateOf< top_metrics: { top: Array<{ sort: number[] | string[]; - metrics: Record< - TAggregationContainer extends Record }> - ? TKeys - : string, - string | number | null - >; + metrics: Record, string | number | null>; }>; }; weighted_avg: { value: number | null }; @@ -547,8 +553,8 @@ export type AggregateOf< // t_test: {} not defined })[ValidAggregationKeysOf & AggregationTypeName]; -type AggregateOfMap = { - [TAggregationName in keyof TAggregationMap]: TAggregationMap[TAggregationName] extends estypes.AggregationsAggregationContainer +type AggregateOfMap = { + [TAggregationName in keyof TAggregationMap]: Required[TAggregationName] extends estypes.AggregationsAggregationContainer ? AggregateOf : never; // using never means we effectively ignore optional keys, using {} creates a union type of { ... } | {} }; @@ -567,7 +573,7 @@ type SearchResponseOf< > = SubAggregateOf; // if aggregation response cannot be inferred, fall back to unknown -type WrapAggregationResponse = keyof T extends never +type WrapAggregationResponse = keyof UnionToIntersection extends never ? { aggregations?: unknown } : { aggregations?: T }; diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index a5ed81c15c9b1..9e3a419d63964 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -8,7 +8,7 @@ import { ToolingLog } from '@kbn/dev-utils'; -import { Config, createRunner } from './lib'; +import { Config, createRunner, Task, GlobalTask } from './lib'; import * as Tasks from './tasks'; export interface BuildOptions { @@ -32,12 +32,12 @@ export interface BuildOptions { createExamplePlugins: boolean; } -export async function buildDistributables(log: ToolingLog, options: BuildOptions) { +export async function buildDistributables(log: ToolingLog, options: BuildOptions): Promise { log.verbose('building distributables with options:', options); - const config = await Config.create(options); + const config: Config = await Config.create(options); - const run = createRunner({ + const run: (task: Task | GlobalTask) => Promise = createRunner({ config, log, }); diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index f5c1755b228e9..e3d9688e60962 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -97,6 +97,7 @@ export const IGNORE_DIRECTORY_GLOBS = [ 'packages/kbn-pm/src/utils/__fixtures__/*', 'x-pack/dev-tools', 'packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack', + 'typings/*', ]; /** diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index 27998f881a03d..5888e6eaf6806 100644 --- a/src/dev/typescript/run_type_check_cli.ts +++ b/src/dev/typescript/run_type_check_cli.ts @@ -75,7 +75,7 @@ export async function runTypeCheckCli() { [ '--max-old-space-size=5120', require.resolve('typescript/bin/tsc'), - ...['--project', p.tsConfigPath, ...(flags.verbose ? ['--verbose'] : [])], + ...['--project', p.tsConfigPath], ...tscArgs, ], { diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index 0f65d3f1e33e2..01a7786c5d856 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -492,7 +492,9 @@ export class SenseEditor { return result.join('\n'); }; - updateActionsBar = () => this.coreEditor.legacyUpdateUI(this.currentReqRange); + updateActionsBar = () => { + return this.coreEditor.legacyUpdateUI(this.currentReqRange); + }; getCoreEditor() { return this.coreEditor; diff --git a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx index 09ac0c1dd94bb..8be3526e3ad29 100644 --- a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx +++ b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx @@ -17,7 +17,7 @@ import classNames from 'classnames'; import _ from 'lodash'; import React from 'react'; import { Subscription } from 'rxjs'; -import ReactGridLayout, { Layout } from 'react-grid-layout'; +import ReactGridLayout, { Layout, ReactGridLayoutProps } from 'react-grid-layout'; import { GridData } from '../../../../common'; import { ViewMode } from '../../../services/embeddable'; import { DASHBOARD_GRID_COLUMN_COUNT, DASHBOARD_GRID_HEIGHT } from '../dashboard_constants'; @@ -54,9 +54,9 @@ function ResponsiveGrid({ size: { width: number }; isViewMode: boolean; layout: Layout[]; - onLayoutChange: () => void; + onLayoutChange: ReactGridLayoutProps['onLayoutChange']; children: JSX.Element[]; - maximizedPanelId: string; + maximizedPanelId?: string; useMargins: boolean; }) { // This is to prevent a bug where view mode changes when the panel is expanded. View mode changes will trigger diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts index 180e826b5bc4e..9e968c9bae8a0 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts @@ -383,7 +383,7 @@ export class SearchInterceptor { private showTimeoutErrorMemoized = memoize( this.showTimeoutErrorToast, - (_: SearchTimeoutError, sessionId: string) => { + (_: SearchTimeoutError, sessionId?: string) => { return sessionId; } ); @@ -400,12 +400,7 @@ export class SearchInterceptor { ); }; - private showRestoreWarning = memoize( - this.showRestoreWarningToast, - (_: SearchTimeoutError, sessionId: string) => { - return sessionId; - } - ); + private showRestoreWarning = memoize(this.showRestoreWarningToast); /** * Show one error notification per session. diff --git a/src/plugins/data/public/ui/search_bar/search_bar.tsx b/src/plugins/data/public/ui/search_bar/search_bar.tsx index 9478d84741b7e..385f052adece6 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -25,7 +25,7 @@ import { FilterBar } from '../filter_bar/filter_bar'; import { SavedQueryMeta, SaveQueryForm } from '../saved_query_form'; import { SavedQueryManagementComponent } from '../saved_query_management'; -interface SearchBarInjectedDeps { +export interface SearchBarInjectedDeps { kibana: KibanaReactContextValue; intl: InjectedIntl; timeHistory: TimeHistoryContract; diff --git a/src/plugins/data/server/search/collectors/usage.ts b/src/plugins/data/server/search/collectors/usage.ts index f9d703900fc04..836ff33b335c4 100644 --- a/src/plugins/data/server/search/collectors/usage.ts +++ b/src/plugins/data/server/search/collectors/usage.ts @@ -62,15 +62,15 @@ export function usageProvider(core: CoreSetup): SearchUsage { { maxWait: 5000 } ); - const trackSuccess = (duration: number) => { + const trackSuccess = async (duration: number) => { collectedUsage.successCount++; collectedUsage.totalDuration += duration; - return updateSearchUsage(); + return await updateSearchUsage(); }; - const trackError = () => { + const trackError = async () => { collectedUsage.errorCount++; - return updateSearchUsage(); + return await updateSearchUsage(); }; return { trackSuccess, trackError }; diff --git a/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/number/number.tsx b/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/number/number.tsx index 4a86d8322b9ef..dfbd43f99142c 100644 --- a/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/number/number.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/number/number.tsx @@ -26,7 +26,7 @@ export class NumberFormatEditor extends DefaultFormatEditor; + declare context: React.ContextType; state = { ...defaultState, sampleInputs: [10000, 12.345678, -1, -999, 0.52], diff --git a/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx b/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx index 76c460de4ed42..36e61af6cea33 100644 --- a/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx +++ b/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx @@ -51,7 +51,7 @@ interface TestScriptState { export class TestScript extends Component { static contextType = contextType; - public readonly context!: IndexPatternManagmentContextValue; + public declare readonly context: IndexPatternManagmentContextValue; defaultProps = { name: 'myScriptedField', diff --git a/src/plugins/data_view_management/public/components/field_editor/field_editor.tsx b/src/plugins/data_view_management/public/components/field_editor/field_editor.tsx index ceaad76d6d124..5beb4fb989d5b 100644 --- a/src/plugins/data_view_management/public/components/field_editor/field_editor.tsx +++ b/src/plugins/data_view_management/public/components/field_editor/field_editor.tsx @@ -125,7 +125,7 @@ export interface FieldEdiorProps { export class FieldEditor extends PureComponent { static contextType = contextType; - public readonly context!: IndexPatternManagmentContextValue; + public declare readonly context: IndexPatternManagmentContextValue; supportedLangs: estypes.ScriptLanguage[] = []; deprecatedLangs: estypes.ScriptLanguage[] = []; diff --git a/src/plugins/discover/public/application/main/services/discover_search_session.ts b/src/plugins/discover/public/application/main/services/discover_search_session.ts index cace655a82a0f..c864c06e4003c 100644 --- a/src/plugins/discover/public/application/main/services/discover_search_session.ts +++ b/src/plugins/discover/public/application/main/services/discover_search_session.ts @@ -31,8 +31,10 @@ export class DiscoverSearchSessionManager { * skips if `searchSessionId` matches current search session id */ readonly newSearchSessionIdFromURL$: Rx.Observable; + private readonly deps: DiscoverSearchSessionManagerDeps; - constructor(private readonly deps: DiscoverSearchSessionManagerDeps) { + constructor(deps: DiscoverSearchSessionManagerDeps) { + this.deps = deps; this.newSearchSessionIdFromURL$ = createQueryParamObservable( this.deps.history, SEARCH_SESSION_ID_QUERY_PARAM diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index b86212251cb74..9cc2eb78aafbe 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -40,6 +40,10 @@ import { EmbeddableStart } from '../../embeddable/public'; import type { SpacesApi } from '../../../../x-pack/plugins/spaces/public'; +export interface HistoryLocationState { + referrer: string; +} + export interface DiscoverServices { addBasePath: (path: string) => string; capabilities: Capabilities; @@ -48,7 +52,7 @@ export interface DiscoverServices { data: DataPublicPluginStart; docLinks: DocLinksStart; embeddable: EmbeddableStart; - history: () => History; + history: () => History; theme: ChartsPluginStart['theme']; filterManager: FilterManager; fieldFormats: FieldFormatsStart; diff --git a/src/plugins/discover/public/kibana_services.ts b/src/plugins/discover/public/kibana_services.ts index 12b0a77a7865d..ffdfd82058693 100644 --- a/src/plugins/discover/public/kibana_services.ts +++ b/src/plugins/discover/public/kibana_services.ts @@ -10,7 +10,7 @@ import { once } from 'lodash'; import { createHashHistory } from 'history'; import type { ScopedHistory, AppMountParameters } from 'kibana/public'; import type { UiActionsStart } from 'src/plugins/ui_actions/public'; -import { DiscoverServices } from './build_services'; +import { DiscoverServices, HistoryLocationState } from './build_services'; import { createGetterSetter } from '../../kibana_utils/public'; import { DocViewsRegistry } from './services/doc_views/doc_views_registry'; @@ -46,7 +46,7 @@ export const [getDocViewsRegistry, setDocViewsRegistry] = * Makes sure discover and context are using one instance of history. */ export const getHistory = once(() => { - const history = createHashHistory(); + const history = createHashHistory(); history.listen(() => { // keep at least one listener so that `history.location` always in sync }); diff --git a/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx index 507d2be7198d5..9a575a9446a01 100644 --- a/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx +++ b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx @@ -94,7 +94,7 @@ export class AttributeService< ? await this.options.unwrapMethod(input.savedObjectId) : await this.defaultUnwrapMethod(input); } - return { attributes: input[ATTRIBUTE_SERVICE_KEY] }; + return { attributes: (input as ValType)[ATTRIBUTE_SERVICE_KEY] }; } public async wrapAttributes( @@ -141,7 +141,7 @@ export class AttributeService< getInputAsValueType = async (input: ValType | RefType): Promise => { if (!this.inputIsRefType(input)) { - return input; + return input as ValType; } const { attributes } = await this.unwrapAttributes(input); const { savedObjectId, ...originalInputToPropagate } = input; @@ -162,7 +162,7 @@ export class AttributeService< const onSave = async (props: OnSaveProps): Promise => { await this.options.checkForDuplicateTitle(props); try { - const newAttributes = { ...input[ATTRIBUTE_SERVICE_KEY] }; + const newAttributes = { ...(input as ValType)[ATTRIBUTE_SERVICE_KEY] }; newAttributes.title = props.newTitle; const wrappedInput = (await this.wrapAttributes(newAttributes, true)) as RefType; @@ -182,7 +182,11 @@ export class AttributeService< reject()} - title={get(saveOptions, 'saveModalTitle', input[ATTRIBUTE_SERVICE_KEY].title)} + title={get( + saveOptions, + 'saveModalTitle', + (input as ValType)[ATTRIBUTE_SERVICE_KEY].title + )} showCopyOnSave={false} objectType={this.type} showDescription={false} diff --git a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts index 26d366bb8dabe..fe471ef853b23 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts @@ -29,15 +29,17 @@ export const EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY = 'EMBEDDABLE_STATE_TRANSFER' export class EmbeddableStateTransfer { public isTransferInProgress: boolean; private storage: Storage; + private appList: ReadonlyMap | undefined; constructor( private navigateToApp: ApplicationStart['navigateToApp'], currentAppId$: ApplicationStart['currentAppId$'], - private appList?: ReadonlyMap | undefined, + appList?: ReadonlyMap | undefined, customStorage?: Storage ) { this.storage = customStorage ? customStorage : new Storage(sessionStorage); this.isTransferInProgress = false; + this.appList = appList; currentAppId$.subscribe(() => { this.isTransferInProgress = false; }); diff --git a/src/plugins/expressions/common/execution/execution_contract.ts b/src/plugins/expressions/common/execution/execution_contract.ts index 14ab7bebbf057..69587c58f1045 100644 --- a/src/plugins/expressions/common/execution/execution_contract.ts +++ b/src/plugins/expressions/common/execution/execution_contract.ts @@ -24,7 +24,11 @@ export class ExecutionContract) {} + protected readonly execution: Execution; + + constructor(execution: Execution) { + this.execution = execution; + } /** * Cancel the execution of the expression. This will set abort signal diff --git a/src/plugins/expressions/common/expression_types/expression_type.ts b/src/plugins/expressions/common/expression_types/expression_type.ts index d179beeb76860..bf8a96a4ab1e6 100644 --- a/src/plugins/expressions/common/expression_types/expression_type.ts +++ b/src/plugins/expressions/common/expression_types/expression_type.ts @@ -30,8 +30,9 @@ export class ExpressionType { */ serialize?: (value: Serializable) => unknown; deserialize?: (serialized: unknown[]) => Serializable; + private readonly definition: AnyExpressionTypeDefinition; - constructor(private readonly definition: AnyExpressionTypeDefinition) { + constructor(definition: AnyExpressionTypeDefinition) { const { name, help, deserialize, serialize, validate } = definition; this.name = name; @@ -43,6 +44,7 @@ export class ExpressionType { this.serialize = serialize; this.deserialize = deserialize; + this.definition = definition; } getToFn = ( diff --git a/src/plugins/field_formats/common/field_formats_registry.ts b/src/plugins/field_formats/common/field_formats_registry.ts index 3d856e4686126..19af3a1f976ab 100644 --- a/src/plugins/field_formats/common/field_formats_registry.ts +++ b/src/plugins/field_formats/common/field_formats_registry.ts @@ -177,7 +177,7 @@ export class FieldFormatsRegistry { return new ConcreteFieldFormat(params, this.getConfig); }, - (formatId: FieldFormatId, params: FieldFormatParams) => + (formatId: FieldFormatId, params?: FieldFormatParams) => JSON.stringify({ formatId, ...params, @@ -212,10 +212,10 @@ export class FieldFormatsRegistry { * https://lodash.com/docs#memoize * * @param {KBN_FIELD_TYPES} fieldType - * @param {ES_FIELD_TYPES[]} esTypes + * @param {ES_FIELD_TYPES[] | undefined} esTypes * @return {String} */ - getDefaultInstanceCacheResolver(fieldType: KBN_FIELD_TYPES, esTypes: ES_FIELD_TYPES[]): string { + getDefaultInstanceCacheResolver(fieldType: KBN_FIELD_TYPES, esTypes?: ES_FIELD_TYPES[]): string { // @ts-ignore return Array.isArray(esTypes) && esTypes.indexOf(fieldType) === -1 ? [fieldType, ...esTypes].join('-') diff --git a/src/plugins/input_control_vis/public/components/vis/input_control_vis.tsx b/src/plugins/input_control_vis/public/components/vis/input_control_vis.tsx index 956a6c55b61ba..faeae649021df 100644 --- a/src/plugins/input_control_vis/public/components/vis/input_control_vis.tsx +++ b/src/plugins/input_control_vis/public/components/vis/input_control_vis.tsx @@ -25,6 +25,10 @@ function isRangeControl(control: RangeControl | ListControl): control is RangeCo return control.type === CONTROL_TYPES.RANGE; } +interface UnknownControl { + type: string; +} + interface InputControlVisProps { stageFilter: (controlIndex: number, newValue: any) => void; submitFilters: () => void; @@ -90,7 +94,7 @@ export class InputControlVis extends Component { /> ); } else { - throw new Error(`Unhandled control type ${control!.type}`); + throw new Error(`Unhandled control type ${(control as UnknownControl)!.type}`); } return ( diff --git a/src/plugins/kibana_overview/public/components/add_data/add_data.tsx b/src/plugins/kibana_overview/public/components/add_data/add_data.tsx index 81121a17080c2..1be7cd133bca2 100644 --- a/src/plugins/kibana_overview/public/components/add_data/add_data.tsx +++ b/src/plugins/kibana_overview/public/components/add_data/add_data.tsx @@ -12,7 +12,10 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from ' import { FormattedMessage } from '@kbn/i18n-react'; import { CoreStart } from 'kibana/public'; import { RedirectAppLinks, useKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { FeatureCatalogueEntry } from '../../../../../../src/plugins/home/public'; +import { + FeatureCatalogueEntry, + FeatureCatalogueCategory, +} from '../../../../../../src/plugins/home/public'; // @ts-expect-error untyped component import { Synopsis } from '../synopsis'; import { METRIC_TYPE, trackUiMetric } from '../../lib/ui_metric'; @@ -94,8 +97,8 @@ AddData.propTypes = { icon: PropTypes.string.isRequired, path: PropTypes.string.isRequired, showOnHomePage: PropTypes.bool.isRequired, - category: PropTypes.string.isRequired, - order: PropTypes.number, - }) - ), + category: PropTypes.oneOf(Object.values(FeatureCatalogueCategory)).isRequired, + order: PropTypes.number as PropTypes.Validator, + }).isRequired + ).isRequired, }; diff --git a/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx b/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx index fa9e33a2ba82c..85f354d8a81cb 100644 --- a/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx +++ b/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx @@ -12,7 +12,10 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiSpacer, EuiTitle } fro import { FormattedMessage } from '@kbn/i18n-react'; import { CoreStart } from 'kibana/public'; import { RedirectAppLinks, useKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { FeatureCatalogueEntry } from '../../../../../../src/plugins/home/public'; +import { + FeatureCatalogueEntry, + FeatureCatalogueCategory, +} from '../../../../../../src/plugins/home/public'; // @ts-expect-error untyped component import { Synopsis } from '../synopsis'; import { METRIC_TYPE, trackUiMetric } from '../../lib/ui_metric'; @@ -81,8 +84,8 @@ ManageData.propTypes = { icon: PropTypes.string.isRequired, path: PropTypes.string.isRequired, showOnHomePage: PropTypes.bool.isRequired, - category: PropTypes.string.isRequired, - order: PropTypes.number, - }) - ), + category: PropTypes.oneOf(Object.values(FeatureCatalogueCategory)).isRequired, + order: PropTypes.number as PropTypes.Validator, + }).isRequired + ).isRequired, }; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts b/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts index 33d0d079d26f8..5a71355da8bbe 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts +++ b/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts @@ -26,7 +26,6 @@ import { export class ControlGroupContainerFactory implements EmbeddableFactoryDefinition { public readonly isContainerType = true; public readonly type = CONTROL_GROUP_TYPE; - public inject: EmbeddablePersistableStateService['inject']; public extract: EmbeddablePersistableStateService['extract']; diff --git a/src/plugins/visualize/public/application/utils/use/use_editor_updates.test.ts b/src/plugins/visualize/public/application/utils/use/use_editor_updates.test.ts index 9e1cfe60caa3c..0a1127cf11b3d 100644 --- a/src/plugins/visualize/public/application/utils/use/use_editor_updates.test.ts +++ b/src/plugins/visualize/public/application/utils/use/use_editor_updates.test.ts @@ -253,7 +253,7 @@ describe('useEditorUpdates', () => { describe('handle linked search changes', () => { test('should update saved search id in saved instance', () => { - // @ts-expect-error + // @ts-expect-error 4.3.5 upgrade savedVisInstance.savedSearch = { id: 'saved_search_id', }; @@ -287,7 +287,8 @@ describe('useEditorUpdates', () => { savedVisInstance.savedVis = { savedSearchId: 'saved_search_id', }; - // @ts-expect-error + + // @ts-expect-error 4.3.5 upgrade savedVisInstance.savedSearch = { id: 'saved_search_id', }; diff --git a/test/tsconfig.json b/test/tsconfig.json index 64c85dad73312..abf320a696714 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -5,7 +5,7 @@ "emitDeclarationOnly": true, "declaration": true, "declarationMap": true, - "types": ["node", "resize-observer-polyfill", "@emotion/react/types/css-prop"] + "types": ["node", "@emotion/react/types/css-prop"] }, "include": [ "**/*", diff --git a/tsconfig.base.json b/tsconfig.base.json index 18c0ad38f4601..ae06e39f7a274 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,6 +16,9 @@ "@emotion/core": [ "typings/@emotion" ], + "resize-observer-polyfill": [ + "typings/resize-observer-polyfill" + ] }, // Support .tsx files and transform JSX into calls to React.createElement "jsx": "react", @@ -68,7 +71,6 @@ "flot", "jest-styled-components", "@testing-library/jest-dom", - "resize-observer-polyfill", "@emotion/react/types/css-prop" ] } diff --git a/typings/resize-observer-polyfill/index.d.ts b/typings/resize-observer-polyfill/index.d.ts new file mode 100644 index 0000000000000..a2d9c88664722 --- /dev/null +++ b/typings/resize-observer-polyfill/index.d.ts @@ -0,0 +1,10 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// eslint-disable-next-line import/no-default-export +export default ResizeObserver; diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx index d1482c55429f4..91fb8552beed8 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx +++ b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx @@ -33,7 +33,9 @@ export interface Params { export class DashboardToDiscoverDrilldown implements Drilldown { - constructor(protected readonly params: Params) {} + constructor(protected readonly params: Params) { + this.ReactCollectConfig = (props) => ; + } public readonly id = SAMPLE_DASHBOARD_TO_DISCOVER_DRILLDOWN; @@ -47,9 +49,7 @@ export class DashboardToDiscoverDrilldown return [APPLY_FILTER_TRIGGER]; } - private readonly ReactCollectConfig: React.FC = (props) => ( - - ); + private readonly ReactCollectConfig!: React.FC; public readonly CollectConfig = reactToUiComponent(this.ReactCollectConfig); diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index e57859bfa253e..846847dcd7e05 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -22,14 +22,14 @@ "actions", "alerting", "cloud", + "fleet", "home", "maps", "ml", "security", "spaces", "taskManager", - "usageCollection", - "fleet" + "usageCollection" ], "server": true, "ui": true, diff --git a/x-pack/plugins/apm/public/application/uxApp.tsx b/x-pack/plugins/apm/public/application/uxApp.tsx index cfb1a5c354c2d..f7a8f7030d4f9 100644 --- a/x-pack/plugins/apm/public/application/uxApp.tsx +++ b/x-pack/plugins/apm/public/application/uxApp.tsx @@ -130,6 +130,7 @@ export function UXAppRoot({ services={{ ...core, ...plugins, embeddable, data }} > + {/* @ts-expect-error Type instantiation is excessively deep */} 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 155de8fbdd947..6ca632eac4f2e 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 @@ -46,7 +46,9 @@ function useServicesFetcher() { const { query: { rangeFrom, rangeTo, environment, kuery }, - } = useApmParams('/services/{serviceName}', '/services'); + } = + // @ts-ignore 4.3.5 upgrade - Type instantiation is excessively deep and possibly infinite. + useApmParams('/services/{serviceName}', '/services'); const { start, end } = useTimeRange({ rangeFrom, rangeTo }); diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx index b61b225c2fe94..a862ff872f61a 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx @@ -25,6 +25,7 @@ export function BackendContents({ start, end, }: ContentsProps) { + // @ts-ignore 4.3.5 upgrade - Type instantiation is excessively deep and possibly infinite. const { query } = useApmParams( '/service-map', '/services/{serviceName}/service-map' diff --git a/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx b/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx index 5d49f1d9a8f18..30ca3e79f6d7b 100644 --- a/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx @@ -138,6 +138,7 @@ function UnoptimizedManagedTable(props: Props) { }, [isLoading, noItemsMessage]); return ( + // @ts-expect-error TS thinks pagination should be non-nullable, but it's not { - // a little too much effort needed to satisfy TS here - // @ts-ignore + const link = (...args: [any]) => { return core.http.basePath.prepend('/app/apm' + router.link(...args)); }; diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts index e9280ba3e5976..68e29f7afcc79 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts @@ -11,9 +11,9 @@ import { } from '../../../../../../../../src/core/server/mocks'; import { createHttpServer } from 'src/core/server/test_utils'; import supertest from 'supertest'; -import { createApmEventClient } from '.'; +import { APMEventClient } from '.'; -describe('createApmEventClient', () => { +describe('APMEventClient', () => { let server: ReturnType; beforeEach(() => { @@ -38,7 +38,7 @@ describe('createApmEventClient', () => { router.get( { path: '/', validate: false }, async (context, request, res) => { - const eventClient = createApmEventClient({ + const eventClient = new APMEventClient({ esClient: { search: async ( params: any, diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts index 6c9a0cb45e273..6e09e2aecfd48 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts @@ -57,26 +57,13 @@ type TypeOfProcessorEvent = { profile: Profile; }[T]; -type ESSearchRequestOf = Omit< - TParams, - 'apm' -> & { index: string[] | string }; - type TypedSearchResponse = InferSearchResponseOf< TypeOfProcessorEvent>, - ESSearchRequestOf + TParams >; -export type APMEventClient = ReturnType; - -export function createApmEventClient({ - esClient, - debug, - request, - indices, - options: { includeFrozen } = { includeFrozen: false }, -}: { +export interface APMEventClientConfig { esClient: ElasticsearchClient; debug: boolean; request: KibanaRequest; @@ -84,102 +71,119 @@ export function createApmEventClient({ options: { includeFrozen: boolean; }; -}) { - return { - async search( - operationName: string, - params: TParams - ): Promise> { - const withProcessorEventFilter = unpackProcessorEvents(params, indices); - - const { includeLegacyData = false } = params.apm; - - const withPossibleLegacyDataFilter = !includeLegacyData - ? addFilterToExcludeLegacyData(withProcessorEventFilter) - : withProcessorEventFilter; - - const searchParams = { - ...withPossibleLegacyDataFilter, - ...(includeFrozen ? { ignore_throttled: false } : {}), - ignore_unavailable: true, - preference: 'any', - }; - - // only "search" operation is currently supported - const requestType = 'search'; - - return callAsyncWithDebug({ - cb: () => { - const searchPromise = withApmSpan(operationName, () => { - const controller = new AbortController(); - return cancelEsRequestOnAbort( - esClient.search(searchParams, { signal: controller.signal }), - request, - controller - ); - }); - - return unwrapEsResponse(searchPromise); - }, - getDebugMessage: () => ({ - body: getDebugBody({ - params: searchParams, - requestType, - operationName, - }), - title: getDebugTitle(request), +} + +export class APMEventClient { + private readonly esClient: ElasticsearchClient; + private readonly debug: boolean; + private readonly request: KibanaRequest; + private readonly indices: ApmIndicesConfig; + private readonly includeFrozen: boolean; + + constructor(config: APMEventClientConfig) { + this.esClient = config.esClient; + this.debug = config.debug; + this.request = config.request; + this.indices = config.indices; + this.includeFrozen = config.options.includeFrozen; + } + + async search( + operationName: string, + params: TParams + ): Promise> { + const withProcessorEventFilter = unpackProcessorEvents( + params, + this.indices + ); + + const { includeLegacyData = false } = params.apm; + + const withPossibleLegacyDataFilter = !includeLegacyData + ? addFilterToExcludeLegacyData(withProcessorEventFilter) + : withProcessorEventFilter; + + const searchParams = { + ...withPossibleLegacyDataFilter, + ...(this.includeFrozen ? { ignore_throttled: false } : {}), + ignore_unavailable: true, + preference: 'any', + }; + + // only "search" operation is currently supported + const requestType = 'search'; + + return callAsyncWithDebug({ + cb: () => { + const searchPromise = withApmSpan(operationName, () => { + const controller = new AbortController(); + return cancelEsRequestOnAbort( + this.esClient.search(searchParams, { signal: controller.signal }), + this.request, + controller + ); + }); + + return unwrapEsResponse(searchPromise); + }, + getDebugMessage: () => ({ + body: getDebugBody({ + params: searchParams, + requestType, + operationName, }), - isCalledWithInternalUser: false, - debug, - request, - requestType, - operationName, - requestParams: searchParams, - }); - }, - - async termsEnum( - operationName: string, - params: APMEventESTermsEnumRequest - ): Promise { - const requestType = 'terms_enum'; - const { index } = unpackProcessorEvents(params, indices); - - return callAsyncWithDebug({ - cb: () => { - const { apm, ...rest } = params; - const termsEnumPromise = withApmSpan(operationName, () => { - const controller = new AbortController(); - return cancelEsRequestOnAbort( - esClient.termsEnum( - { - index: Array.isArray(index) ? index.join(',') : index, - ...rest, - }, - { signal: controller.signal } - ), - request, - controller - ); - }); - - return unwrapEsResponse(termsEnumPromise); - }, - getDebugMessage: () => ({ - body: getDebugBody({ - params, - requestType, - operationName, - }), - title: getDebugTitle(request), + title: getDebugTitle(this.request), + }), + isCalledWithInternalUser: false, + debug: this.debug, + request: this.request, + requestType, + operationName, + requestParams: searchParams, + }); + } + + async termsEnum( + operationName: string, + params: APMEventESTermsEnumRequest + ): Promise { + const requestType = 'terms_enum'; + const { index } = unpackProcessorEvents(params, this.indices); + + return callAsyncWithDebug({ + cb: () => { + const { apm, ...rest } = params; + const termsEnumPromise = withApmSpan(operationName, () => { + const controller = new AbortController(); + return cancelEsRequestOnAbort( + this.esClient.termsEnum( + { + index: Array.isArray(index) ? index.join(',') : index, + ...rest, + }, + { signal: controller.signal } + ), + this.request, + controller + ); + }); + + return unwrapEsResponse(termsEnumPromise); + }, + getDebugMessage: () => ({ + body: getDebugBody({ + params, + requestType, + operationName, }), - isCalledWithInternalUser: false, - debug, - request, - requestType, - operationName, - requestParams: params, - }); - }, - }; + title: getDebugTitle(this.request), + }), + isCalledWithInternalUser: false, + debug: this.debug, + request: this.request, + requestType, + operationName, + requestParams: params, + }); + } } diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts index 85fb94dd765b0..fb2d46bfa5aa2 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts @@ -14,10 +14,7 @@ import { ApmIndicesConfig, getApmIndices, } from '../../routes/settings/apm_indices/get_apm_indices'; -import { - APMEventClient, - createApmEventClient, -} from './create_es_client/create_apm_event_client'; +import { APMEventClient } from './create_es_client/create_apm_event_client'; import { APMInternalClient, createInternalESClient, @@ -58,7 +55,7 @@ export async function setupRequest({ return { indices, - apmEventClient: createApmEventClient({ + apmEventClient: new APMEventClient({ esClient: context.core.elasticsearch.client.asCurrentUser, debug: query._inspect, request, diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts index 04cee211c78db..97dc298d11c56 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts @@ -8,8 +8,10 @@ import { merge } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { - TRANSACTION_TYPE, AGENT_NAME, + TRANSACTION_TYPE, + TRANSACTION_NAME, + SERVICE_NAME, } from '../../../common/elasticsearch_fieldnames'; import { arrayUnionToCallable } from '../../../common/utils/array_union_to_callable'; import { TransactionGroupRequestBase, TransactionGroupSetup } from './fetcher'; @@ -21,7 +23,7 @@ interface MetricParams { searchAggregatedTransactions: boolean; } -type BucketKey = Record; +type BucketKey = Record; function mergeRequestWithAggs< TRequestBase extends TransactionGroupRequestBase, diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index b0c21fe28abb2..b603d9e72a2b0 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -23,7 +23,7 @@ import { APM_FEATURE, registerFeaturesUsage } from './feature'; import { registerApmAlerts } from './routes/alerts/register_apm_alerts'; import { registerFleetPolicyCallbacks } from './routes/fleet/register_fleet_policy_callbacks'; import { createApmTelemetry } from './lib/apm_telemetry'; -import { createApmEventClient } from './lib/helpers/create_es_client/create_apm_event_client'; +import { APMEventClient } from './lib/helpers/create_es_client/create_apm_event_client'; import { getInternalSavedObjectsClient } from './lib/helpers/get_internal_saved_objects_client'; import { createApmAgentConfigurationIndex } from './routes/settings/agent_configuration/create_agent_config_index'; import { getApmIndices } from './routes/settings/apm_indices/get_apm_indices'; @@ -66,7 +66,7 @@ export class APMPlugin public setup( core: CoreSetup, - plugins: Omit + plugins: APMPluginSetupDependencies ) { this.logger = this.initContext.logger.get(); const config$ = this.initContext.config.create(); @@ -224,7 +224,7 @@ export class APMPlugin const esClient = context.core.elasticsearch.client.asCurrentUser; - return createApmEventClient({ + return new APMEventClient({ debug: debug ?? false, esClient, request, diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts index f196b718968da..773b1ce694125 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts @@ -171,27 +171,29 @@ export function registerTransactionErrorRateAlertType({ return {}; } - const results = response.aggregations.series.buckets - .map((bucket) => { - const [serviceName, environment, transactionType] = bucket.key; - - const failed = - bucket.outcomes.buckets.find( - (outcomeBucket) => outcomeBucket.key === EventOutcome.failure - )?.doc_count ?? 0; - const succesful = - bucket.outcomes.buckets.find( - (outcomeBucket) => outcomeBucket.key === EventOutcome.success - )?.doc_count ?? 0; - - return { + const results = []; + for (const bucket of response.aggregations.series.buckets) { + const [serviceName, environment, transactionType] = bucket.key; + + const failed = + bucket.outcomes.buckets.find( + (outcomeBucket) => outcomeBucket.key === EventOutcome.failure + )?.doc_count ?? 0; + const succesful = + bucket.outcomes.buckets.find( + (outcomeBucket) => outcomeBucket.key === EventOutcome.success + )?.doc_count ?? 0; + const errorRate = (failed / (failed + succesful)) * 100; + + if (errorRate >= alertParams.threshold) { + results.push({ serviceName, environment, transactionType, - errorRate: (failed / (failed + succesful)) * 100, - }; - }) - .filter((result) => result.errorRate >= alertParams.threshold); + errorRate, + }); + } + } results.forEach((result) => { const { serviceName, environment, transactionType, errorRate } = diff --git a/x-pack/plugins/apm/server/routes/data_view/route.ts b/x-pack/plugins/apm/server/routes/data_view/route.ts index 4e1c0ca050a09..83d2ac35b3672 100644 --- a/x-pack/plugins/apm/server/routes/data_view/route.ts +++ b/x-pack/plugins/apm/server/routes/data_view/route.ts @@ -22,12 +22,13 @@ const staticDataViewRoute = createApmServerRoute({ config, } = resources; - const [setup, savedObjectsClient] = await Promise.all([ - setupRequest(resources), - core - .start() - .then((coreStart) => coreStart.savedObjects.createInternalRepository()), - ]); + const setupPromise = setupRequest(resources); + const clientPromise = core + .start() + .then((coreStart) => coreStart.savedObjects.createInternalRepository()); + + const setup = await setupPromise; + const savedObjectsClient = await clientPromise; const spaceId = spaces?.setup.spacesService.getSpaceId(request); diff --git a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts index e460991029915..d32e751a6ca99 100644 --- a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts +++ b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts @@ -110,6 +110,7 @@ export async function getErrorGroupMainStatistics({ ); return ( + // @ts-ignore 4.3.5 upgrade - Expression produces a union type that is too complex to represent. ts(2590) response.aggregations?.error_groups.buckets.map((bucket) => ({ groupId: bucket.key as string, name: getErrorName(bucket.sample.hits.hits[0]._source), diff --git a/x-pack/plugins/apm/server/routes/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/routes/service_map/get_service_anomalies.ts index 66b12dab59775..0a0a92760decd 100644 --- a/x-pack/plugins/apm/server/routes/service_map/get_service_anomalies.ts +++ b/x-pack/plugins/apm/server/routes/service_map/get_service_anomalies.ts @@ -120,6 +120,7 @@ export async function getServiceAnomalies({ const relevantBuckets = uniqBy( sortBy( // make sure we only return data for jobs that are available in this space + // @ts-ignore 4.3.5 upgrade typedAnomalyResponse.aggregations?.services.buckets.filter((bucket) => jobIds.includes(bucket.key.jobId as string) ) ?? [], diff --git a/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts b/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts index ec081916f455d..a9f5615acb1c0 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts @@ -168,6 +168,7 @@ export async function getServiceInstancesTransactionStatistics< const { timeseries } = serviceNodeBucket; return { serviceNodeName, + // @ts-ignore 4.3.5 upgrade - Expression produces a union type that is too complex to represent. errorRate: timeseries.buckets.map((dateBucket) => ({ x: dateBucket.key, y: dateBucket.failures.doc_count / dateBucket.doc_count, diff --git a/x-pack/plugins/apm/server/routes/settings/apm_indices/get_apm_indices.ts b/x-pack/plugins/apm/server/routes/settings/apm_indices/get_apm_indices.ts index 6b917919bade5..347fca380c863 100644 --- a/x-pack/plugins/apm/server/routes/settings/apm_indices/get_apm_indices.ts +++ b/x-pack/plugins/apm/server/routes/settings/apm_indices/get_apm_indices.ts @@ -52,7 +52,7 @@ export async function getApmIndices({ }: { config: APMConfig; savedObjectsClient: ISavedObjectsClient; -}) { +}): Promise { try { const apmIndicesSavedObject = await getApmIndicesSavedObject( savedObjectsClient diff --git a/x-pack/plugins/apm/server/routes/traces/get_trace_items.ts b/x-pack/plugins/apm/server/routes/traces/get_trace_items.ts index 419c3e44d68a6..7a38988ac1b17 100644 --- a/x-pack/plugins/apm/server/routes/traces/get_trace_items.ts +++ b/x-pack/plugins/apm/server/routes/traces/get_trace_items.ts @@ -71,10 +71,8 @@ export async function getTraceItems( }, }); - const [errorResponse, traceResponse] = await Promise.all([ - errorResponsePromise, - traceResponsePromise, - ]); + const errorResponse = await errorResponsePromise; + const traceResponse = await traceResponsePromise; const exceedsMax = traceResponse.hits.total.value > maxTraceItems; const traceDocs = traceResponse.hits.hits.map((hit) => hit._source); diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 5b6d44ab9295d..a089c7bf3968a 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -17,7 +17,10 @@ import { AlertingApiRequestHandlerContext } from '../../../alerting/server'; import type { RacApiRequestHandlerContext } from '../../../rule_registry/server'; import { LicensingApiRequestHandlerContext } from '../../../licensing/server'; import { APMConfig } from '..'; -import { APMPluginDependencies } from '../types'; +import { + APMPluginSetupDependencies, + APMPluginStartDependencies, +} from '../types'; import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/server'; import { UxUIFilters } from '../../typings/ui_filters'; @@ -62,9 +65,9 @@ export interface APMRouteHandlerResources { start: () => Promise; }; plugins: { - [key in keyof APMPluginDependencies]: { - setup: Required[key]['setup']; - start: () => Promise[key]['start']>; + [key in keyof APMPluginSetupDependencies]: { + setup: Required[key]; + start: () => Promise[key]>; }; }; ruleDataClient: IRuleDataClient; diff --git a/x-pack/plugins/apm/server/types.ts b/x-pack/plugins/apm/server/types.ts index 02aae547407e5..0044a5abe8176 100644 --- a/x-pack/plugins/apm/server/types.ts +++ b/x-pack/plugins/apm/server/types.ts @@ -4,9 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { ValuesType } from 'utility-types'; + import { Observable } from 'rxjs'; -import { CoreSetup, CoreStart, KibanaRequest } from 'kibana/server'; +import { KibanaRequest } from 'kibana/server'; import { RuleRegistryPluginSetupContract, RuleRegistryPluginStartContract, @@ -47,130 +47,58 @@ import { FleetStartContract as FleetPluginStart, } from '../../fleet/server'; import { APMConfig } from '.'; -import { getApmIndices } from './routes/settings/apm_indices/get_apm_indices'; -import { createApmEventClient } from './lib/helpers/create_es_client/create_apm_event_client'; +import { ApmIndicesConfig } from './routes/settings/apm_indices/get_apm_indices'; +import { APMEventClient } from './lib/helpers/create_es_client/create_apm_event_client'; import { ApmPluginRequestHandlerContext } from './routes/typings'; export interface APMPluginSetup { config$: Observable; - getApmIndices: () => ReturnType; + getApmIndices: () => Promise; createApmEventClient: (params: { debug?: boolean; request: KibanaRequest; context: ApmPluginRequestHandlerContext; - }) => Promise>; -} - -interface DependencyMap { - core: { - setup: CoreSetup; - start: CoreStart; - }; - spaces: { - setup: SpacesPluginSetup; - start: SpacesPluginStart; - }; - home: { - setup: HomeServerPluginSetup; - start: HomeServerPluginStart; - }; - licensing: { - setup: LicensingPluginSetup; - start: LicensingPluginStart; - }; - cloud: { - setup: CloudSetup; - start: undefined; - }; - usageCollection: { - setup: UsageCollectionSetup; - start: undefined; - }; - taskManager: { - setup: TaskManagerSetupContract; - start: TaskManagerStartContract; - }; - alerting: { - setup: AlertingPlugin['setup']; - start: AlertingPlugin['start']; - }; - actions: { - setup: ActionsPlugin['setup']; - start: ActionsPlugin['start']; - }; - observability: { - setup: ObservabilityPluginSetup; - start: undefined; - }; - features: { - setup: FeaturesPluginSetup; - start: FeaturesPluginStart; - }; - security: { - setup: SecurityPluginSetup; - start: SecurityPluginStart; - }; - ml: { - setup: MlPluginSetup; - start: MlPluginStart; - }; - data: { - setup: DataPluginSetup; - start: DataPluginStart; - }; - ruleRegistry: { - setup: RuleRegistryPluginSetupContract; - start: RuleRegistryPluginStartContract; - }; - fleet: { - setup: FleetPluginSetup; - start: FleetPluginStart; - }; + }) => Promise; } -const requiredDependencies = [ - 'features', - 'data', - 'licensing', - 'triggersActionsUi', - 'embeddable', - 'infra', - 'observability', - 'ruleRegistry', -] as const; - -const optionalDependencies = [ - 'spaces', - 'cloud', - 'usageCollection', - 'taskManager', - 'actions', - 'alerting', - 'security', - 'ml', - 'home', - 'maps', - 'fleet', -] as const; +export interface APMPluginSetupDependencies { + // required dependencies + data: DataPluginSetup; + features: FeaturesPluginSetup; + licensing: LicensingPluginSetup; + observability: ObservabilityPluginSetup; + ruleRegistry: RuleRegistryPluginSetupContract; -type RequiredDependencies = Pick< - DependencyMap, - ValuesType & keyof DependencyMap ->; - -type OptionalDependencies = Partial< - Pick< - DependencyMap, - ValuesType & keyof DependencyMap - > ->; - -export type APMPluginDependencies = RequiredDependencies & OptionalDependencies; + // optional dependencies + actions?: ActionsPlugin['setup']; + alerting?: AlertingPlugin['setup']; + cloud?: CloudSetup; + fleet?: FleetPluginSetup; + home?: HomeServerPluginSetup; + ml?: MlPluginSetup; + security?: SecurityPluginSetup; + spaces?: SpacesPluginSetup; + taskManager?: TaskManagerSetupContract; + usageCollection?: UsageCollectionSetup; +} -export type APMPluginSetupDependencies = { - [key in keyof APMPluginDependencies]: Required[key]['setup']; -}; +export interface APMPluginStartDependencies { + // required dependencies + data: DataPluginStart; + features: FeaturesPluginStart; + licensing: LicensingPluginStart; + observability: undefined; + ruleRegistry: RuleRegistryPluginStartContract; -export type APMPluginStartDependencies = { - [key in keyof APMPluginDependencies]: Required[key]['start']; -}; + // optional dependencies + actions?: ActionsPlugin['start']; + alerting?: AlertingPlugin['start']; + cloud?: undefined; + fleet?: FleetPluginStart; + home?: HomeServerPluginStart; + ml?: MlPluginStart; + security?: SecurityPluginStart; + spaces?: SpacesPluginStart; + taskManager?: TaskManagerStartContract; + usageCollection?: undefined; +} diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/abstract_dashboard_drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/abstract_dashboard_drilldown.tsx index 96a17bd894391..daec52bccda37 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/abstract_dashboard_drilldown.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/abstract_dashboard_drilldown.tsx @@ -9,6 +9,7 @@ import type { KibanaLocation } from 'src/plugins/share/public'; import React from 'react'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { DashboardStart } from 'src/plugins/dashboard/public'; +import { DrilldownConfig } from '../../../../common/drilldowns/dashboard_drilldown/types'; import { reactToUiComponent } from '../../../../../../../src/plugins/kibana_react/public'; import { CollectConfigContainer } from './components'; import { @@ -20,6 +21,7 @@ import { txtGoToDashboard } from './i18n'; import { CollectConfigProps, StartServicesGetter, + UiComponent, } from '../../../../../../../src/plugins/kibana_utils/public'; import { Config } from './types'; @@ -34,7 +36,10 @@ export interface Params { export abstract class AbstractDashboardDrilldown implements Drilldown { - constructor(protected readonly params: Params) {} + constructor(protected readonly params: Params) { + this.ReactCollectConfig = (props) => ; + this.CollectConfig = reactToUiComponent(this.ReactCollectConfig); + } public abstract readonly id: string; @@ -50,9 +55,11 @@ export abstract class AbstractDashboardDrilldown - > = (props) => ; + >; - public readonly CollectConfig = reactToUiComponent(this.ReactCollectConfig); + public readonly CollectConfig: UiComponent< + CollectConfigProps + >; public readonly createConfig = () => ({ dashboardId: '', diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.test.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.test.ts index 586f636a088e1..60be59ab2db6c 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.test.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.test.ts @@ -75,7 +75,7 @@ const kqlSavedSearch: SavedSearch = { title: 'farequote_filter_and_kuery', description: '', columns: ['_source'], - // @ts-expect-error + // @ts-expect-error this isn't a valid SavedSearch object... but does anyone care? kibanaSavedObjectMeta: { searchSourceJSON: '{"highlightAll":true,"version":true,"query":{"query":"responsetime > 49","language":"kuery"},"filter":[{"meta":{"index":"90a978e0-1c80-11ec-b1d7-f7e5cf21b9e0","negate":false,"disabled":false,"alias":null,"type":"phrase","key":"airline","value":"ASA","params":{"query":"ASA","type":"phrase"}},"query":{"match":{"airline":{"query":"ASA","type":"phrase"}}},"$state":{"store":"appState"}}],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_details_flyout/accordion_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_details_flyout/accordion_list.test.tsx index 2219a50a5e7f3..2109160f5bb25 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_details_flyout/accordion_list.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_details_flyout/accordion_list.test.tsx @@ -48,7 +48,9 @@ describe('AccordionList', () => { expect(table.prop('items')).toEqual([{ item: 'first item' }, { item: 'second item' }]); - expect(table.prop('columns')[0].render({ item: 'first item' })).toEqual('first item'); + // columns from accordion_list.tsx always have a render function, so avoid type errors and just convert to any + const column: any = table.prop('columns')[0]; + expect(column.render({ item: 'first item' })).toEqual('first item'); }); it('is disabled when there are no items', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_requests_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_requests_table.test.tsx index e11661e4a942e..03e5d835df9b3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_requests_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_requests_table.test.tsx @@ -83,6 +83,7 @@ describe('CrawlRequestsTable', () => { const table = wrapper.find(EuiBasicTable); const columns = table.prop('columns'); + // @ts-expect-error 4.3.5 upgrade const crawlID = shallow(columns[0].render('618d0e66abe97bc688328900', { stage: 'crawl' })); expect(crawlID.text()).toContain('618d0e66abe97bc688328900'); @@ -90,6 +91,7 @@ describe('CrawlRequestsTable', () => { expect(actions.fetchCrawlRequest).toHaveBeenCalledWith('618d0e66abe97bc688328900'); expect(actions.openFlyout).toHaveBeenCalled(); + // @ts-expect-error 4.3.5 upgrade const processCrawlID = shallow(columns[0].render('54325423aef7890543', { stage: 'process' })); expect(processCrawlID.text()).toContain('54325423aef7890543'); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_requests_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_requests_table.tsx index 0949be0ced0a6..70dd55f40ad4b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_requests_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_requests_table.tsx @@ -12,7 +12,8 @@ import { useActions, useValues } from 'kea'; import { EuiBadge, EuiBasicTable, - EuiBasicTableColumn, + EuiTableFieldDataColumnType, + EuiTableComputedColumnType, EuiEmptyPrompt, EuiLink, } from '@elastic/eui'; @@ -30,7 +31,9 @@ export const CrawlRequestsTable: React.FC = () => { const { events } = useValues(CrawlerLogic); const { fetchCrawlRequest, openFlyout } = useActions(CrawlDetailLogic); - const columns: Array> = [ + const columns: Array< + EuiTableFieldDataColumnType | EuiTableComputedColumnType + > = [ { field: 'id', name: i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx index 13dd77da40931..32e1b61a2c3e6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx @@ -81,8 +81,8 @@ describe('CredentialsList', () => { ], }); const wrapper = shallow(); - const { items } = wrapper.find(EuiBasicTable).props(); - expect(items.map((i: ApiToken) => i.id)).toEqual([undefined, 1, 2]); + const items = wrapper.find(EuiBasicTable).props().items as ApiToken[]; + expect(items.map((i) => i.id)).toEqual([undefined, 1, 2]); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/ignored_queries_panel/ignored_queries_panel.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/ignored_queries_panel/ignored_queries_panel.test.tsx index 919e1e8706c94..8c1b4212040aa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/ignored_queries_panel/ignored_queries_panel.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/ignored_queries_panel/ignored_queries_panel.test.tsx @@ -66,6 +66,7 @@ describe('IgnoredQueriesPanel', () => { }); it('show a query', () => { + // @ts-expect-error 4.3.5 upgrade const column = getColumn(0).render('test query'); expect(column).toEqual('test query'); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/error_pages/components/no_data_layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/error_pages/components/no_data_layout.tsx index 8d867eeb74f86..04b899e3cb886 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/error_pages/components/no_data_layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/error_pages/components/no_data_layout.tsx @@ -18,7 +18,7 @@ interface LayoutProps { export const NoDataLayout: React.FunctionComponent = withRouter< any, React.FunctionComponent ->(({ actionSection, title, modalClosePath, children }) => { +>(({ actionSection, title, modalClosePath, children }: React.PropsWithChildren) => { return ( diff --git a/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx b/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx index d582572862012..be905cd67c976 100644 --- a/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx +++ b/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx @@ -108,7 +108,7 @@ describe('field_manager', () => { ).toEqual(['field1', 'field2', 'field3']); act(() => { - getInstance().find(FieldPicker).dive().find(EuiSelectable).prop('onChange')([ + getInstance().find(FieldPicker).dive().find(EuiSelectable).prop('onChange')!([ { checked: 'on', label: 'field3' }, ]); }); diff --git a/x-pack/plugins/graph/public/state_management/fields.ts b/x-pack/plugins/graph/public/state_management/fields.ts index 34b1a74510e1a..3e91f4c1fb58f 100644 --- a/x-pack/plugins/graph/public/state_management/fields.ts +++ b/x-pack/plugins/graph/public/state_management/fields.ts @@ -84,8 +84,8 @@ export const updateSaveButtonSaga = ({ notifyReact }: GraphStoreDependencies) => * * Won't be necessary once the workspace is moved to redux */ -export const syncFieldsSaga = ({ getWorkspace }: GraphStoreDependencies) => { - function* syncFields() { +export const syncFieldsSaga = ({ getWorkspace }: GraphStoreDependencies): (() => Generator) => { + function* syncFields(): Generator { const workspace = getWorkspace(); if (!workspace) { return; diff --git a/x-pack/plugins/graph/public/state_management/persistence.ts b/x-pack/plugins/graph/public/state_management/persistence.ts index e3f106488571e..27a635d25eeaf 100644 --- a/x-pack/plugins/graph/public/state_management/persistence.ts +++ b/x-pack/plugins/graph/public/state_management/persistence.ts @@ -25,6 +25,7 @@ import { updateMetaData, metaDataSelector } from './meta_data'; import { openSaveModal, SaveWorkspaceHandler } from '../services/save_modal'; import { getEditPath } from '../services/url'; import { saveSavedWorkspace } from '../helpers/saved_workspace_utils'; +import type { IndexPattern } from '../../../../../src/plugins/data/public'; export interface LoadSavedWorkspacePayload { indexPatterns: IndexPatternSavedObject[]; @@ -49,7 +50,7 @@ export const loadingSaga = ({ notifications, indexPatternProvider, }: GraphStoreDependencies) => { - function* deserializeWorkspace(action: Action) { + function* deserializeWorkspace(action: Action): Generator { const { indexPatterns, savedWorkspace, urlQuery } = action.payload; const migrationStatus = migrateLegacyIndexPatternRef(savedWorkspace, indexPatterns); if (!migrationStatus.success) { @@ -65,8 +66,11 @@ export const loadingSaga = ({ } const selectedIndexPatternId = lookupIndexPatternId(savedWorkspace); - const indexPattern = yield call(indexPatternProvider.get, selectedIndexPatternId); - const initialSettings = settingsSelector(yield select()); + const indexPattern = (yield call( + indexPatternProvider.get, + selectedIndexPatternId + )) as IndexPattern; + const initialSettings = settingsSelector((yield select()) as GraphState); const createdWorkspace = createWorkspace(indexPattern.title, initialSettings); @@ -87,7 +91,7 @@ export const loadingSaga = ({ yield put( setDatasource({ type: 'indexpattern', - id: indexPattern.id, + id: indexPattern.id!, title: indexPattern.title, }) ); @@ -122,13 +126,13 @@ export const savingSaga = (deps: GraphStoreDependencies) => { return; } - const savedObjectId = yield cps(showModal, { + const savedObjectId = (yield cps(showModal, { deps, workspace, savedWorkspace, state, selectedDatasource, - }); + })) as string; if (savedObjectId) { yield put(updateMetaData({ savedObjectId })); } diff --git a/x-pack/plugins/graph/public/state_management/store.ts b/x-pack/plugins/graph/public/state_management/store.ts index ba9bff98b0ca9..b2b685f51573b 100644 --- a/x-pack/plugins/graph/public/state_management/store.ts +++ b/x-pack/plugins/graph/public/state_management/store.ts @@ -83,7 +83,7 @@ function registerSagas(sagaMiddleware: SagaMiddleware, deps: GraphStoreD sagaMiddleware.run(submitSearchSaga(deps)); } -export const createGraphStore = (deps: GraphStoreDependencies) => { +export const createGraphStore = (deps: GraphStoreDependencies): Store => { const sagaMiddleware = createSagaMiddleware(); const rootReducer = createRootReducer(deps.addBasePath); diff --git a/x-pack/plugins/graph/public/state_management/workspace.ts b/x-pack/plugins/graph/public/state_management/workspace.ts index 9e8cca488e4ef..48a428fd08b4c 100644 --- a/x-pack/plugins/graph/public/state_management/workspace.ts +++ b/x-pack/plugins/graph/public/state_management/workspace.ts @@ -16,6 +16,7 @@ import { datasourceSelector } from './datasource'; import { liveResponseFieldsSelector, selectedFieldsSelector } from './fields'; import { fetchTopNodes } from '../services/fetch_top_nodes'; import { Workspace } from '../types'; +import type { ServerResultNode } from '../types'; const actionCreator = actionCreatorFactory('x-pack/graph/workspace'); @@ -52,21 +53,26 @@ export const fillWorkspaceSaga = ({ http, notifications, }: GraphStoreDependencies) => { - function* fetchNodes() { + function* fetchNodes(): Generator { try { const workspace = getWorkspace(); if (!workspace) { return; } - const state: GraphState = yield select(); + const state = (yield select()) as GraphState; const fields = selectedFieldsSelector(state); const datasource = datasourceSelector(state).current; if (datasource.type === 'none') { return; } - const topTermNodes = yield call(fetchTopNodes, http.post, datasource.title, fields); + const topTermNodes = (yield call( + fetchTopNodes, + http.post, + datasource.title, + fields + )) as ServerResultNode[]; workspace.mergeGraph({ nodes: topTermNodes, edges: [], diff --git a/x-pack/plugins/graph/public/types/app_state.ts b/x-pack/plugins/graph/public/types/app_state.ts index c2cbe16b62fb6..1ec21c4991a1b 100644 --- a/x-pack/plugins/graph/public/types/app_state.ts +++ b/x-pack/plugins/graph/public/types/app_state.ts @@ -8,7 +8,7 @@ import { SimpleSavedObject } from 'src/core/public'; import { FontawesomeIcon } from '../helpers/style_choices'; import { OutlinkEncoder } from '../helpers/outlink_encoders'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import type { IndexPattern } from '../../../../../src/plugins/data/public'; export interface UrlTemplate { url: string; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts index 2033ad53bf7b0..a650176f5f77c 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts @@ -592,7 +592,5 @@ export const reducer = (state: State, action: Action): State => { isValid: action.value, }; } - default: - throw new Error(`Action "${action!.type}" not recognized.`); } }; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx index 3dd092fa48b99..485a104b3ff9d 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx @@ -177,7 +177,7 @@ export const Criterion: React.FC = ({ value={criterion.field ?? 'a chosen field'} isActive={isFieldPopoverOpen} color={errors.field.length === 0 ? 'success' : 'danger'} - onClick={(e) => { + onClick={(e: React.MouseEvent) => { e.stopPropagation(); setIsFieldPopoverOpen(!isFieldPopoverOpen); }} @@ -185,7 +185,7 @@ export const Criterion: React.FC = ({ } isOpen={isFieldPopoverOpen} closePopover={() => setIsFieldPopoverOpen(false)} - onClick={(e) => e.stopPropagation()} + onClick={(e: React.MouseEvent) => e.stopPropagation()} ownFocus panelPaddingSize="s" anchorPosition="downLeft" @@ -230,7 +230,7 @@ export const Criterion: React.FC = ({ ? 'success' : 'danger' } - onClick={(e) => { + onClick={(e: React.MouseEvent) => { e.stopPropagation(); setIsComparatorPopoverOpen(!isComparatorPopoverOpen); }} diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 286fc5f769cad..316249ec0fe3c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -43,7 +43,7 @@ const TIMESTAMP_FORMAT = { hour: 'numeric', minute: 'numeric', second: 'numeric', -}; +} as const; export class LogTextStreamLoadingItemView extends React.PureComponent< LogTextStreamLoadingItemViewProps, diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx index 5b437d3da69ab..097ec3d98e162 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useEffect, useMemo } from 'react'; -import { RouteComponentProps } from 'react-router-dom'; +import { RouteComponentProps, useHistory } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiPageHeader, EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; @@ -22,10 +22,14 @@ interface Props { sourcePipeline?: Pipeline; } +interface LocationState { + sourcePipeline?: Pipeline; +} + export const PipelinesCreate: React.FunctionComponent = ({ - history, sourcePipeline, }) => { + const history = useHistory(); const { services } = useKibana(); const [isSaving, setIsSaving] = useState(false); @@ -61,7 +65,7 @@ export const PipelinesCreate: React.FunctionComponent, getState: () => MapStoreState ) => { - const targetLayer = getLayerById(layerId, getState()); - if (!targetLayer || !('getFields' in targetLayer)) { + const targetLayer: ILayer | undefined = getLayerById(layerId, getState()); + if (!targetLayer) { return; } @@ -555,6 +555,10 @@ function updateStyleProperties(layerId: string, previousFields: IField[]) { return; } + if (!('getFields' in targetLayer)) { + return; + } + const nextFields = await (targetLayer as IVectorLayer).getFields(); // take into account all fields, since labels can be driven by any field (source or join) const { hasChanges, nextStyleDescriptor } = await ( style as IVectorStyle diff --git a/x-pack/plugins/maps/public/components/tooltip_selector/tooltip_selector.tsx b/x-pack/plugins/maps/public/components/tooltip_selector/tooltip_selector.tsx index bd178047b2251..ff00ef958acdf 100644 --- a/x-pack/plugins/maps/public/components/tooltip_selector/tooltip_selector.tsx +++ b/x-pack/plugins/maps/public/components/tooltip_selector/tooltip_selector.tsx @@ -116,6 +116,8 @@ export class TooltipSelector extends Component { const prop: FieldProps | undefined = this.state.fieldProps.find((field: FieldProps) => { return field.name === propertyName; }); + + // @ts-expect-error 4.3.5 upgrade return prop ? prop!.label : propertyName; }; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_geometry_filter_form.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_geometry_filter_form.tsx index d5ed944f7e7c8..e42a5cc873c1c 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_geometry_filter_form.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_geometry_filter_form.tsx @@ -98,7 +98,9 @@ export class FeatureGeometryFilterForm extends Component { // Ensure filter will not overflow URL. Filters that contain geometry can be extremely large. // No elasticsearch support for pre-indexed shapes and geo_point spatial queries. if ( - window.location.href.length + rison.encode(filter as RisonObject).length + META_OVERHEAD > + window.location.href.length + + rison.encode(filter as unknown as RisonObject).length + + META_OVERHEAD > URL_MAX_LENGTH ) { this.setState({ diff --git a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts index 85746298d4e51..ac5602ddd830f 100644 --- a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts +++ b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts @@ -491,8 +491,7 @@ export function alertingServiceProvider( .filter((v) => v.doc_count > 0 && v[resultsLabel.aggGroupLabel].doc_count > 0) // Map response .map(formatter) - : // @ts-expect-error - [formatter(result as AggResultsResponse)] + : [formatter(result as unknown as AggResultsResponse)] ).filter(isDefined); }; diff --git a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.test.ts b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.test.ts index e853d5de5899d..d4c3648d05325 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.test.ts +++ b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.test.ts @@ -35,7 +35,7 @@ describe('ML - data recognizer', () => { } as unknown as SavedObjectsClientContract, { find: jest.fn() } as unknown as DataViewsService, {} as JobSavedObjectService, - { headers: { authorization: '' } } as KibanaRequest + { headers: { authorization: '' } } as unknown as KibanaRequest ); describe('jobOverrides', () => { diff --git a/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx b/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx index be8831860c9ec..1afd36e406daf 100644 --- a/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx +++ b/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx @@ -16,10 +16,14 @@ import { useRouterNavigate } from '../../../common/lib/kibana'; import { LiveQuery } from '../../../live_queries'; import { useBreadcrumbs } from '../../../common/hooks/use_breadcrumbs'; +interface LocationState { + form: Record; +} + const NewLiveQueryPageComponent = () => { useBreadcrumbs('live_query_new'); const { replace } = useHistory(); - const location = useLocation(); + const location = useLocation(); const liveQueryListProps = useRouterNavigate('live_queries'); const [initialFormData, setInitialFormData] = useState | undefined>({}); diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index c44427f3ca9e1..178c4e5bc9fde 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -69,11 +69,15 @@ interface IReportingAPI { } export class ReportingAPIClient implements IReportingAPI { + private http: HttpSetup; + constructor( - private http: HttpSetup, + http: HttpSetup, private uiSettings: IUiSettingsClient, private kibanaVersion: string - ) {} + ) { + this.http = http; + } public getKibanaAppHref(job: Job): string { const searchParams = stringify({ jobId: job.id }); diff --git a/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.mock.ts b/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.mock.ts index 323adcc756674..63a159121e009 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.mock.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.mock.ts @@ -30,6 +30,7 @@ export const createRuleDataClientMock = ( kibanaVersion: '7.16.0', isWriteEnabled: jest.fn(() => true), + // @ts-ignore 4.3.5 upgrade getReader: jest.fn((_options?: { namespace?: string }) => ({ search, getDynamicIndexPattern, diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts index c65fdece6c5f0..fcddcab378bc6 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts @@ -31,6 +31,7 @@ describe('createLifecycleExecutor', () => { it('wraps and unwraps the original executor state', async () => { const logger = loggerMock.create(); const ruleDataClientMock = createRuleDataClientMock(); + // @ts-ignore 4.3.5 upgrade - Expression produces a union type that is too complex to represent.ts(2590) const executor = createLifecycleExecutor( logger, ruleDataClientMock diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts index 3fa567b8aca96..2284ad5e796ee 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts @@ -276,6 +276,7 @@ describe('createLifecycleRuleTypeFactory', () => { return castArray(val); }); + // @ts-ignore 4.3.5 upgrade helpers.ruleDataClientMock.getReader().search.mockResolvedValueOnce({ hits: { hits: [{ fields: stored } as any], diff --git a/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx b/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx index b7c52361a33a1..722f9a3f17f6d 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx @@ -159,7 +159,7 @@ export const UserForm: FunctionComponent = ({ } else { try { const users = await getUsersThrottled(); - if (users.some((user) => user.username === values.username)) { + if (users?.some((user) => user.username === values.username)) { errors.username = i18n.translate( 'xpack.security.management.users.userForm.usernameTakenError', { diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index 6e99db7e1ed0b..4b04f15682777 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -124,7 +124,7 @@ const EntriesSchema = schema.conditional( ) ); -const getTrustedAppForOsScheme = (forUpdateFlow: boolean = false) => +const getTrustedAppForOsScheme = () => schema.object({ name: schema.string({ minLength: 1, maxLength: 256 }), description: schema.maybe(schema.string({ minLength: 0, maxLength: 256, defaultValue: '' })), @@ -143,7 +143,7 @@ const getTrustedAppForOsScheme = (forUpdateFlow: boolean = false) => }), ]), entries: EntriesSchema, - ...(forUpdateFlow ? { version: schema.maybe(schema.string()) } : {}), + version: schema.maybe(schema.string()), }); export const PostTrustedAppCreateRequestSchema = { @@ -154,5 +154,5 @@ export const PutTrustedAppUpdateRequestSchema = { params: schema.object({ id: schema.string(), }), - body: getTrustedAppForOsScheme(true), + body: getTrustedAppForOsScheme(), }; diff --git a/x-pack/plugins/security_solution/cypress/tsconfig.json b/x-pack/plugins/security_solution/cypress/tsconfig.json index 6fdc868429138..55ba3de538060 100644 --- a/x-pack/plugins/security_solution/cypress/tsconfig.json +++ b/x-pack/plugins/security_solution/cypress/tsconfig.json @@ -15,7 +15,6 @@ "cypress-file-upload", "cypress-pipe", "node", - "resize-observer-polyfill", ], }, "references": [ diff --git a/x-pack/plugins/security_solution/public/common/mock/utils.ts b/x-pack/plugins/security_solution/public/common/mock/utils.ts index 0bafdc4fad1e8..419bd2d9e7ee1 100644 --- a/x-pack/plugins/security_solution/public/common/mock/utils.ts +++ b/x-pack/plugins/security_solution/public/common/mock/utils.ts @@ -62,5 +62,5 @@ export const SUB_PLUGINS_REDUCER: SubPluginsInitReducer = { * These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture, * they are cast to mutable versions here. */ - management: managementReducer as ManagementPluginReducer['management'], + management: managementReducer as unknown as ManagementPluginReducer['management'], }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index d8c17f064d016..e5381757670ea 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -35,7 +35,8 @@ import { import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; describe('rule helpers', () => { - // @ts-expect-error + // @ts-expect-error 4.3.5 upgrade - likely requires moment upgrade + // https://github.com/elastic/kibana/issues/120236 moment.suppressDeprecationWarnings = true; describe('getStepsData', () => { test('returns object with about, define, schedule and actions step properties formatted', () => { diff --git a/x-pack/plugins/security_solution/public/management/index.ts b/x-pack/plugins/security_solution/public/management/index.ts index 3e2c8b0ca2ec8..4ca8adee5f9a8 100644 --- a/x-pack/plugins/security_solution/public/management/index.ts +++ b/x-pack/plugins/security_solution/public/management/index.ts @@ -53,7 +53,7 @@ export class Management { * Cast the ImmutableReducer to a regular reducer for compatibility with * the subplugin architecture (which expects plain redux reducers.) */ - reducer: { management: managementReducer } as ManagementPluginReducer, + reducer: { management: managementReducer } as unknown as ManagementPluginReducer, middleware: managementMiddlewareFactory(core, plugins), }, }; diff --git a/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts b/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts index b3289a510deef..1ce268ee74a5a 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts +++ b/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts @@ -101,7 +101,14 @@ export const sideEffectSimulatorFactory: () => SideEffectSimulator = () => { */ simulateElementResize(target: Element, contentRect: DOMRect) { if (this.elements.has(target)) { - const entries: ResizeObserverEntry[] = [{ target, contentRect }]; + const entries: ResizeObserverEntry[] = [ + { + target, + contentRect, + borderBoxSize: [{ inlineSize: 0, blockSize: 0 }], + contentBoxSize: [{ inlineSize: 0, blockSize: 0 }], + }, + ]; this.callback(entries, this); } } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx index af05198ef9974..feaad62b96e79 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx @@ -282,7 +282,7 @@ export const EventsTrSupplementContainer = styled.div.attrs(({ width }))``; export const EventsTrSupplement = styled.div.attrs(({ className = '' }) => ({ - className: `siemEventsTable__trSupplement ${className}`, + className: `siemEventsTable__trSupplement ${className}` as string, }))<{ className: string }>` font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; line-height: ${({ theme }) => theme.eui.euiLineHeight}; @@ -410,7 +410,7 @@ export const EventsHeadingTitleSpan = styled.span.attrs(({ className }) => ({ `; export const EventsHeadingExtra = styled.div.attrs(({ className = '' }) => ({ - className: `siemEventsHeading__extra ${className}`, + className: `siemEventsHeading__extra ${className}` as string, }))` margin-left: auto; margin-right: 2px; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.test.ts index 0e436760a88ee..0af6bceab26a5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.test.ts @@ -27,6 +27,7 @@ describe('query for signal', () => { server = serverMock.create(); ({ context } = requestContextMock.createTools()); + // @ts-expect-error 4.3.5 upgrade // eslint-disable-next-line @typescript-eslint/no-explicit-any ruleDataClient.getReader().search.mockResolvedValue(getEmptySignalsResponse() as any); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index 8da9267daabac..13106ec3012be 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -17,7 +17,7 @@ import { ExceptionListClient } from '../../../../../lists/server'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; import { getExceptionListItemSchemaMock } from '../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; -// @ts-expect-error +// @ts-expect-error 4.3.5 upgrade - likely requires moment upgrade moment.suppressDeprecationWarnings = true; import { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts index 040ade4dad41c..c10c63db62ee3 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts @@ -21,6 +21,17 @@ import { securitySolutionFactory } from './factory'; import { SecuritySolutionFactory } from './factory/types'; import { EndpointAppContext } from '../../endpoint/types'; +function isObj(req: unknown): req is Record { + return typeof req === 'object' && req !== null; +} +function assertValidRequestType( + req: unknown +): asserts req is StrategyRequestType & { factoryQueryType: FactoryQueryTypes } { + if (!isObj(req) || req.factoryQueryType == null) { + throw new Error('factoryQueryType is required'); + } +} + export const securitySolutionSearchStrategyProvider = ( data: PluginStart, endpointContext: EndpointAppContext @@ -29,9 +40,8 @@ export const securitySolutionSearchStrategyProvider = { - if (request.factoryQueryType == null) { - throw new Error('factoryQueryType is required'); - } + assertValidRequestType(request); + const queryFactory: SecuritySolutionFactory = securitySolutionFactory[request.factoryQueryType]; const dsl = queryFactory.buildDsl(request); diff --git a/x-pack/plugins/timelines/public/components/t_grid/styles.tsx b/x-pack/plugins/timelines/public/components/t_grid/styles.tsx index 9f957e2c61910..ceb19837c434d 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/styles.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/styles.tsx @@ -281,7 +281,7 @@ export const EventsTrSupplementContainer = styled.div.attrs(({ width }))``; export const EventsTrSupplement = styled.div.attrs(({ className = '' }) => ({ - className: `siemEventsTable__trSupplement ${className}`, + className: `siemEventsTable__trSupplement ${className}` as string, }))<{ className: string }>` font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; line-height: ${({ theme }) => theme.eui.euiLineHeight}; @@ -409,7 +409,7 @@ export const EventsHeadingTitleSpan = styled.span.attrs(({ className }) => ({ `; export const EventsHeadingExtra = styled.div.attrs(({ className = '' }) => ({ - className: `siemEventsHeading__extra ${className}`, + className: `siemEventsHeading__extra ${className}` as string, }))` margin-left: auto; margin-right: 2px; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/credential_store.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/credential_store.test.ts index 8532e2e4eece4..90adfdfa8fced 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/credential_store.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/credential_store.test.ts @@ -8,7 +8,7 @@ import { KibanaRequest } from 'src/core/server'; import { loggingSystemMock, httpServerMock } from 'src/core/server/mocks'; import { securityMock } from '../../../../security/server/mocks'; -import { ReindexSavedObject } from '../../../common/types'; +import { ReindexStep, ReindexStatus, ReindexSavedObject } from '../../../common/types'; import { credentialStoreFactory } from './credential_store'; const basicAuthHeader = 'Basic abc'; @@ -25,7 +25,19 @@ const securityStartMock = securityMock.createStart(); const reindexOpMock = { id: 'asdf', - attributes: { indexName: 'test', lastCompletedStep: 1, locked: null }, + type: 'type', + references: [], + attributes: { + indexName: 'test', + newIndexName: 'new-index', + status: ReindexStatus.inProgress, + lastCompletedStep: ReindexStep.created, + locked: null, + reindexTaskId: null, + reindexTaskPercComplete: null, + errorMessage: null, + runningReindexCount: null, + }, } as ReindexSavedObject; describe('credentialStore', () => { @@ -52,7 +64,7 @@ describe('credentialStore', () => { security: securityStartMock, }); - reindexOpMock.attributes.lastCompletedStep = 0; + reindexOpMock.attributes.lastCompletedStep = ReindexStep.readonly; expect(credStore.get(reindexOpMock)).toBeUndefined(); }); diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/use_monitor_histogram.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/use_monitor_histogram.ts index 8f500f54cf4ba..82299825b4154 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/use_monitor_histogram.ts +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/use_monitor_histogram.ts @@ -46,6 +46,7 @@ export const useMonitorHistogram = ({ items }: { items: MonitorSummary[] }) => { ); const histogramBuckets = data?.aggregations?.histogram.buckets ?? []; + // @ts-ignore 4.3.5 upgrade const simplified = histogramBuckets.map((histogramBucket) => { const byId: { [key: string]: number } = {}; histogramBucket.by_id.buckets.forEach((idBucket) => { diff --git a/x-pack/plugins/uptime/public/state/alerts/alerts.ts b/x-pack/plugins/uptime/public/state/alerts/alerts.ts index cc934a8c68d8b..a86de3ac02b83 100644 --- a/x-pack/plugins/uptime/public/state/alerts/alerts.ts +++ b/x-pack/plugins/uptime/public/state/alerts/alerts.ts @@ -10,7 +10,7 @@ import { handleActions, Action } from 'redux-actions'; import { call, put, select, takeLatest } from 'redux-saga/effects'; import { createAsyncAction } from '../actions/utils'; import { asyncInitState, handleAsyncAction } from '../reducers/utils'; -import { AppState } from '../index'; +import type { AppState } from '../../state'; import { AsyncInitState } from '../reducers/types'; import { fetchEffectFactory } from '../effects/fetch_effect'; import { @@ -110,7 +110,7 @@ export function* fetchAlertsEffect() { yield call(disableAlertById, action.payload); yield put(deleteAnomalyAlertAction.success(action.payload.alertId)); showAlertDisabledSuccess(); - const monitorId = yield select(monitorIdSelector); + const monitorId = (yield select(monitorIdSelector)) as AppState['ui']['monitorId']; yield put(getAnomalyAlertAction.get({ monitorId })); } catch (err) { showAlertDisabledFailed(err); @@ -145,9 +145,9 @@ export function* fetchAlertsEffect() { getMonitorAlertsAction.fail ) ); - yield takeLatest(createAlertAction.get, function* (action: Action) { + yield takeLatest(createAlertAction.get, function* (action: Action): Generator { try { - const response = yield call(createAlert, action.payload); + const response = (yield call(createAlert, action.payload)) as Alert; yield put(createAlertAction.success(response)); kibanaService.core.notifications.toasts.addSuccess( diff --git a/x-pack/plugins/uptime/public/state/effects/alerts.ts b/x-pack/plugins/uptime/public/state/effects/alerts.ts index c63c6c4bdb120..4463a55b1744a 100644 --- a/x-pack/plugins/uptime/public/state/effects/alerts.ts +++ b/x-pack/plugins/uptime/public/state/effects/alerts.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { Action } from 'redux-actions'; +import type { Action } from 'redux-actions'; import { call, put, takeLatest, select } from 'redux-saga/effects'; + import { fetchEffectFactory } from './fetch_effect'; import { deleteAlertAction, getExistingAlertAction } from '../actions/alerts'; import { disableAlertById, fetchAlertRecords } from '../api/alerts'; @@ -23,18 +24,21 @@ export function* fetchAlertsEffect() { ) ); - yield takeLatest(String(deleteAlertAction.get), function* (action: Action<{ alertId: string }>) { - try { - const response = yield call(disableAlertById, action.payload); - yield put(deleteAlertAction.success(response)); - kibanaService.core.notifications.toasts.addSuccess('Alert successfully deleted!'); - const monitorId = yield select(monitorIdSelector); - yield put(getExistingAlertAction.get({ monitorId })); - } catch (err) { - kibanaService.core.notifications.toasts.addError(err, { - title: 'Alert cannot be deleted', - }); - yield put(deleteAlertAction.fail(err)); + yield takeLatest( + String(deleteAlertAction.get), + function* (action: Action<{ alertId: string }>): Generator { + try { + const response = yield call(disableAlertById, action.payload); + yield put(deleteAlertAction.success(response)); + kibanaService.core.notifications.toasts.addSuccess('Alert successfully deleted!'); + const monitorId = (yield select(monitorIdSelector)) as ReturnType; + yield put(getExistingAlertAction.get({ monitorId })); + } catch (err) { + kibanaService.core.notifications.toasts.addError(err, { + title: 'Alert cannot be deleted', + }); + yield put(deleteAlertAction.fail(err)); + } } - }); + ); } diff --git a/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts b/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts index 250873b90f1a0..4fbd60f7929ce 100644 --- a/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts +++ b/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts @@ -25,7 +25,7 @@ export function fetchEffectFactory( success: (response: R) => Action, fail: (error: IHttpFetchError) => Action ) { - return function* (action: Action) { + return function* (action: Action): Generator { try { const response = yield call(fetch, action.payload); if (response instanceof Error) { @@ -34,7 +34,7 @@ export function fetchEffectFactory( yield put(fail(response as IHttpFetchError)); } else { - yield put(success(response)); + yield put(success(response as R)); } } catch (error) { // eslint-disable-next-line no-console diff --git a/x-pack/plugins/uptime/public/state/effects/journey.ts b/x-pack/plugins/uptime/public/state/effects/journey.ts index 8d33179bde402..8898eb0e5c521 100644 --- a/x-pack/plugins/uptime/public/state/effects/journey.ts +++ b/x-pack/plugins/uptime/public/state/effects/journey.ts @@ -14,11 +14,15 @@ import { FetchJourneyStepsParams, } from '../actions/journey'; import { fetchJourneySteps } from '../api/journey'; +import type { SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; -export function* fetchJourneyStepsEffect() { +export function* fetchJourneyStepsEffect(): Generator { yield takeLatest(getJourneySteps, function* (action: Action) { try { - const response = yield call(fetchJourneySteps, action.payload); + const response = (yield call( + fetchJourneySteps, + action.payload + )) as SyntheticsJourneyApiResponse; yield put(getJourneyStepsSuccess(response)); } catch (e) { yield put(getJourneyStepsFail({ checkGroup: action.payload.checkGroup, error: e })); diff --git a/x-pack/plugins/uptime/public/state/effects/ml_anomaly.ts b/x-pack/plugins/uptime/public/state/effects/ml_anomaly.ts index 6b2fa92a63b08..00c911e001fb3 100644 --- a/x-pack/plugins/uptime/public/state/effects/ml_anomaly.ts +++ b/x-pack/plugins/uptime/public/state/effects/ml_anomaly.ts @@ -22,8 +22,9 @@ import { deleteMLJob, getMLCapabilities, } from '../api/ml_anomaly'; -import { MonitorIdParam } from '../actions/types'; +import type { MonitorIdParam, DeleteJobResults } from '../actions/types'; import { anomalyAlertSelector, deleteAlertAction } from '../alerts/alerts'; +import type { AppState } from '../../state'; export function* fetchMLJobEffect() { yield takeLatest( @@ -43,20 +44,25 @@ export function* fetchMLJobEffect() { ) ); - yield takeLatest(String(deleteMLJobAction.get), function* (action: Action) { - try { - const response = yield call(deleteMLJob, action.payload); - yield put(deleteMLJobAction.success(response)); + yield takeLatest( + String(deleteMLJobAction.get), + function* (action: Action): Generator { + try { + const response = (yield call(deleteMLJob, action.payload)) as DeleteJobResults; + yield put(deleteMLJobAction.success(response)); - // let's delete alert as well if it's there - const { data: anomalyAlert } = yield select(anomalyAlertSelector); - if (anomalyAlert) { - yield put(deleteAlertAction.get({ alertId: anomalyAlert.id as string })); + // let's delete alert as well if it's there + const { data: anomalyAlert } = (yield select( + anomalyAlertSelector + )) as AppState['alerts']['anomalyAlert']; + if (anomalyAlert) { + yield put(deleteAlertAction.get({ alertId: anomalyAlert.id })); + } + } catch (err) { + yield put(deleteMLJobAction.fail(err)); } - } catch (err) { - yield put(deleteMLJobAction.fail(err)); } - }); + ); yield takeLatest( getMLCapabilitiesAction.get, diff --git a/x-pack/plugins/uptime/public/state/effects/network_events.ts b/x-pack/plugins/uptime/public/state/effects/network_events.ts index cad76dd059c01..75ea3d4467eb4 100644 --- a/x-pack/plugins/uptime/public/state/effects/network_events.ts +++ b/x-pack/plugins/uptime/public/state/effects/network_events.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Action } from 'redux-actions'; +import type { Action } from 'redux-actions'; import { call, put, takeLatest } from 'redux-saga/effects'; import { getNetworkEvents, @@ -14,27 +14,34 @@ import { FetchNetworkEventsParams, } from '../actions/network_events'; import { fetchNetworkEvents } from '../api/network_events'; +import type { SyntheticsNetworkEventsApiResponse } from '../../../common/runtime_types'; export function* fetchNetworkEventsEffect() { - yield takeLatest(getNetworkEvents, function* (action: Action) { - try { - const response = yield call(fetchNetworkEvents, action.payload); + yield takeLatest( + getNetworkEvents, + function* (action: Action): Generator { + try { + const response = (yield call( + fetchNetworkEvents, + action.payload + )) as SyntheticsNetworkEventsApiResponse; - yield put( - getNetworkEventsSuccess({ - checkGroup: action.payload.checkGroup, - stepIndex: action.payload.stepIndex, - ...response, - }) - ); - } catch (e) { - yield put( - getNetworkEventsFail({ - checkGroup: action.payload.checkGroup, - stepIndex: action.payload.stepIndex, - error: e, - }) - ); + yield put( + getNetworkEventsSuccess({ + checkGroup: action.payload.checkGroup, + stepIndex: action.payload.stepIndex, + ...response, + }) + ); + } catch (e) { + yield put( + getNetworkEventsFail({ + checkGroup: action.payload.checkGroup, + stepIndex: action.payload.stepIndex, + error: e, + }) + ); + } } - }); + ); } diff --git a/x-pack/plugins/uptime/public/state/index.ts b/x-pack/plugins/uptime/public/state/index.ts index 61b1a5f9d9527..506051b95fd5a 100644 --- a/x-pack/plugins/uptime/public/state/index.ts +++ b/x-pack/plugins/uptime/public/state/index.ts @@ -6,6 +6,7 @@ */ import { createStore, applyMiddleware } from 'redux'; +import type { Store } from 'redux'; import { composeWithDevTools } from 'redux-devtools-extension'; import createSagaMiddleware from 'redux-saga'; import { rootEffect } from './effects'; @@ -15,6 +16,6 @@ export type AppState = ReturnType; const sagaMW = createSagaMiddleware(); -export const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(sagaMW))); +export const store: Store = createStore(rootReducer, composeWithDevTools(applyMiddleware(sagaMW))); sagaMW.run(rootEffect); diff --git a/x-pack/plugins/uptime/public/state/selectors/index.ts b/x-pack/plugins/uptime/public/state/selectors/index.ts index c0da9389f13af..db18cbb9e114f 100644 --- a/x-pack/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/plugins/uptime/public/state/selectors/index.ts @@ -6,7 +6,7 @@ */ import { createSelector } from 'reselect'; -import { AppState } from '../../state'; +import type { AppState } from '../../state'; // UI Selectors export const getBasePath = ({ ui: { basePath } }: AppState) => basePath; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts index 81a1e6ba7ca7a..4fe7e94803fd7 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts @@ -189,6 +189,7 @@ export const getMonitorStatus: UMElasticsearchQueryFn< monitors = monitors.concat(monitorRes); } while (afterKey !== undefined); + // @ts-ignore 4.3.5 upgrade - Expression produces a union type that is too complex to represent.ts(2590) return monitors .filter((monitor) => monitor?.doc_count >= numTimes) .map(({ key, doc_count: count, fields }) => ({ diff --git a/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.test.ts index 22f714e408a1c..f62c31b10e459 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.test.ts @@ -217,4 +217,42 @@ describe('getPingHistogram', () => { expect(mockEsClient.search).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); + + it('returns an empty array if agg buckets are undefined', async () => { + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + + mockEsClient.search.mockResolvedValueOnce({ + body: { + aggregations: { + timeseries: { + buckets: undefined, + interval: '1m', + }, + }, + }, + } as any); + + const result = await getPingHistogram({ uptimeEsClient, dateStart: 'now-15m', dateEnd: 'now' }); + + expect(result.histogram).toEqual([]); + }); + + it('returns an empty array if agg buckets are empty', async () => { + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + + mockEsClient.search.mockResolvedValueOnce({ + body: { + aggregations: { + timeseries: { + buckets: [], + interval: '1m', + }, + }, + }, + } as any); + + const result = await getPingHistogram({ uptimeEsClient, dateStart: 'now-15m', dateEnd: 'now' }); + + expect(result.histogram).toEqual([]); + }); }); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts b/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts index 600a335effe2c..139ac8bad417c 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts @@ -90,7 +90,7 @@ export const getPingHistogram: UMElasticsearchQueryFn { + const histogram = buckets.map((bucket: Pick) => { const x: number = bucket.key; const downCount = bucket.down.value || 0; const upCount = bucket.up.value || 0; diff --git a/x-pack/test/functional/services/observability/users.ts b/x-pack/test/functional/services/observability/users.ts index 7cb98603548ba..ac2fc8ddd324e 100644 --- a/x-pack/test/functional/services/observability/users.ts +++ b/x-pack/test/functional/services/observability/users.ts @@ -96,7 +96,6 @@ const defineBasicObservabilityRole = ( { spaces: ['*'], base: [], - // @ts-expect-error TypeScript doesn't distinguish between missing and // undefined props yet feature: features, }, diff --git a/yarn.lock b/yarn.lock index 7a1afe15ddd33..ca0569e44007b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1477,14 +1477,6 @@ dependencies: tslib "^2.0.0" -"@dsherret/to-absolute-glob@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@dsherret/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1f6475dc8bd974cea07a2daf3864b317b1dd332c" - integrity sha1-H2R13IvZdM6gei2vOGSzF7HdMyw= - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - "@elastic/apm-rum-core@^5.12.1": version "5.12.1" resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.12.1.tgz#ad78787876c68b9ce718d1c42b8e7b12b12eaa69" @@ -4950,17 +4942,15 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@ts-morph/common@~0.7.0": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.7.3.tgz#380020c278e4aa6cecedf362a1157591d1003267" - integrity sha512-M6Tcu0EZDLL8Ht7WAYz7yJfDZ9eArhqR8XZ9Mk3q8jwU6MKFAttrw3JtW4JhneqTz7pZMv4XaimEdXI0E4K4rg== +"@ts-morph/common@~0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.10.1.tgz#be15b9ab13a32bbc1f6a6bd7dc056b2247b272eb" + integrity sha512-rKN/VtZUUlW4M+6vjLFSaFc1Z9sK+1hh0832ucPtPkXqOw/mSWE80Lau4z2zTPNTqtxAjfZbvKpQcEwJy0KIEg== dependencies: - "@dsherret/to-absolute-glob" "^2.0.2" - fast-glob "^3.2.4" - is-negated-glob "^1.0.0" + fast-glob "^3.2.5" + minimatch "^3.0.4" mkdirp "^1.0.4" - multimatch "^5.0.0" - typescript "~4.1.2" + path-browserify "^1.0.1" "@tsconfig/node10@^1.0.7": version "1.0.8" @@ -5626,11 +5616,16 @@ resolved "https://registry.yarnpkg.com/@types/he/-/he-1.1.1.tgz#19e14033c4ee8f1a702c74dcc6182664839ac2b7" integrity sha512-jpzrsR1ns0n3kyWt92QfOUQhIuJGQ9+QGa7M62rO6toe98woQjnsnzjdMtsQXCdvjjmqjS2ZBCC7xKw0cdzU+Q== -"@types/history@*", "@types/history@^4.7.3": +"@types/history@*": version "4.7.3" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.3.tgz#856c99cdc1551d22c22b18b5402719affec9839a" integrity sha512-cS5owqtwzLN5kY+l+KgKdRJ/Cee8tlmQoGQuIE9tWnSmS3JMKzmxo2HIAk2wODMifGwO20d62xZQLYz+RLfXmw== +"@types/history@^4.7.9": + version "4.7.9" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.9.tgz#1cfb6d60ef3822c589f18e70f8b12f9a28ce8724" + integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ== + "@types/hjson@^2.4.2": version "2.4.2" resolved "https://registry.yarnpkg.com/@types/hjson/-/hjson-2.4.2.tgz#fd0288a5b6778cda993c978e43cc978ddc8f22e9" @@ -13699,17 +13694,16 @@ fast-glob@2.2.7, fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@3.2.5, fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.2, fast-glob@^3.2.4: - version "3.2.5" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" - integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== +fast-glob@3.2.7, fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.2, fast-glob@^3.2.4, fast-glob@^3.2.5: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" + glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" + micromatch "^4.0.4" fast-json-parse@^1.0.3: version "1.0.3" @@ -14751,7 +14745,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.0: +glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -20022,17 +20016,6 @@ multimatch@^4.0.0: arrify "^2.0.1" minimatch "^3.0.4" -multimatch@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" - integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== - dependencies: - "@types/minimatch" "^3.0.3" - array-differ "^3.0.0" - array-union "^2.1.0" - arrify "^2.0.1" - minimatch "^3.0.4" - multiparty@^4.1.2: version "4.2.1" resolved "https://registry.yarnpkg.com/multiparty/-/multiparty-4.2.1.tgz#d9b6c46d8b8deab1ee70c734b0af771dd46e0b13" @@ -21495,6 +21478,11 @@ path-browserify@0.0.1, path-browserify@~0.0.0: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -27509,13 +27497,12 @@ ts-loader@^7.0.5: micromatch "^4.0.0" semver "^6.0.0" -ts-morph@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-9.1.0.tgz#10d2088387c71f3c674f82492a3cec1e3538f0dd" - integrity sha512-sei4u651MBenr27sD6qLDXN3gZ4thiX71E3qV7SuVtDas0uvK2LtgZkIYUf9DKm/fLJ6AB/+yhRJ1vpEBJgy7Q== +ts-morph@^11.0.0: + version "11.0.3" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-11.0.3.tgz#01a92b3c2b5a48ccdf318ec90864229b8061d056" + integrity sha512-ymuPkndv9rzqTLiHWMkVrFXWcN4nBiBGhRP/kTC9F5amAAl7BNLfyrsTzMD1o9A0zishKoF1KQT/0yyFhJnPgA== dependencies: - "@dsherret/to-absolute-glob" "^2.0.2" - "@ts-morph/common" "~0.7.0" + "@ts-morph/common" "~0.10.1" code-block-writer "^10.1.1" ts-node@^10.2.1: @@ -27737,10 +27724,10 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -typescript@4.1.3, typescript@^3.3.3333, typescript@^3.5.3, typescript@^4.3.5, typescript@~4.1.2, typescript@~4.4.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== +typescript@4.3.5, typescript@^3.3.3333, typescript@^3.5.3, typescript@^4.3.5, typescript@~4.4.2: + version "4.3.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== ua-parser-js@^0.7.18: version "0.7.24" From 1b38b8ea1516d996432e9a280e455ef8d0d2fbd0 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 3 Dec 2021 02:48:51 +0000 Subject: [PATCH 24/65] chore(NA): splits types from code on @kbn/config (#120267) --- package.json | 1 + packages/BUILD.bazel | 1 + packages/kbn-cli-dev-mode/BUILD.bazel | 2 +- packages/kbn-config/BUILD.bazel | 26 ++++++++++++++++++++++---- packages/kbn-config/package.json | 1 - packages/kbn-docs-utils/BUILD.bazel | 2 +- packages/kbn-optimizer/BUILD.bazel | 4 +++- yarn.lock | 4 ++++ 8 files changed, 33 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 06e338ab87ca7..a6dfd03893c81 100644 --- a/package.json +++ b/package.json @@ -561,6 +561,7 @@ "@types/kbn__apm-config-loader": "link:bazel-bin/packages/kbn-apm-config-loader/npm_module_types", "@types/kbn__apm-utils": "link:bazel-bin/packages/kbn-apm-utils/npm_module_types", "@types/kbn__cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode/npm_module_types", + "@types/kbn__config": "link:bazel-bin/packages/kbn-config/npm_module_types", "@types/kbn__i18n": "link:bazel-bin/packages/kbn-i18n/npm_module_types", "@types/kbn__i18n-react": "link:bazel-bin/packages/kbn-i18n-react/npm_module_types", "@types/license-checker": "15.0.0", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 9e124cf63a391..8208496f7d800 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -83,6 +83,7 @@ filegroup( "//packages/kbn-apm-config-loader:build_types", "//packages/kbn-apm-utils:build_types", "//packages/kbn-cli-dev-mode:build_types", + "//packages/kbn-config:build_types", "//packages/kbn-i18n:build_types", "//packages/kbn-i18n-react:build_types", ], diff --git a/packages/kbn-cli-dev-mode/BUILD.bazel b/packages/kbn-cli-dev-mode/BUILD.bazel index 686866ce7bc88..c6611e71e35ab 100644 --- a/packages/kbn-cli-dev-mode/BUILD.bazel +++ b/packages/kbn-cli-dev-mode/BUILD.bazel @@ -48,7 +48,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/kbn-config", + "//packages/kbn-config:npm_module_types", "//packages/kbn-config-schema", "//packages/kbn-dev-utils", "//packages/kbn-logging", diff --git a/packages/kbn-config/BUILD.bazel b/packages/kbn-config/BUILD.bazel index 061b8306bcc6f..c0b75ab491ac0 100644 --- a/packages/kbn-config/BUILD.bazel +++ b/packages/kbn-config/BUILD.bazel @@ -1,9 +1,10 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") -load("//src/dev/bazel:index.bzl", "jsts_transpiler") +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") PKG_BASE_NAME = "kbn-config" PKG_REQUIRE_NAME = "@kbn/config" +TYPES_PKG_REQUIRE_NAME = "@types/kbn__config" SOURCE_FILES = glob( [ @@ -91,7 +92,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + deps = RUNTIME_DEPS + [":target_node"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) @@ -110,3 +111,20 @@ filegroup( ], visibility = ["//visibility:public"], ) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = TYPES_PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json index e30db61ae0c28..012b39d733a78 100644 --- a/packages/kbn-config/package.json +++ b/packages/kbn-config/package.json @@ -1,7 +1,6 @@ { "name": "@kbn/config", "main": "./target_node/index.js", - "types": "./target_types/index.d.ts", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", "private": true diff --git a/packages/kbn-docs-utils/BUILD.bazel b/packages/kbn-docs-utils/BUILD.bazel index ab018a0ab73b0..6bb37b3500152 100644 --- a/packages/kbn-docs-utils/BUILD.bazel +++ b/packages/kbn-docs-utils/BUILD.bazel @@ -36,7 +36,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/kbn-config", + "//packages/kbn-config:npm_module_types", "//packages/kbn-dev-utils", "//packages/kbn-utils", "@npm//ts-morph", diff --git a/packages/kbn-optimizer/BUILD.bazel b/packages/kbn-optimizer/BUILD.bazel index 647fcdfcbaad3..cc03c81070745 100644 --- a/packages/kbn-optimizer/BUILD.bazel +++ b/packages/kbn-optimizer/BUILD.bazel @@ -46,6 +46,7 @@ RUNTIME_DEPS = [ "@npm//execa", "@npm//jest-diff", "@npm//json-stable-stringify", + "@npm//js-yaml", "@npm//lmdb-store", "@npm//loader-utils", "@npm//node-sass", @@ -61,7 +62,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/kbn-config", + "//packages/kbn-config:npm_module_types", "//packages/kbn-config-schema", "//packages/kbn-dev-utils", "//packages/kbn-std", @@ -81,6 +82,7 @@ TYPES_DEPS = [ "@npm//@types/compression-webpack-plugin", "@npm//@types/jest", "@npm//@types/json-stable-stringify", + "@npm//@types/js-yaml", "@npm//@types/loader-utils", "@npm//@types/node", "@npm//@types/normalize-path", diff --git a/yarn.lock b/yarn.lock index ca0569e44007b..9472271c20998 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5824,6 +5824,10 @@ version "0.0.0" uid "" +"@types/kbn__config@link:bazel-bin/packages/kbn-config/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__i18n-react@link:bazel-bin/packages/kbn-i18n-react/npm_module_types": version "0.0.0" uid "" From 31042342d61dfc3e778e9217eefd589c795f903c Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 2 Dec 2021 21:14:13 -0700 Subject: [PATCH 25/65] [jest] restore integration test running x-pack (#120252) --- jest.config.integration.js | 16 +++++++++++++++- .../get_searchable_types.test.ts | 3 ++- .../managed_configuration.test.ts | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/jest.config.integration.js b/jest.config.integration.js index e2b2afaa715ee..a2ac498986c08 100644 --- a/jest.config.integration.js +++ b/jest.config.integration.js @@ -6,8 +6,22 @@ * Side Public License, v 1. */ +const Fs = require('fs'); +const Path = require('path'); + module.exports = { preset: '@kbn/test/jest_integration', rootDir: '.', - roots: ['/src', '/packages'], + roots: [ + '/src', + '/packages', + ...Fs.readdirSync(Path.resolve(__dirname, 'x-pack')).flatMap((name) => { + // create roots for all x-pack/* dirs except for test + if (name !== 'test' && Fs.statSync(Path.resolve(__dirname, 'x-pack', name)).isDirectory()) { + return [`/x-pack/${name}`]; + } + + return []; + }), + ], }; diff --git a/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts b/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts index d1a9f692c2e9f..b8224d9a30d08 100644 --- a/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts +++ b/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts @@ -14,7 +14,8 @@ import { registerInternalSearchableTypesRoute } from '../get_searchable_types'; type SetupServerReturn = UnwrapPromise>; const pluginId = Symbol('globalSearch'); -describe('GET /internal/global_search/searchable_types', () => { +// FAILING: https://github.com/elastic/kibana/issues/120268 +describe.skip('GET /internal/global_search/searchable_types', () => { let server: SetupServerReturn['server']; let httpSetup: SetupServerReturn['httpSetup']; let globalSearchHandlerContext: ReturnType< diff --git a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts index 271d24d73357b..3442e69aab44a 100644 --- a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts +++ b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts @@ -16,7 +16,8 @@ import { TaskManagerPlugin, TaskManagerStartContract } from '../plugin'; import { coreMock } from '../../../../../src/core/server/mocks'; import { TaskManagerConfig } from '../config'; -describe('managed configuration', () => { +// FAILING: https://github.com/elastic/kibana/issues/120269 +describe.skip('managed configuration', () => { let taskManagerStart: TaskManagerStartContract; let logger: Logger; From 3ede8c4a8b09578b54d37f1e694bee58521be557 Mon Sep 17 00:00:00 2001 From: Peter Dyson Date: Fri, 3 Dec 2021 15:53:25 +1000 Subject: [PATCH 26/65] [DOCS] document missing enabledActionTypes value for Microsoft Teams action (#113211) * [DOCS] document missing value for Microsoft Teams action [DOCS] document missing value for Microsoft Teams action for xpack.actions.enabledActionTypes config. eg: xpack.actions.enabledActionTypes: ['.email','.teams'] * include the full list of possible values * Update docs/settings/alert-action-settings.asciidoc LGTM Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * commit using @elastic.co Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/settings/alert-action-settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 66e23686e14e0..f0c22ebf8b730 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -126,7 +126,7 @@ into a single string. This configuration can be used for environments where the files cannot be made available. `xpack.actions.enabledActionTypes` {ess-icon}:: -A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, and `.webhook`. An empty list `[]` will disable all action types. +A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.email`, `.index`, `.jira`, `.pagerduty`, `.resilient`, `.server-log`, `.servicenow`, .`servicenow-itom`, `.servicenow-sir`, `.slack`, `.swimlane`, `.teams`, and `.webhook`. An empty list `[]` will disable all action types. + Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function. From 8e5b3a0fd33939a371e37be7e076ea7d19fb8b02 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 3 Dec 2021 09:05:04 +0200 Subject: [PATCH 27/65] [Canvas] Filters panel for element settings. (#117270) * Added filters panel on selecting the element. * Added unit tests. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../element_settings.component.tsx | 38 +++++++- .../workpad_filters/__stories__/elements.ts | 19 +++- .../__stories__/workpad_filters.stories.tsx | 5 +- .../hooks/use_canvas_filters.ts | 7 +- .../workpad_filters/workpad_filters.tsx | 19 +++- .../canvas/public/functions/filters.ts | 7 +- .../plugins/canvas/public/lib/filter.test.ts | 89 +++++++++++++++++++ x-pack/plugins/canvas/public/lib/filter.ts | 42 ++++++++- 8 files changed, 206 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/canvas/public/components/sidebar/element_settings/element_settings.component.tsx b/x-pack/plugins/canvas/public/components/sidebar/element_settings/element_settings.component.tsx index a912668d91432..0dcafd012ee7c 100644 --- a/x-pack/plugins/canvas/public/components/sidebar/element_settings/element_settings.component.tsx +++ b/x-pack/plugins/canvas/public/components/sidebar/element_settings/element_settings.component.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useState } from 'react'; import PropTypes from 'prop-types'; -import { EuiTabbedContent } from '@elastic/eui'; +import { EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; // @ts-expect-error unconverted component @@ -15,6 +15,8 @@ import { Datasource } from '../../datasource'; // @ts-expect-error unconverted component import { FunctionFormList } from '../../function_form_list'; import { PositionedElement } from '../../../../types'; +import { WorkpadFilters } from '../../workpad_filters/workpad_filters'; +import { isExpressionWithFilters } from '../../../lib/filter'; const strings = { getDataTabLabel: () => @@ -29,6 +31,11 @@ const strings = { defaultMessage: 'Display', description: 'This tab contains the settings for how data is displayed in a Canvas element', }), + getFiltersTabLabel: () => + i18n.translate('xpack.canvas.elementSettings.filtersTabLabel', { + defaultMessage: 'Filters', + description: 'This tab contains information about filters related to a Canvas element', + }), }; interface Props { @@ -39,6 +46,16 @@ interface Props { } export const ElementSettings: FunctionComponent = ({ element }) => { + const filtersTab = isExpressionWithFilters(element.expression) && { + id: 'filters', + name: strings.getFiltersTabLabel(), + content: ( +
+ +
+ ), + }; + const tabs = [ { id: 'edit', @@ -60,8 +77,23 @@ export const ElementSettings: FunctionComponent = ({ element }) => { ), }, + ...(filtersTab ? [filtersTab] : []), ]; - return ; + + const [selectedTab, setSelectedTab] = useState(tabs[0]); + + const onTabClick = (tab: EuiTabbedContentTab) => setSelectedTab(tab); + + const getTab = (tabId: string) => tabs.filter((tab) => tab.id === tabId)[0] ?? tabs[0]; + + return ( + + ); }; ElementSettings.propTypes = { diff --git a/x-pack/plugins/canvas/public/components/workpad_filters/__stories__/elements.ts b/x-pack/plugins/canvas/public/components/workpad_filters/__stories__/elements.ts index 9eacfb54a411f..56df931ffade5 100644 --- a/x-pack/plugins/canvas/public/components/workpad_filters/__stories__/elements.ts +++ b/x-pack/plugins/canvas/public/components/workpad_filters/__stories__/elements.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import { CanvasElement } from '../../../../types'; +import { CanvasElement, PositionedElement } from '../../../../types'; const timeFormat = 'MM.dd.YYYY HH:mm'; @@ -31,6 +31,14 @@ const time2 = { }; const group2 = 'Group 2'; +export const element: CanvasElement = { + id: '0', + position: generatePosition(0), + type: 'element', + expression: `filters group="${group2}"`, + filter: '', +}; + const element1: CanvasElement = { id: '1', position: generatePosition(1), @@ -44,7 +52,7 @@ const element2: CanvasElement = { position: generatePosition(2), type: 'element', expression: '', - filter: `exactly value="machine-learning" column="project1" filterGroup="${group1}"`, + filter: `exactly value="machine-learning" column="project1" filterGroup="${group2}"`, }; const element3: CanvasElement = { @@ -63,4 +71,9 @@ const element4: CanvasElement = { filter: `exactly value="kibana" column="project2" filterGroup="${group2}"`, }; -export const elements = [element1, element2, element3, element4]; +export const elementWithGroup: PositionedElement = { + ...element, + ast: { type: 'expression', chain: [] }, +}; + +export const elements = [element, element1, element2, element3, element4]; diff --git a/x-pack/plugins/canvas/public/components/workpad_filters/__stories__/workpad_filters.stories.tsx b/x-pack/plugins/canvas/public/components/workpad_filters/__stories__/workpad_filters.stories.tsx index b97043bf83304..b477ac220f6a9 100644 --- a/x-pack/plugins/canvas/public/components/workpad_filters/__stories__/workpad_filters.stories.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_filters/__stories__/workpad_filters.stories.tsx @@ -9,7 +9,7 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; import { reduxDecorator } from '../../../../storybook'; import { WorkpadFilters } from '../workpad_filters'; -import { elements } from './elements'; +import { elementWithGroup, elements } from './elements'; storiesOf('components/WorkpadFilters/WorkpadFilters', module) .addDecorator((story) => ( @@ -20,4 +20,5 @@ storiesOf('components/WorkpadFilters/WorkpadFilters', module) )) .addDecorator(reduxDecorator({ elements })) - .add('redux: default', () => ); + .add('redux: default', () => ) + .add('redux: selected element with group', () => ); diff --git a/x-pack/plugins/canvas/public/components/workpad_filters/hooks/use_canvas_filters.ts b/x-pack/plugins/canvas/public/components/workpad_filters/hooks/use_canvas_filters.ts index ce8e90def5aad..10643e729d837 100644 --- a/x-pack/plugins/canvas/public/components/workpad_filters/hooks/use_canvas_filters.ts +++ b/x-pack/plugins/canvas/public/components/workpad_filters/hooks/use_canvas_filters.ts @@ -8,15 +8,18 @@ import { fromExpression } from '@kbn/interpreter/common'; import { shallowEqual, useSelector } from 'react-redux'; import { State } from '../../../../types'; +import { getFiltersByGroups } from '../../../lib/filter'; import { adaptCanvasFilter } from '../../../lib/filter_adapters'; import { getGlobalFilters } from '../../../state/selectors/workpad'; const extractExpressionAST = (filtersExpressions: string[]) => fromExpression(filtersExpressions.join(' | ')); -export function useCanvasFilters() { +export function useCanvasFilters(groups: string[] = [], ungrouped: boolean = false) { const filterExpressions = useSelector((state: State) => getGlobalFilters(state), shallowEqual); - const expression = extractExpressionAST(filterExpressions); + const filtersByGroups = getFiltersByGroups(filterExpressions, groups, ungrouped); + + const expression = extractExpressionAST(filtersByGroups); const filters = expression.chain.map(adaptCanvasFilter); return filters; diff --git a/x-pack/plugins/canvas/public/components/workpad_filters/workpad_filters.tsx b/x-pack/plugins/canvas/public/components/workpad_filters/workpad_filters.tsx index c04fe543804b4..610e6e56af350 100644 --- a/x-pack/plugins/canvas/public/components/workpad_filters/workpad_filters.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_filters/workpad_filters.tsx @@ -7,14 +7,22 @@ import React, { FC, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { State, FilterField } from '../../../types'; -import { groupFiltersBy } from '../../lib/filter'; +import { State, FilterField, PositionedElement } from '../../../types'; +import { + extractGroupsFromElementsFilters, + groupFiltersBy, + extractUngroupedFromElementsFilters, +} from '../../lib/filter'; import { setGroupFiltersByOption } from '../../state/actions/sidebar'; import { getGroupFiltersByOption } from '../../state/selectors/sidebar'; import { useCanvasFilters } from './hooks'; import { WorkpadFilters as Component } from './workpad_filters.component'; -export const WorkpadFilters: FC = () => { +interface Props { + element?: PositionedElement | null; +} + +export const WorkpadFilters: FC = ({ element }) => { const groupFiltersByField: FilterField = useSelector((state: State) => getGroupFiltersByOption(state) ); @@ -28,7 +36,10 @@ export const WorkpadFilters: FC = () => { [dispatch] ); - const canvasFilters = useCanvasFilters(); + const groups = element ? extractGroupsFromElementsFilters(element.expression) : undefined; + const ungrouped = element ? extractUngroupedFromElementsFilters(element.expression) : false; + + const canvasFilters = useCanvasFilters(groups, ungrouped); const filtersGroups = groupFiltersByField ? groupFiltersBy(canvasFilters, groupFiltersByField) diff --git a/x-pack/plugins/canvas/public/functions/filters.ts b/x-pack/plugins/canvas/public/functions/filters.ts index bc6aedd4662c1..ca2cede4e8555 100644 --- a/x-pack/plugins/canvas/public/functions/filters.ts +++ b/x-pack/plugins/canvas/public/functions/filters.ts @@ -15,6 +15,7 @@ import { getGlobalFilters, getWorkpadVariablesAsObject } from '../state/selector import { ExpressionValueFilter } from '../../types'; import { getFunctionHelp } from '../../i18n'; import { InitializeArguments } from '.'; +import { getFiltersByGroups } from '../lib/filter'; export interface Arguments { group: string[]; @@ -35,11 +36,7 @@ function getFiltersByGroup(allFilters: string[], groups?: string[], ungrouped = }); } - return allFilters.filter((filter: string) => { - const ast = fromExpression(filter); - const expGroups: string[] = get(ast, 'chain[0].arguments.filterGroup', []); - return expGroups.length > 0 && expGroups.every((expGroup) => groups.includes(expGroup)); - }); + return getFiltersByGroups(allFilters, groups); } type FiltersFunction = ExpressionFunctionDefinition< diff --git a/x-pack/plugins/canvas/public/lib/filter.test.ts b/x-pack/plugins/canvas/public/lib/filter.test.ts index 497f75b91650f..bf19bd6ecf4b8 100644 --- a/x-pack/plugins/canvas/public/lib/filter.test.ts +++ b/x-pack/plugins/canvas/public/lib/filter.test.ts @@ -18,6 +18,10 @@ import { flattenFilterView, createFilledFilterView, groupFiltersBy, + getFiltersByGroups, + extractGroupsFromElementsFilters, + extractUngroupedFromElementsFilters, + isExpressionWithFilters, } from './filter'; const formatterFactory = (value: unknown) => () => JSON.stringify(value); @@ -280,3 +284,88 @@ describe('groupFiltersBy', () => { expect(grouped).toEqual([{ name: null, filters: filtersWithoutGroups }]); }); }); + +describe('getFiltersByGroups', () => { + const group1 = 'Group 1'; + const group2 = 'Group 2'; + + const filters = [ + `exactly value="x-pack" column="project1" filterGroup="${group1}"`, + `exactly value="beats" column="project1" filterGroup="${group2}"`, + `exactly value="machine-learning" column="project1"`, + `exactly value="kibana" column="project2" filterGroup="${group2}"`, + ]; + + it('returns all filters related to a specified groups', () => { + expect(getFiltersByGroups(filters, [group1, group2])).toEqual([ + filters[0], + filters[1], + filters[3], + ]); + + expect(getFiltersByGroups(filters, [group2])).toEqual([filters[1], filters[3]]); + }); + + it('returns filters without group if ungrouped is true', () => { + expect(getFiltersByGroups(filters, [], true)).toEqual([filters[2]]); + }); + + it('returns filters with group if ungrouped is true and groups are not empty', () => { + expect(getFiltersByGroups(filters, [group1], true)).toEqual([filters[0]]); + }); + + it('returns empty array if not found any filter with a specified group', () => { + expect(getFiltersByGroups(filters, ['absent group'])).toEqual([]); + }); + + it('returns empty array if not groups specified', () => { + expect(getFiltersByGroups(filters, [])).toEqual(filters); + }); +}); + +describe('extractGroupsFromElementsFilters', () => { + const exprFilters = 'filters'; + const exprRest = 'demodata | plot | render'; + + it('returns groups which are specified at filters expression', () => { + const groups = ['group 1', 'group 2', 'group 3', 'group 4']; + const additionalGroups = [...groups, 'group 5']; + const groupsExpr = groups.map((group) => `group="${group}"`).join(' '); + const additionalGroupsExpr = additionalGroups.map((group) => `group="${group}"`).join(' '); + + expect( + extractGroupsFromElementsFilters( + `${exprFilters} ${groupsExpr} | ${exprFilters} ${additionalGroupsExpr} | ${exprRest}` + ) + ).toEqual(additionalGroups); + }); + + it('returns empty array if no groups were specified at filters expression', () => { + expect(extractGroupsFromElementsFilters(`${exprFilters} | ${exprRest}`)).toEqual([]); + }); +}); + +describe('extractUngroupedFromElementsFilters', () => { + it('checks if ungrouped filters expression exist at the element', () => { + const expression = + 'filters group="10" group="11" | filters group="15" ungrouped=true | demodata | plot | render'; + const isUngrouped = extractUngroupedFromElementsFilters(expression); + expect(isUngrouped).toBeTruthy(); + + const nextExpression = + 'filters group="10" group="11" | filters group="15" | demodata | plot | render'; + const nextIsUngrouped = extractUngroupedFromElementsFilters(nextExpression); + expect(nextIsUngrouped).toBeFalsy(); + }); +}); + +describe('isExpressionWithFilters', () => { + it('checks if the expression is applying filters', () => { + const expression = + 'filters group="10" group="11" | filters group="15" ungrouped=true | demodata | plot | render'; + expect(isExpressionWithFilters(expression)).toBeTruthy(); + + const nextExpression = 'demodata | plot | render'; + expect(isExpressionWithFilters(nextExpression)).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/canvas/public/lib/filter.ts b/x-pack/plugins/canvas/public/lib/filter.ts index ae75822e4a7c9..56f6558eff48b 100644 --- a/x-pack/plugins/canvas/public/lib/filter.ts +++ b/x-pack/plugins/canvas/public/lib/filter.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { flowRight, groupBy } from 'lodash'; +import { fromExpression } from '@kbn/interpreter/common'; +import { flowRight, get, groupBy } from 'lodash'; import { Filter as FilterType, FilterField, @@ -53,3 +54,42 @@ export const groupFiltersBy = (filters: FilterType[], groupByField: FilterField) filters: groupedFilters[key], })); }; + +export const getFiltersByGroups = ( + filters: string[], + groups: string[], + ungrouped: boolean = false +) => + filters.filter((filter: string) => { + const ast = fromExpression(filter); + const expGroups: string[] = get(ast, 'chain[0].arguments.filterGroup', []); + if (!groups?.length && ungrouped) { + return expGroups.length === 0; + } + + return ( + !groups.length || + (expGroups.length > 0 && expGroups.every((expGroup) => groups.includes(expGroup))) + ); + }); + +export const extractGroupsFromElementsFilters = (expr: string) => { + const ast = fromExpression(expr); + const filtersFns = ast.chain.filter((expression) => expression.function === 'filters'); + const groups = filtersFns.reduce((foundGroups, filterFn) => { + const filterGroups = filterFn?.arguments.group?.map((g) => g.toString()) ?? []; + return [...foundGroups, ...filterGroups]; + }, []); + return [...new Set(groups)]; +}; + +export const extractUngroupedFromElementsFilters = (expr: string) => { + const ast = fromExpression(expr); + const filtersFns = ast.chain.filter((expression) => expression.function === 'filters'); + return filtersFns.some((filterFn) => filterFn?.arguments.ungrouped?.[0]); +}; + +export const isExpressionWithFilters = (expr: string) => { + const ast = fromExpression(expr); + return ast.chain.some((expression) => expression.function === 'filters'); +}; From fea14a0da5de5adf4baa54b5cfe4876952afe8d2 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 3 Dec 2021 09:12:46 +0200 Subject: [PATCH 28/65] [Canvas] Added KibanaThemeProvider to expression_error. (#120073) * Wrapped up debug and error with KibanaThemeProvider. 1. Replaced renderer with getRenderer function and factory. 2. Updated storybook. 3. Moved defaultTheme$ to presentain_util. * Fixed exports. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__stories__/error_renderer.stories.tsx | 4 +- .../expression_renderers/debug_renderer.tsx | 35 ++++++++---- .../expression_renderers/error_renderer.tsx | 55 +++++++++++-------- .../public/expression_renderers/index.ts | 8 +-- src/plugins/expression_error/public/index.ts | 11 ++-- src/plugins/expression_error/public/plugin.ts | 6 +- .../common/lib/utils}/default_theme.ts | 6 +- .../common/lib/utils/index.ts | 1 + .../canvas_plugin_src/renderers/external.ts | 9 +-- .../renderers/markdown/index.tsx | 2 +- .../canvas_plugin_src/renderers/table.tsx | 2 +- .../canvas_plugin_src/renderers/text.tsx | 2 +- .../shareable_runtime/supported_renderers.js | 14 +++-- 13 files changed, 93 insertions(+), 62 deletions(-) rename {x-pack/plugins/canvas/public/lib => src/plugins/presentation_util/common/lib/utils}/default_theme.ts (66%) diff --git a/src/plugins/expression_error/public/expression_renderers/__stories__/error_renderer.stories.tsx b/src/plugins/expression_error/public/expression_renderers/__stories__/error_renderer.stories.tsx index 9081a8512c11a..378e22f834e1d 100644 --- a/src/plugins/expression_error/public/expression_renderers/__stories__/error_renderer.stories.tsx +++ b/src/plugins/expression_error/public/expression_renderers/__stories__/error_renderer.stories.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; -import { errorRenderer } from '../error_renderer'; +import { getErrorRenderer } from '../error_renderer'; import { Render } from '../../../../presentation_util/public/__stories__'; storiesOf('renderers/error', module).add('default', () => { @@ -16,5 +16,5 @@ storiesOf('renderers/error', module).add('default', () => { const config = { error: thrownError, }; - return ; + return ; }); diff --git a/src/plugins/expression_error/public/expression_renderers/debug_renderer.tsx b/src/plugins/expression_error/public/expression_renderers/debug_renderer.tsx index e3cf86b67148f..d4c74a9c646e7 100644 --- a/src/plugins/expression_error/public/expression_renderers/debug_renderer.tsx +++ b/src/plugins/expression_error/public/expression_renderers/debug_renderer.tsx @@ -8,9 +8,13 @@ import { render, unmountComponentAtNode } from 'react-dom'; import React from 'react'; +import { Observable } from 'rxjs'; +import { CoreTheme } from 'kibana/public'; import { ExpressionRenderDefinition } from 'src/plugins/expressions/common'; import { i18n } from '@kbn/i18n'; -import { withSuspense } from '../../../../../src/plugins/presentation_util/public'; +import { CoreSetup } from '../../../../core/public'; +import { KibanaThemeProvider } from '../../../kibana_react/public'; +import { withSuspense, defaultTheme$ } from '../../../../../src/plugins/presentation_util/public'; import { LazyDebugRenderComponent } from '../components'; import { JSON } from '../../common'; @@ -30,13 +34,22 @@ const strings = { }), }; -export const debugRenderer = (): ExpressionRenderDefinition => ({ - name: 'debug', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render(domNode, config, handlers) { - handlers.onDestroy(() => unmountComponentAtNode(domNode)); - render(, domNode); - }, -}); +export const getDebugRenderer = + (theme$: Observable = defaultTheme$) => + (): ExpressionRenderDefinition => ({ + name: 'debug', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render(domNode, config, handlers) { + handlers.onDestroy(() => unmountComponentAtNode(domNode)); + render( + + + , + domNode + ); + }, + }); + +export const debugRendererFactory = (core: CoreSetup) => getDebugRenderer(core.theme.theme$); diff --git a/src/plugins/expression_error/public/expression_renderers/error_renderer.tsx b/src/plugins/expression_error/public/expression_renderers/error_renderer.tsx index f0fbed22f38a0..65847a18d4e0a 100644 --- a/src/plugins/expression_error/public/expression_renderers/error_renderer.tsx +++ b/src/plugins/expression_error/public/expression_renderers/error_renderer.tsx @@ -5,12 +5,17 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; +import { Observable } from 'rxjs'; +import { CoreTheme } from 'kibana/public'; import { I18nProvider } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; -import { withSuspense } from '../../../presentation_util/public'; +import { CoreSetup } from '../../../../core/public'; +import { KibanaThemeProvider } from '../../../kibana_react/public'; +import { withSuspense, defaultTheme$ } from '../../../presentation_util/public'; import { ErrorRendererConfig } from '../../common/types'; import { LazyErrorRenderComponent } from '../components'; @@ -27,25 +32,31 @@ const errorStrings = { const ErrorComponent = withSuspense(LazyErrorRenderComponent); -export const errorRenderer = (): ExpressionRenderDefinition => ({ - name: 'error', - displayName: errorStrings.getDisplayName(), - help: errorStrings.getHelpDescription(), - reuseDomNode: true, - render: async ( - domNode: HTMLElement, - config: ErrorRendererConfig, - handlers: IInterpreterRenderHandlers - ) => { - handlers.onDestroy(() => { - unmountComponentAtNode(domNode); - }); +export const getErrorRenderer = + (theme$: Observable = defaultTheme$) => + (): ExpressionRenderDefinition => ({ + name: 'error', + displayName: errorStrings.getDisplayName(), + help: errorStrings.getHelpDescription(), + reuseDomNode: true, + render: async ( + domNode: HTMLElement, + config: ErrorRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render( + + + + + , + domNode + ); + }, + }); - render( - - - , - domNode - ); - }, -}); +export const errorRendererFactory = (core: CoreSetup) => getErrorRenderer(core.theme.theme$); diff --git a/src/plugins/expression_error/public/expression_renderers/index.ts b/src/plugins/expression_error/public/expression_renderers/index.ts index 237ee5644cdc0..295e30fc1bffd 100644 --- a/src/plugins/expression_error/public/expression_renderers/index.ts +++ b/src/plugins/expression_error/public/expression_renderers/index.ts @@ -6,9 +6,5 @@ * Side Public License, v 1. */ -import { errorRenderer } from './error_renderer'; -import { debugRenderer } from './debug_renderer'; - -export const renderers = [errorRenderer, debugRenderer]; - -export { errorRenderer, debugRenderer }; +export { getErrorRenderer, errorRendererFactory } from './error_renderer'; +export { getDebugRenderer, debugRendererFactory } from './debug_renderer'; diff --git a/src/plugins/expression_error/public/index.ts b/src/plugins/expression_error/public/index.ts index be34980045395..acc5133f5b7d1 100755 --- a/src/plugins/expression_error/public/index.ts +++ b/src/plugins/expression_error/public/index.ts @@ -6,9 +6,6 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/110893 -/* eslint-disable @kbn/eslint/no_export_all */ - import { ExpressionErrorPlugin } from './plugin'; export type { ExpressionErrorPluginSetup, ExpressionErrorPluginStart } from './plugin'; @@ -17,5 +14,11 @@ export function plugin() { return new ExpressionErrorPlugin(); } -export * from './expression_renderers'; +export { + getErrorRenderer, + getDebugRenderer, + errorRendererFactory, + debugRendererFactory, +} from './expression_renderers'; + export { LazyDebugComponent, LazyErrorComponent } from './components'; diff --git a/src/plugins/expression_error/public/plugin.ts b/src/plugins/expression_error/public/plugin.ts index 0b82ccf5d2dba..de3793b6a30ae 100755 --- a/src/plugins/expression_error/public/plugin.ts +++ b/src/plugins/expression_error/public/plugin.ts @@ -8,7 +8,7 @@ import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; -import { errorRenderer, debugRenderer } from './expression_renderers'; +import { debugRendererFactory, errorRendererFactory } from './expression_renderers'; interface SetupDeps { expressions: ExpressionsSetup; @@ -25,8 +25,8 @@ export class ExpressionErrorPlugin implements Plugin { public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionErrorPluginSetup { - expressions.registerRenderer(errorRenderer); - expressions.registerRenderer(debugRenderer); + expressions.registerRenderer(errorRendererFactory(core)); + expressions.registerRenderer(debugRendererFactory(core)); } public start(core: CoreStart): ExpressionErrorPluginStart {} diff --git a/x-pack/plugins/canvas/public/lib/default_theme.ts b/src/plugins/presentation_util/common/lib/utils/default_theme.ts similarity index 66% rename from x-pack/plugins/canvas/public/lib/default_theme.ts rename to src/plugins/presentation_util/common/lib/utils/default_theme.ts index 5256ba81c9b8a..c403937016ec8 100644 --- a/x-pack/plugins/canvas/public/lib/default_theme.ts +++ b/src/plugins/presentation_util/common/lib/utils/default_theme.ts @@ -1,9 +1,11 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ + import { CoreTheme } from 'kibana/public'; import { Observable } from 'rxjs'; diff --git a/src/plugins/presentation_util/common/lib/utils/index.ts b/src/plugins/presentation_util/common/lib/utils/index.ts index 232ec09cf8b06..5fc7e31fdf9f5 100644 --- a/src/plugins/presentation_util/common/lib/utils/index.ts +++ b/src/plugins/presentation_util/common/lib/utils/index.ts @@ -10,6 +10,7 @@ export * from './dataurl'; export * from './httpurl'; export * from './resolve_dataurl'; export * from './url'; +export { defaultTheme$ } from './default_theme'; export async function getElasticLogo() { return await import('./elastic_logo'); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts index 94aadf6598b5a..572771537ec72 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -7,7 +7,10 @@ import { imageRenderer } from '../../../../../src/plugins/expression_image/public'; import { metricRenderer } from '../../../../../src/plugins/expression_metric/public'; -import { errorRenderer, debugRenderer } from '../../../../../src/plugins/expression_error/public'; +import { + errorRendererFactory, + debugRendererFactory, +} from '../../../../../src/plugins/expression_error/public'; import { repeatImageRenderer } from '../../../../../src/plugins/expression_repeat_image/public'; import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public'; import { @@ -16,8 +19,6 @@ import { } from '../../../../../src/plugins/expression_shape/public'; export const renderFunctions = [ - debugRenderer, - errorRenderer, imageRenderer, metricRenderer, revealImageRenderer, @@ -26,4 +27,4 @@ export const renderFunctions = [ progressRenderer, ]; -export const renderFunctionFactories = []; +export const renderFunctionFactories = [debugRendererFactory, errorRendererFactory]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/markdown/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/markdown/index.tsx index bc9748fa8f6ab..498e09e14b7e0 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/markdown/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/markdown/index.tsx @@ -10,12 +10,12 @@ import ReactDOM from 'react-dom'; import { CoreTheme } from 'kibana/public'; import { Observable } from 'rxjs'; import { KibanaThemeProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { defaultTheme$ } from '../../../../../../src/plugins/presentation_util/common/lib'; import { StartInitializer } from '../../plugin'; import { RendererStrings } from '../../../i18n'; import { Return as Config } from '../../functions/browser/markdown'; import { Markdown } from '../../../../../../src/plugins/kibana_react/public'; import { RendererFactory } from '../../../types'; -import { defaultTheme$ } from '../../../public/lib/default_theme'; const { markdown: strings } = RendererStrings; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/table.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/table.tsx index 3af0fe00f2465..ec918f89321ad 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/table.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/table.tsx @@ -10,11 +10,11 @@ import React from 'react'; import { CoreTheme } from 'kibana/public'; import { Observable } from 'rxjs'; import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; +import { defaultTheme$ } from '../../../../../src/plugins/presentation_util/common/lib'; import { StartInitializer } from '../plugin'; import { Datatable as DatatableComponent } from '../../public/components/datatable'; import { RendererStrings } from '../../i18n'; import { RendererFactory, Style, Datatable } from '../../types'; -import { defaultTheme$ } from '../../public/lib/default_theme'; const { dropdownFilter: strings } = RendererStrings; export interface TableArguments { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/text.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/text.tsx index 3bc62d888e02f..a89c56c66ce27 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/text.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/text.tsx @@ -10,10 +10,10 @@ import React from 'react'; import { CoreTheme } from 'kibana/public'; import { Observable } from 'rxjs'; import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; +import { defaultTheme$ } from '../../../../../src/plugins/presentation_util/common/lib'; import { StartInitializer } from '../plugin'; import { RendererStrings } from '../../i18n'; import { RendererFactory } from '../../types'; -import { defaultTheme$ } from '../../public/lib/default_theme'; const { text: strings } = RendererStrings; diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index db61cfaf86c51..1d2326b77dd26 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -12,8 +12,8 @@ import { getTableRenderer } from '../canvas_plugin_src/renderers/table'; import { getTextRenderer } from '../canvas_plugin_src/renderers/text'; import { imageRenderer as image } from '../../../../src/plugins/expression_image/public'; import { - errorRenderer as error, - debugRenderer as debug, + getErrorRenderer, + getDebugRenderer, } from '../../../../src/plugins/expression_error/public'; import { repeatImageRenderer as repeatImage } from '../../../../src/plugins/expression_repeat_image/public'; import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public'; @@ -25,7 +25,13 @@ import { metricRenderer as metric } from '../../../../src/plugins/expression_met const unboxFactory = (factory) => factory(); -const renderFunctionsFactories = [getMarkdownRenderer, getTextRenderer, getTableRenderer]; +const renderFunctionsFactories = [ + getMarkdownRenderer, + getTextRenderer, + getTableRenderer, + getErrorRenderer, + getDebugRenderer, +]; /** * This is a collection of renderers which are bundled with the runtime. If @@ -33,8 +39,6 @@ const renderFunctionsFactories = [getMarkdownRenderer, getTextRenderer, getTable * not render. This includes any plugins. */ export const renderFunctions = [ - debug, - error, image, repeatImage, revealImage, From 7033517ced3a333ce421ff65926cba7273c0d4ba Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Fri, 3 Dec 2021 09:36:38 +0100 Subject: [PATCH 29/65] [navSearch] handle `displayName` for `savedObjects` result provider (#119442) * [navSearch] use the type's displayName in the `savedObjects` result provider * add unit tests * update documentation * adapt unit tests * address review comments and start to cleanup searchbar component * wrap onChange with useCallback * add unit tests for resultToOption Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/user/introduction.asciidoc | 2 +- .../public/components/popover_footer.tsx | 78 ++++ .../public/components/popover_placeholder.tsx | 44 +++ .../public/components/result_tag_list.tsx | 70 ++++ .../public/components/search_bar.tsx | 344 ++++-------------- .../global_search_bar/public/lib/index.ts | 9 + .../public/lib/result_to_option.test.ts | 92 +++++ .../public/lib/result_to_option.tsx | 48 +++ .../public/lib/suggestion_to_option.ts | 24 ++ .../get_searchable_types.test.ts | 130 +++++++ .../saved_objects/get_searchable_types.ts | 29 ++ .../map_object_to_result.test.ts | 12 +- .../saved_objects/map_object_to_result.ts | 1 + .../providers/saved_objects/provider.test.ts | 4 +- .../providers/saved_objects/provider.ts | 12 +- 15 files changed, 617 insertions(+), 282 deletions(-) create mode 100644 x-pack/plugins/global_search_bar/public/components/popover_footer.tsx create mode 100644 x-pack/plugins/global_search_bar/public/components/popover_placeholder.tsx create mode 100644 x-pack/plugins/global_search_bar/public/components/result_tag_list.tsx create mode 100644 x-pack/plugins/global_search_bar/public/lib/index.ts create mode 100644 x-pack/plugins/global_search_bar/public/lib/result_to_option.test.ts create mode 100644 x-pack/plugins/global_search_bar/public/lib/result_to_option.tsx create mode 100644 x-pack/plugins/global_search_bar/public/lib/suggestion_to_option.ts create mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects/get_searchable_types.test.ts create mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects/get_searchable_types.ts diff --git a/docs/user/introduction.asciidoc b/docs/user/introduction.asciidoc index 89a21b0424ed0..fa5801e622706 100644 --- a/docs/user/introduction.asciidoc +++ b/docs/user/introduction.asciidoc @@ -233,7 +233,7 @@ To get the most from the search feature, follow these tips: |Search by type |`type:dashboard` -Available types: `application`, `canvas-workpad`, `dashboard`, `index-pattern`, `lens`, `maps`, `query`, `search`, `visualization` +Available types: `application`, `canvas-workpad`, `dashboard`, `data-view`, `lens`, `maps`, `query`, `search`, `visualization` |Search by tag |`tag:mytagname` + diff --git a/x-pack/plugins/global_search_bar/public/components/popover_footer.tsx b/x-pack/plugins/global_search_bar/public/components/popover_footer.tsx new file mode 100644 index 0000000000000..da7f8997a16b6 --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/components/popover_footer.tsx @@ -0,0 +1,78 @@ +/* + * 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, { FC } from 'react'; +import { EuiCode, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +interface PopoverFooterProps { + isMac: boolean; +} + +export const PopoverFooter: FC = ({ isMac }) => { + return ( + + + +

+ +   + type:  + +   + tag: +

+
+
+ + +

+ + ), + commandDescription: ( + + {isMac ? ( + + ) : ( + + )} + + ), + }} + /> +

+
+
+
+ ); +}; diff --git a/x-pack/plugins/global_search_bar/public/components/popover_placeholder.tsx b/x-pack/plugins/global_search_bar/public/components/popover_placeholder.tsx new file mode 100644 index 0000000000000..2dd191a7bf672 --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/components/popover_placeholder.tsx @@ -0,0 +1,44 @@ +/* + * 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, { FC } from 'react'; +import { EuiImage, EuiSelectableMessage, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +interface PopoverPlaceholderProps { + darkMode: boolean; + basePath: string; +} + +export const PopoverPlaceholder: FC = ({ basePath, darkMode }) => { + return ( + + + +

+ +

+
+

+ +

+
+ ); +}; diff --git a/x-pack/plugins/global_search_bar/public/components/result_tag_list.tsx b/x-pack/plugins/global_search_bar/public/components/result_tag_list.tsx new file mode 100644 index 0000000000000..4cc75d207e287 --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/components/result_tag_list.tsx @@ -0,0 +1,70 @@ +/* + * 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, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiBadge } from '@elastic/eui'; +import type { Tag } from '../../../saved_objects_tagging/public'; + +const MAX_TAGS_TO_SHOW = 3; + +const TagListWrapper: FC = ({ children }) => ( +
    + {children} +
+); + +const buildListItem = ({ color, name, id }: Tag) => { + return ( +
  • + {name} +
  • + ); +}; + +interface ResultTagListProps { + tags: Tag[]; + searchTagIds: string[]; +} + +export const ResultTagList: FC = ({ tags, searchTagIds }) => { + const showOverflow = tags.length > MAX_TAGS_TO_SHOW; + + if (!showOverflow) { + return {tags.map(buildListItem)}; + } + + // float searched tags to the start of the list, actual order doesn't matter + tags.sort((a) => { + if (searchTagIds.find((id) => id === a.id)) return -1; + return 1; + }); + + const overflowList = tags.splice(MAX_TAGS_TO_SHOW); + const overflowMessage = i18n.translate('xpack.globalSearchBar.searchbar.overflowTagsAriaLabel', { + defaultMessage: '{n} more {n, plural, one {tag} other {tags}}: {tags}', + values: { + n: overflowList.length, + // @ts-ignore-line + tags: overflowList.map(({ name }) => name), + }, + }); + + return ( + + {tags.map(buildListItem)} +
  • + +{overflowList.length} +
  • +
    + ); +}; diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index fbf412ca704d8..995f685d4287f 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -5,48 +5,34 @@ * 2.0. */ +import React, { FC, useCallback, useRef, useState, useEffect } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; +import useEvent from 'react-use/lib/useEvent'; +import useMountedState from 'react-use/lib/useMountedState'; +import { Subscription } from 'rxjs'; import { - EuiCode, - EuiFlexGroup, - EuiFlexItem, EuiHeaderSectionItemButton, EuiIcon, - EuiImage, - EuiSelectableMessage, EuiSelectableTemplateSitewide, EuiSelectableTemplateSitewideOption, - EuiText, - EuiBadge, euiSelectableTemplateSitewideRenderOptions, } from '@elastic/eui'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { ApplicationStart } from 'kibana/public'; -import React, { ReactNode, useCallback, useRef, useState, useEffect } from 'react'; -import useDebounce from 'react-use/lib/useDebounce'; -import useEvent from 'react-use/lib/useEvent'; -import useMountedState from 'react-use/lib/useMountedState'; -import { Subscription } from 'rxjs'; -import { +import type { ApplicationStart } from 'kibana/public'; +import type { GlobalSearchPluginStart, GlobalSearchResult, GlobalSearchFindParams, } from '../../../global_search/public'; -import { SavedObjectTaggingPluginStart, Tag } from '../../../saved_objects_tagging/public'; +import type { SavedObjectTaggingPluginStart } from '../../../saved_objects_tagging/public'; import { parseSearchParams } from '../search_syntax'; import { getSuggestions, SearchSuggestion } from '../suggestions'; +import { resultToOption, suggestionToOption } from '../lib'; +import { PopoverFooter } from './popover_footer'; +import { PopoverPlaceholder } from './popover_placeholder'; import './search_bar.scss'; -interface Props { - globalSearch: GlobalSearchPluginStart; - navigateToUrl: ApplicationStart['navigateToUrl']; - trackUiMetric: (metricType: UiCounterMetricType, eventName: string | string[]) => void; - taggingApi?: SavedObjectTaggingPluginStart; - basePathUrl: string; - darkMode: boolean; -} - const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; const setFieldValue = (field: HTMLInputElement, value: string) => { @@ -60,7 +46,6 @@ const setFieldValue = (field: HTMLInputElement, value: string) => { const clearField = (field: HTMLInputElement) => setFieldValue(field, ''); -const cleanMeta = (str: string) => (str.charAt(0).toUpperCase() + str.slice(1)).replace(/-/g, ' '); const blurEvent = new FocusEvent('blur'); const sortByScore = (a: GlobalSearchResult, b: GlobalSearchResult): number => { @@ -77,108 +62,23 @@ const sortByTitle = (a: GlobalSearchResult, b: GlobalSearchResult): number => { return 0; }; -const TagListWrapper = ({ children }: { children: ReactNode }) => ( -
      - {children} -
    -); - -const buildListItem = ({ color, name, id }: Tag) => { - return ( -
  • - {name} -
  • - ); -}; - -const tagList = (tags: Tag[], searchTagIds: string[]) => { - const TAGS_TO_SHOW = 3; - const showOverflow = tags.length > TAGS_TO_SHOW; - - if (!showOverflow) return {tags.map(buildListItem)}; - - // float searched tags to the start of the list, actual order doesn't matter - tags.sort((a) => { - if (searchTagIds.find((id) => id === a.id)) return -1; - return 1; - }); - - const overflowList = tags.splice(TAGS_TO_SHOW); - const overflowMessage = i18n.translate('xpack.globalSearchBar.searchbar.overflowTagsAriaLabel', { - defaultMessage: '{n} more {n, plural, one {tag} other {tags}}: {tags}', - values: { - n: overflowList.length, - // @ts-ignore-line - tags: overflowList.map(({ name }) => name), - }, - }); - - return ( - - {tags.map(buildListItem)} -
  • - +{overflowList.length} -
  • -
    - ); -}; - -const resultToOption = ( - result: GlobalSearchResult, - searchTagIds: string[], - getTag?: SavedObjectTaggingPluginStart['ui']['getTag'] -): EuiSelectableTemplateSitewideOption => { - const { id, title, url, icon, type, meta = {} } = result; - const { tagIds = [], categoryLabel = '' } = meta as { tagIds: string[]; categoryLabel: string }; - // only displaying icons for applications and integrations - const useIcon = type === 'application' || type === 'integration'; - const option: EuiSelectableTemplateSitewideOption = { - key: id, - label: title, - url, - type, - icon: { type: useIcon && icon ? icon : 'empty' }, - 'data-test-subj': `nav-search-option`, - }; - - if (type === 'application') option.meta = [{ text: categoryLabel }]; - else option.meta = [{ text: cleanMeta(type) }]; - - if (getTag && tagIds.length) { - // TODO #85189 - refactor to use TagList instead of getTag - // Casting to Tag[] because we know all our IDs will be valid here, no need to check for undefined - option.append = tagList(tagIds.map(getTag) as Tag[], searchTagIds); - } - - return option; -}; - -const suggestionToOption = (suggestion: SearchSuggestion): EuiSelectableTemplateSitewideOption => { - const { key, label, description, icon, suggestedSearch } = suggestion; - return { - key, - label, - type: '__suggestion__', - icon: { type: icon }, - suggestion: suggestedSearch, - meta: [{ text: description }], - 'data-test-subj': `nav-search-option`, - }; -}; +interface SearchBarProps { + globalSearch: GlobalSearchPluginStart; + navigateToUrl: ApplicationStart['navigateToUrl']; + trackUiMetric: (metricType: UiCounterMetricType, eventName: string | string[]) => void; + taggingApi?: SavedObjectTaggingPluginStart; + basePathUrl: string; + darkMode: boolean; +} -export function SearchBar({ +export const SearchBar: FC = ({ globalSearch, taggingApi, navigateToUrl, trackUiMetric, basePathUrl, darkMode, -}: Props) { +}) => { const isMounted = useMountedState(); const [initialLoad, setInitialLoad] = useState(false); const [searchValue, setSearchValue] = useState(''); @@ -312,79 +212,59 @@ export function SearchBar({ [buttonRef, searchRef, trackUiMetric] ); - const onChange = (selection: EuiSelectableTemplateSitewideOption[]) => { - const selected = selection.find(({ checked }) => checked === 'on'); - if (!selected) { - return; - } - - // @ts-ignore - ts error is "union type is too complex to express" - const { url, type, suggestion } = selected; + const onChange = useCallback( + (selection: EuiSelectableTemplateSitewideOption[]) => { + const selected = selection.find(({ checked }) => checked === 'on'); + if (!selected) { + return; + } - // if the type is a suggestion, we change the query on the input and trigger a new search - // by setting the searchValue (only setting the field value does not trigger a search) - if (type === '__suggestion__') { - setFieldValue(searchRef!, suggestion); - setSearchValue(suggestion); - return; - } + // @ts-ignore - ts error is "union type is too complex to express" + const { url, type, suggestion } = selected; - // errors in tracking should not prevent selection behavior - try { - if (type === 'application') { - const key = selected.keys ?? 'unknown'; - trackUiMetric(METRIC_TYPE.CLICK, [ - 'user_navigated_to_application', - `user_navigated_to_application_${key.toLowerCase().replaceAll(' ', '_')}`, // which application - ]); - } else { - trackUiMetric(METRIC_TYPE.CLICK, [ - 'user_navigated_to_saved_object', - `user_navigated_to_saved_object_${type}`, // which type of saved object - ]); + // if the type is a suggestion, we change the query on the input and trigger a new search + // by setting the searchValue (only setting the field value does not trigger a search) + if (type === '__suggestion__') { + setFieldValue(searchRef!, suggestion); + setSearchValue(suggestion); + return; } - } catch (e) { - // eslint-disable-next-line no-console - console.log('Error trying to track searchbar metrics', e); - } - navigateToUrl(url); + // errors in tracking should not prevent selection behavior + try { + if (type === 'application') { + const key = selected.keys ?? 'unknown'; + trackUiMetric(METRIC_TYPE.CLICK, [ + 'user_navigated_to_application', + `user_navigated_to_application_${key.toLowerCase().replaceAll(' ', '_')}`, // which application + ]); + } else { + trackUiMetric(METRIC_TYPE.CLICK, [ + 'user_navigated_to_saved_object', + `user_navigated_to_saved_object_${type}`, // which type of saved object + ]); + } + } catch (e) { + // eslint-disable-next-line no-console + console.log('Error trying to track searchbar metrics', e); + } - (document.activeElement as HTMLElement).blur(); - if (searchRef) { - clearField(searchRef); - searchRef.dispatchEvent(blurEvent); - } - }; + navigateToUrl(url); - const emptyMessage = ( - - - -

    - -

    -
    -

    - -

    -
    + (document.activeElement as HTMLElement).blur(); + if (searchRef) { + clearField(searchRef); + searchRef.dispatchEvent(blurEvent); + } + }, + [trackUiMetric, navigateToUrl, searchRef] ); + const emptyMessage = ; + const placeholderText = i18n.translate('xpack.globalSearchBar.searchBar.placeholder', { + defaultMessage: 'Search Elastic', + }); + useEvent('keydown', onKeyDown); return ( @@ -395,102 +275,38 @@ export function SearchBar({ popoverButtonBreakpoints={['xs', 's']} singleSelection={true} renderOption={(option) => euiSelectableTemplateSitewideRenderOptions(option, searchTerm)} - popoverButton={ - - - - } searchProps={{ onInput: (e: React.UIEvent) => setSearchValue(e.currentTarget.value), 'data-test-subj': 'nav-search-input', inputRef: setSearchRef, compressed: true, className: 'kbnSearchBar', - 'aria-label': i18n.translate('xpack.globalSearchBar.searchBar.placeholder', { - defaultMessage: 'Search Elastic', - }), - placeholder: i18n.translate('xpack.globalSearchBar.searchBar.placeholder', { - defaultMessage: 'Search Elastic', - }), + 'aria-label': placeholderText, + placeholder: placeholderText, onFocus: () => { trackUiMetric(METRIC_TYPE.COUNT, 'search_focus'); setInitialLoad(true); }, }} + emptyMessage={emptyMessage} + noMatchesMessage={emptyMessage} popoverProps={{ 'data-test-subj': 'nav-search-popover', panelClassName: 'navSearch__panel', repositionOnScroll: true, buttonRef: setButtonRef, }} - emptyMessage={emptyMessage} - noMatchesMessage={emptyMessage} - popoverFooter={ - - - -

    - -   - type:  - -   - tag: -

    -
    -
    - - -

    - - ), - commandDescription: ( - - {isMac ? ( - - ) : ( - - )} - - ), - }} - /> -

    -
    -
    -
    + + } + popoverFooter={} /> ); -} +}; diff --git a/x-pack/plugins/global_search_bar/public/lib/index.ts b/x-pack/plugins/global_search_bar/public/lib/index.ts new file mode 100644 index 0000000000000..91f4922c27e05 --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/lib/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { resultToOption } from './result_to_option'; +export { suggestionToOption } from './suggestion_to_option'; diff --git a/x-pack/plugins/global_search_bar/public/lib/result_to_option.test.ts b/x-pack/plugins/global_search_bar/public/lib/result_to_option.test.ts new file mode 100644 index 0000000000000..ab935522ab921 --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/lib/result_to_option.test.ts @@ -0,0 +1,92 @@ +/* + * 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 { GlobalSearchResult } from '../../../global_search/common/types'; +import { resultToOption } from './result_to_option'; + +const createSearchResult = (parts: Partial = {}): GlobalSearchResult => ({ + id: 'id', + title: 'title', + type: 'application', + icon: 'some-icon', + score: 100, + url: '/url', + meta: {}, + ...parts, +}); + +describe('resultToOption', () => { + it('converts the result to the expected format', () => { + const input = createSearchResult({}); + expect(resultToOption(input, [])).toEqual({ + key: input.id, + label: input.title, + url: input.url, + type: input.type, + icon: { type: expect.any(String) }, + 'data-test-subj': expect.any(String), + meta: expect.any(Array), + }); + }); + + it('uses icon for `application` type', () => { + const input = createSearchResult({ type: 'application', icon: 'app-icon' }); + expect(resultToOption(input, [])).toEqual( + expect.objectContaining({ + icon: { type: 'app-icon' }, + }) + ); + }); + + it('uses icon for `integration` type', () => { + const input = createSearchResult({ type: 'integration', icon: 'integ-icon' }); + expect(resultToOption(input, [])).toEqual( + expect.objectContaining({ + icon: { type: 'integ-icon' }, + }) + ); + }); + + it('does not use icon for other types', () => { + const input = createSearchResult({ type: 'dashboard', icon: 'dash-icon' }); + expect(resultToOption(input, [])).toEqual( + expect.objectContaining({ + icon: { type: 'empty' }, + }) + ); + }); + + it('uses the category label as meta for `application` type', () => { + const input = createSearchResult({ type: 'application', meta: { categoryLabel: 'category' } }); + expect(resultToOption(input, [])).toEqual( + expect.objectContaining({ + meta: [{ text: 'category' }], + }) + ); + }); + + it('uses the type as meta for non-`application` type', () => { + const input = createSearchResult({ type: 'dashboard', meta: { categoryLabel: 'category' } }); + expect(resultToOption(input, [])).toEqual( + expect.objectContaining({ + meta: [{ text: 'Dashboard' }], + }) + ); + }); + + it('uses the displayName as meta for non-`application` type when provided', () => { + const input = createSearchResult({ + type: 'dashboard', + meta: { categoryLabel: 'category', displayName: 'foo' }, + }); + expect(resultToOption(input, [])).toEqual( + expect.objectContaining({ + meta: [{ text: 'Foo' }], + }) + ); + }); +}); diff --git a/x-pack/plugins/global_search_bar/public/lib/result_to_option.tsx b/x-pack/plugins/global_search_bar/public/lib/result_to_option.tsx new file mode 100644 index 0000000000000..85bf99f5e5a6d --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/lib/result_to_option.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { EuiSelectableTemplateSitewideOption } from '@elastic/eui'; +import type { GlobalSearchResult } from '../../../global_search/common/types'; +import type { SavedObjectTaggingPluginStart, Tag } from '../../../saved_objects_tagging/public'; +import { ResultTagList } from '../components/result_tag_list'; + +const cleanMeta = (str: string) => (str.charAt(0).toUpperCase() + str.slice(1)).replace(/-/g, ' '); + +export const resultToOption = ( + result: GlobalSearchResult, + searchTagIds: string[], + getTag?: SavedObjectTaggingPluginStart['ui']['getTag'] +): EuiSelectableTemplateSitewideOption => { + const { id, title, url, icon, type, meta = {} } = result; + const { tagIds = [], categoryLabel = '' } = meta as { tagIds: string[]; categoryLabel: string }; + // only displaying icons for applications and integrations + const useIcon = type === 'application' || type === 'integration'; + const option: EuiSelectableTemplateSitewideOption = { + key: id, + label: title, + url, + type, + icon: { type: useIcon && icon ? icon : 'empty' }, + 'data-test-subj': `nav-search-option`, + }; + + option.meta = + type === 'application' + ? [{ text: categoryLabel }] + : [{ text: cleanMeta((meta.displayName as string) ?? type) }]; + + if (getTag && tagIds.length) { + // TODO #85189 - refactor to use TagList instead of getTag + // Casting to Tag[] because we know all our IDs will be valid here, no need to check for undefined + option.append = ( + + ); + } + + return option; +}; diff --git a/x-pack/plugins/global_search_bar/public/lib/suggestion_to_option.ts b/x-pack/plugins/global_search_bar/public/lib/suggestion_to_option.ts new file mode 100644 index 0000000000000..d0ce6364208bc --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/lib/suggestion_to_option.ts @@ -0,0 +1,24 @@ +/* + * 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 { EuiSelectableTemplateSitewideOption } from '@elastic/eui'; +import { SearchSuggestion } from '../suggestions'; + +export const suggestionToOption = ( + suggestion: SearchSuggestion +): EuiSelectableTemplateSitewideOption => { + const { key, label, description, icon, suggestedSearch } = suggestion; + return { + key, + label, + type: '__suggestion__', + icon: { type: icon }, + suggestion: suggestedSearch, + meta: [{ text: description }], + 'data-test-subj': `nav-search-option`, + }; +}; diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/get_searchable_types.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/get_searchable_types.test.ts new file mode 100644 index 0000000000000..3a8494cb7d0e8 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/get_searchable_types.test.ts @@ -0,0 +1,130 @@ +/* + * 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 { SavedObjectTypeRegistry } from '../../../../../../src/core/server'; +import { getSearchableTypes } from './get_searchable_types'; + +describe('getSearchableTypes', () => { + let registry: SavedObjectTypeRegistry; + + beforeEach(() => { + registry = new SavedObjectTypeRegistry(); + }); + + const registerType = ({ + name, + displayName, + hidden = false, + noSearchField = false, + noGetInAppUrl = false, + }: { + name: string; + displayName?: string; + hidden?: boolean; + noSearchField?: boolean; + noGetInAppUrl?: boolean; + }) => { + registry.registerType({ + name, + hidden, + management: { + displayName, + defaultSearchField: noSearchField ? undefined : 'title', + getInAppUrl: noGetInAppUrl + ? undefined + : () => ({ path: 'path', uiCapabilitiesPath: 'uiCapabilitiesPath' }), + }, + namespaceType: 'multiple', + mappings: { properties: {} }, + }); + }; + + it('returns registered types that match', () => { + registerType({ name: 'foo' }); + registerType({ name: 'bar' }); + registerType({ name: 'dolly' }); + + const matching = getSearchableTypes(registry, ['foo', 'dolly']).map((type) => type.name); + expect(matching).toEqual(['foo', 'dolly']); + }); + + it('ignores hidden types', () => { + registerType({ name: 'foo', hidden: true }); + registerType({ name: 'bar' }); + registerType({ name: 'dolly' }); + + const matching = getSearchableTypes(registry, ['foo', 'dolly']).map((type) => type.name); + expect(matching).toEqual(['dolly']); + }); + + it('ignores types without `defaultSearchField`', () => { + registerType({ name: 'foo' }); + registerType({ name: 'bar' }); + registerType({ name: 'dolly', noSearchField: true }); + + const matching = getSearchableTypes(registry, ['foo', 'dolly']).map((type) => type.name); + expect(matching).toEqual(['foo']); + }); + + it('ignores types without `getInAppUrl`', () => { + registerType({ name: 'foo' }); + registerType({ name: 'bar' }); + registerType({ name: 'dolly', noGetInAppUrl: true }); + + const matching = getSearchableTypes(registry, ['foo', 'dolly']).map((type) => type.name); + expect(matching).toEqual(['foo']); + }); + + it('matches ignoring case', () => { + registerType({ name: 'foo' }); + registerType({ name: 'bar' }); + registerType({ name: 'dolly' }); + + const matching = getSearchableTypes(registry, ['FOO', 'DolLy']).map((type) => type.name); + expect(matching).toEqual(['foo', 'dolly']); + }); + + it('matches against the display name when provided', () => { + registerType({ name: 'foo' }); + registerType({ name: 'bar', displayName: 'display' }); + registerType({ name: 'dolly', displayName: 'name' }); + + const matching = getSearchableTypes(registry, ['display', 'name']).map((type) => type.name); + expect(matching).toEqual(['bar', 'dolly']); + }); + + it('ignores cases against the display name', () => { + registerType({ name: 'foo' }); + registerType({ name: 'bar', displayName: 'display' }); + registerType({ name: 'dolly', displayName: 'name' }); + + const matching = getSearchableTypes(registry, ['DISPLAY', 'NaMe']).map((type) => type.name); + expect(matching).toEqual(['bar', 'dolly']); + }); + + it('replaces whitespaces with dashes when matching against the display name', () => { + registerType({ name: 'dashboard' }); + registerType({ name: 'index-pattern', displayName: 'data view' }); + registerType({ name: 'map', displayName: 'my super display name' }); + + const matching = getSearchableTypes(registry, ['data-view', 'my-super-display-name']).map( + (type) => type.name + ); + expect(matching).toEqual(['index-pattern', 'map']); + }); + + it('replaces whitespaces with dashes when matching against the name', () => { + registerType({ name: 'dashboard' }); + registerType({ name: 'index-pattern' }); + registerType({ name: 'new-map' }); + + const matching = getSearchableTypes(registry, ['index pattern', 'new map']).map( + (type) => type.name + ); + expect(matching).toEqual(['index-pattern', 'new-map']); + }); +}); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/get_searchable_types.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/get_searchable_types.ts new file mode 100644 index 0000000000000..fa8153dc161ca --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/get_searchable_types.ts @@ -0,0 +1,29 @@ +/* + * 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 { ISavedObjectTypeRegistry, SavedObjectsType } from 'src/core/server'; + +export const getSearchableTypes = (typeRegistry: ISavedObjectTypeRegistry, types?: string[]) => { + const typeFilter = types + ? (type: SavedObjectsType) => { + if (type.management?.displayName && isTypeMatching(types, type.management.displayName)) { + return true; + } + return isTypeMatching(types, type.name); + } + : () => true; + + return typeRegistry + .getVisibleTypes() + .filter(typeFilter) + .filter((type) => type.management?.defaultSearchField && type.management?.getInAppUrl); +}; + +const isTypeMatching = (list: string[], item: string) => + list.some((e) => toCompareFormat(e) === toCompareFormat(item)); + +const toCompareFormat = (str: string) => str.toLowerCase().replace(/\s/g, '-'); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts index 277be1ef8ac80..3b6011aeab7fc 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts @@ -44,6 +44,7 @@ describe('mapToResult', () => { const type = createType({ name: 'dashboard', management: { + displayName: 'dashDisplayName', defaultSearchField: 'title', icon: 'dashboardApp', getInAppUrl: (obj) => ({ path: `/dashboard/${obj.id}`, uiCapabilitiesPath: '' }), @@ -68,7 +69,7 @@ describe('mapToResult', () => { url: '/dashboard/dash1', icon: 'dashboardApp', score: 42, - meta: { tagIds: [] }, + meta: { tagIds: [], displayName: 'dashDisplayName' }, }); }); @@ -140,6 +141,7 @@ describe('mapToResults', () => { createType({ name: 'typeB', management: { + displayName: 'typeBDisplayName', defaultSearchField: 'description', getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'test.typeB' }), }, @@ -229,7 +231,7 @@ describe('mapToResults', () => { type: 'typeA', url: '/type-a/resultA', score: 100, - meta: { tagIds: [] }, + meta: { tagIds: [], displayName: 'typeA' }, }, { id: 'resultC', @@ -237,7 +239,7 @@ describe('mapToResults', () => { type: 'typeC', url: '/type-c/resultC', score: 42, - meta: { tagIds: ['1', '2'] }, + meta: { tagIds: ['1', '2'], displayName: 'typeC' }, }, { id: 'resultB', @@ -245,7 +247,7 @@ describe('mapToResults', () => { type: 'typeB', url: '/type-b/resultB', score: 69, - meta: { tagIds: [] }, + meta: { tagIds: [], displayName: 'typeBDisplayName' }, }, ]); }); @@ -283,7 +285,7 @@ describe('mapToResults', () => { type: 'typeA', url: '/type-a/resultA', score: 100, - meta: { tagIds: [] }, + meta: { tagIds: [], displayName: 'typeA' }, }, ]); }); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts index c96666dfbfe3c..871710abee11b 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts @@ -56,6 +56,7 @@ export const mapToResult = ( score: object.score, meta: { tagIds: object.references.filter((ref) => ref.type === 'tag').map(({ id }) => id), + displayName: type.management?.displayName ?? object.type, }, }; }; diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts index 2aa95fa5cb6ca..f332747f1ccfc 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts @@ -202,7 +202,7 @@ describe('savedObjectsResultProvider', () => { type: 'typeA', url: '/type-a/resultA', score: 50, - meta: { tagIds: [] }, + meta: { tagIds: [], displayName: 'typeA' }, }, { id: 'resultB', @@ -210,7 +210,7 @@ describe('savedObjectsResultProvider', () => { type: 'typeB', url: '/type-b/resultB', score: 78, - meta: { tagIds: [] }, + meta: { tagIds: [], displayName: 'typeB' }, }, ]); }); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts index 6557e6fe7b663..ae1f6da10bc6e 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts @@ -7,9 +7,10 @@ import { from, combineLatest, of } from 'rxjs'; import { map, takeUntil, first } from 'rxjs/operators'; -import { SavedObjectsFindOptionsReference, ISavedObjectTypeRegistry } from 'src/core/server'; +import { SavedObjectsFindOptionsReference } from 'src/core/server'; import { GlobalSearchResultProvider } from '../../../../global_search/server'; import { mapToResults } from './map_object_to_result'; +import { getSearchableTypes } from './get_searchable_types'; export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider => { return { @@ -58,13 +59,4 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = }; }; -const getSearchableTypes = (typeRegistry: ISavedObjectTypeRegistry, types?: string[]) => - typeRegistry - .getVisibleTypes() - .filter(types ? (type) => includeIgnoreCase(types, type.name) : () => true) - .filter((type) => type.management?.defaultSearchField && type.management?.getInAppUrl); - const uniq = (values: T[]): T[] => [...new Set(values)]; - -const includeIgnoreCase = (list: string[], item: string) => - list.find((e) => e.toLowerCase() === item.toLowerCase()) !== undefined; From cc46febd519f79731f26b485d92249d84944a42e Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Fri, 3 Dec 2021 09:07:49 +0000 Subject: [PATCH 30/65] Bump json-schema and uglify-js (#120142) --- package.json | 3 +++ src/dev/license_checker/config.ts | 1 + yarn.lock | 21 +++++++++------------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index a6dfd03893c81..1e5a135b723ed 100644 --- a/package.json +++ b/package.json @@ -79,9 +79,12 @@ "**/chokidar": "^3.4.3", "**/deepmerge": "^4.2.2", "**/fast-deep-equal": "^3.1.1", + "**/handlebars/uglify-js": "^3.14.3", "**/hoist-non-react-statics": "^3.3.2", + "**/html-minifier/uglify-js": "^3.14.3", "**/isomorphic-fetch/node-fetch": "^2.6.1", "**/istanbul-instrumenter-loader/schema-utils": "1.0.0", + "**/json-schema": "^0.4.0", "**/minimist": "^1.2.5", "**/node-jose/node-forge": "^0.10.0", "**/pdfkit/crypto-js": "4.0.0", diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 078786abb8c64..52b1f816090df 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -25,6 +25,7 @@ export const LICENSE_ALLOWED = [ '(MIT OR WTFPL)', '(Unlicense OR Apache-2.0)', 'AFLv2.1', + '(AFL-2.1 OR BSD-3-Clause)', 'Apache 2.0', 'Apache License, v2.0', 'Apache License, Version 2.0', diff --git a/yarn.lock b/yarn.lock index 9472271c20998..fc670fda132ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10093,7 +10093,7 @@ commander@2, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1, commander@^ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@2.17.x, commander@~2.17.1: +commander@2.17.x: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== @@ -17982,10 +17982,10 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@0.2.3, json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" @@ -27743,13 +27743,10 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg== -uglify-js@3.4.x, uglify-js@^3.1.4: - version "3.4.9" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" - integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q== - dependencies: - commander "~2.17.1" - source-map "~0.6.1" +uglify-js@3.4.x, uglify-js@^3.1.4, uglify-js@^3.14.3: + version "3.14.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.4.tgz#68756f17d1b90b9d289341736cb9a567d6882f90" + integrity sha512-AbiSR44J0GoCeV81+oxcy/jDOElO2Bx3d0MfQCUShq7JRXaM4KtQopZsq2vFv8bCq2yMaGrw1FgygUd03RyRDA== uglify-js@^2.6.2: version "2.8.29" From 3fed3a4360f58476cca4535cc799b35d40538a52 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 3 Dec 2021 11:17:38 +0200 Subject: [PATCH 31/65] [Canvas] Added KibanaThemeProvider to expression_metric. (#120078) * Added KibanaThemeProvider to metric and changed exports. * Added kibanaReact bundle. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/expression_metric/kibana.json | 3 +- .../__stories__/metric_renderer.stories.tsx | 16 ++--- .../public/expression_renderers/index.ts | 6 +- .../expression_renderers/metric_renderer.tsx | 64 +++++++++++-------- src/plugins/expression_metric/public/index.ts | 5 +- .../expression_metric/public/plugin.ts | 4 +- .../canvas_plugin_src/renderers/external.ts | 9 ++- .../shareable_runtime/supported_renderers.js | 4 +- 8 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/plugins/expression_metric/kibana.json b/src/plugins/expression_metric/kibana.json index 2aaef04e3bec3..3d844fa4de9fc 100755 --- a/src/plugins/expression_metric/kibana.json +++ b/src/plugins/expression_metric/kibana.json @@ -10,5 +10,6 @@ "server": true, "ui": true, "requiredPlugins": ["expressions", "presentationUtil"], - "optionalPlugins": [] + "optionalPlugins": [], + "requiredBundles": ["kibanaReact"] } diff --git a/src/plugins/expression_metric/public/expression_renderers/__stories__/metric_renderer.stories.tsx b/src/plugins/expression_metric/public/expression_renderers/__stories__/metric_renderer.stories.tsx index 0e04c32f52ba2..5835730e35f9b 100644 --- a/src/plugins/expression_metric/public/expression_renderers/__stories__/metric_renderer.stories.tsx +++ b/src/plugins/expression_metric/public/expression_renderers/__stories__/metric_renderer.stories.tsx @@ -9,7 +9,7 @@ import React, { CSSProperties } from 'react'; import { storiesOf } from '@storybook/react'; import { Style } from 'src/plugins/expressions'; -import { metricRenderer } from '../metric_renderer'; +import { getMetricRenderer } from '../metric_renderer'; import { Render } from '../../../../presentation_util/public/__stories__'; import { MetricRendererConfig } from '../../../common'; @@ -45,7 +45,7 @@ storiesOf('renderers/Metric', module) label: '', metricFormat: '', }; - return ; + return ; }) .add('with number metric', () => { const config: MetricRendererConfig = { @@ -55,7 +55,7 @@ storiesOf('renderers/Metric', module) label: '', metricFormat: '', }; - return ; + return ; }) .add('with string metric', () => { const config: MetricRendererConfig = { @@ -65,7 +65,7 @@ storiesOf('renderers/Metric', module) label: '', metricFormat: '', }; - return ; + return ; }) .add('with label', () => { const config: MetricRendererConfig = { @@ -75,7 +75,7 @@ storiesOf('renderers/Metric', module) label: 'Average price', metricFormat: '', }; - return ; + return ; }) .add('with number metric and a specified format', () => { const config: MetricRendererConfig = { @@ -85,7 +85,7 @@ storiesOf('renderers/Metric', module) label: 'Average price', metricFormat: '0.00%', }; - return ; + return ; }) .add('with formatted string metric and a specified format', () => { const config: MetricRendererConfig = { @@ -95,7 +95,7 @@ storiesOf('renderers/Metric', module) label: 'Total Revenue', metricFormat: '$0a', }; - return ; + return ; }) .add('with invalid metricFont', () => { const config: MetricRendererConfig = { @@ -105,5 +105,5 @@ storiesOf('renderers/Metric', module) label: 'Total Revenue', metricFormat: '$0a', }; - return ; + return ; }); diff --git a/src/plugins/expression_metric/public/expression_renderers/index.ts b/src/plugins/expression_metric/public/expression_renderers/index.ts index b77e0bb76f1fd..c8d6fa08147ea 100644 --- a/src/plugins/expression_metric/public/expression_renderers/index.ts +++ b/src/plugins/expression_metric/public/expression_renderers/index.ts @@ -6,8 +6,4 @@ * Side Public License, v 1. */ -import { metricRenderer } from './metric_renderer'; - -export const renderers = [metricRenderer]; - -export { metricRenderer }; +export { metricRendererFactory, getMetricRenderer } from './metric_renderer'; diff --git a/src/plugins/expression_metric/public/expression_renderers/metric_renderer.tsx b/src/plugins/expression_metric/public/expression_renderers/metric_renderer.tsx index 02c910640edeb..6a11910d4f26f 100644 --- a/src/plugins/expression_metric/public/expression_renderers/metric_renderer.tsx +++ b/src/plugins/expression_metric/public/expression_renderers/metric_renderer.tsx @@ -6,10 +6,14 @@ * Side Public License, v 1. */ import React, { CSSProperties, lazy } from 'react'; +import { Observable } from 'rxjs'; +import { CoreTheme } from 'kibana/public'; import { render, unmountComponentAtNode } from 'react-dom'; import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { i18n } from '@kbn/i18n'; -import { withSuspense } from '../../../presentation_util/public'; +import { CoreSetup } from '../../../../core/public'; +import { KibanaThemeProvider } from '../../../kibana_react/public'; +import { withSuspense, defaultTheme$ } from '../../../presentation_util/public'; import { MetricRendererConfig } from '../../common/types'; const strings = { @@ -26,30 +30,36 @@ const strings = { const LazyMetricComponent = lazy(() => import('../components/metric_component')); const MetricComponent = withSuspense(LazyMetricComponent); -export const metricRenderer = (): ExpressionRenderDefinition => ({ - name: 'metric', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render: async ( - domNode: HTMLElement, - config: MetricRendererConfig, - handlers: IInterpreterRenderHandlers - ) => { - handlers.onDestroy(() => { - unmountComponentAtNode(domNode); - }); +export const getMetricRenderer = + (theme$: Observable = defaultTheme$) => + (): ExpressionRenderDefinition => ({ + name: 'metric', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render: async ( + domNode: HTMLElement, + config: MetricRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); - render( - , - domNode, - () => handlers.done() - ); - }, -}); + render( + + + , + domNode, + () => handlers.done() + ); + }, + }); + +export const metricRendererFactory = (core: CoreSetup) => getMetricRenderer(core.theme.theme$); diff --git a/src/plugins/expression_metric/public/index.ts b/src/plugins/expression_metric/public/index.ts index 87499f279524d..8a23c2319a3c2 100755 --- a/src/plugins/expression_metric/public/index.ts +++ b/src/plugins/expression_metric/public/index.ts @@ -6,9 +6,6 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/110893 -/* eslint-disable @kbn/eslint/no_export_all */ - import { ExpressionMetricPlugin } from './plugin'; export type { ExpressionMetricPluginSetup, ExpressionMetricPluginStart } from './plugin'; @@ -17,4 +14,4 @@ export function plugin() { return new ExpressionMetricPlugin(); } -export * from './expression_renderers'; +export { metricRendererFactory, getMetricRenderer } from './expression_renderers'; diff --git a/src/plugins/expression_metric/public/plugin.ts b/src/plugins/expression_metric/public/plugin.ts index 8711a824fb7b5..6830fd904751c 100755 --- a/src/plugins/expression_metric/public/plugin.ts +++ b/src/plugins/expression_metric/public/plugin.ts @@ -9,7 +9,7 @@ import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; import { metricFunction } from '../common/expression_functions'; -import { metricRenderer } from './expression_renderers'; +import { metricRendererFactory } from './expression_renderers'; interface SetupDeps { expressions: ExpressionsSetup; @@ -27,7 +27,7 @@ export class ExpressionMetricPlugin { public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionMetricPluginSetup { expressions.registerFunction(metricFunction); - expressions.registerRenderer(metricRenderer); + expressions.registerRenderer(metricRendererFactory(core)); } public start(core: CoreStart): ExpressionMetricPluginStart {} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts index 572771537ec72..f97ac6e538575 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -6,7 +6,7 @@ */ import { imageRenderer } from '../../../../../src/plugins/expression_image/public'; -import { metricRenderer } from '../../../../../src/plugins/expression_metric/public'; +import { metricRendererFactory } from '../../../../../src/plugins/expression_metric/public'; import { errorRendererFactory, debugRendererFactory, @@ -20,11 +20,14 @@ import { export const renderFunctions = [ imageRenderer, - metricRenderer, revealImageRenderer, shapeRenderer, repeatImageRenderer, progressRenderer, ]; -export const renderFunctionFactories = [debugRendererFactory, errorRendererFactory]; +export const renderFunctionFactories = [ + debugRendererFactory, + errorRendererFactory, + metricRendererFactory, +]; diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index 1d2326b77dd26..b71e60e5ef0be 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -21,7 +21,7 @@ import { shapeRenderer as shape, progressRenderer as progress, } from '../../../../src/plugins/expression_shape/public'; -import { metricRenderer as metric } from '../../../../src/plugins/expression_metric/public'; +import { getMetricRenderer } from '../../../../src/plugins/expression_metric/public'; const unboxFactory = (factory) => factory(); @@ -31,6 +31,7 @@ const renderFunctionsFactories = [ getTableRenderer, getErrorRenderer, getDebugRenderer, + getMetricRenderer, ]; /** @@ -42,7 +43,6 @@ export const renderFunctions = [ image, repeatImage, revealImage, - metric, pie, plot, progress, From 680d0e19ccbf4b78faa8781caa1875c9794e78e9 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Fri, 3 Dec 2021 10:55:07 +0100 Subject: [PATCH 32/65] [Discover] Add new KibanaThemeProvider (#119784) * [Discover] Add new KibanaThemeProvider * Wrap remaining components in KibanaThemeProvider * Fix failing unit test * Fix failing unit test * Add missing theme Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/context/context_app.test.tsx | 6 +++- .../utils/use_context_app_fetch.test.ts | 6 +++- .../context/utils/use_context_app_fetch.tsx | 26 ++++++++++------ .../discover/public/application/index.tsx | 8 +++-- .../components/top_nav/get_top_nav_links.ts | 2 ++ .../top_nav/open_options_popover.tsx | 10 +++++-- .../top_nav/show_open_search_panel.tsx | 10 +++++-- .../application/not_found/not_found_route.tsx | 29 ++++++++++-------- .../embeddable/saved_search_embeddable.tsx | 30 ++++++++++++------- 9 files changed, 86 insertions(+), 41 deletions(-) diff --git a/src/plugins/discover/public/application/context/context_app.test.tsx b/src/plugins/discover/public/application/context/context_app.test.tsx index 7f78bb1c698ab..a31557124d49a 100644 --- a/src/plugins/discover/public/application/context/context_app.test.tsx +++ b/src/plugins/discover/public/application/context/context_app.test.tsx @@ -19,6 +19,7 @@ import { DiscoverServices } from '../../build_services'; import { indexPatternsMock } from '../../__mocks__/index_patterns'; import { act } from 'react-dom/test-utils'; import { uiSettingsMock } from '../../__mocks__/ui_settings'; +import { themeServiceMock } from '../../../../../core/public/mocks'; const mockFilterManager = createFilterManagerMock(); const mockNavigationPlugin = { ui: { TopNavMenu: mockTopNavMenu } }; @@ -60,7 +61,10 @@ describe('ContextApp test', () => { indexPatterns: indexPatternsMock, toastNotifications: { addDanger: () => {} }, navigation: mockNavigationPlugin, - core: { notifications: { toasts: [] } }, + core: { + notifications: { toasts: [] }, + theme: { theme$: themeServiceMock.createStartContract().theme$ }, + }, history: () => {}, fieldFormats: { getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), diff --git a/src/plugins/discover/public/application/context/utils/use_context_app_fetch.test.ts b/src/plugins/discover/public/application/context/utils/use_context_app_fetch.test.ts index cd7bcd810dc39..0fc8bd99c021d 100644 --- a/src/plugins/discover/public/application/context/utils/use_context_app_fetch.test.ts +++ b/src/plugins/discover/public/application/context/utils/use_context_app_fetch.test.ts @@ -21,6 +21,7 @@ import { import { indexPatternWithTimefieldMock } from '../../../__mocks__/index_pattern_with_timefield'; import { createContextSearchSourceStub } from '../services/_stubs'; import { IndexPattern } from '../../../../../data_views/common'; +import { themeServiceMock } from '../../../../../../core/public/mocks'; const mockFilterManager = createFilterManagerMock(); @@ -60,7 +61,10 @@ const initDefaults = (tieBreakerFields: string[], indexPatternId = 'the-index-pa }, }, toastNotifications: { addDanger: dangerNotification }, - core: { notifications: { toasts: [] } }, + core: { + notifications: { toasts: [] }, + theme: { theme$: themeServiceMock.createStartContract().theme$ }, + }, history: () => {}, filterManager: mockFilterManager, uiSettings: { diff --git a/src/plugins/discover/public/application/context/utils/use_context_app_fetch.tsx b/src/plugins/discover/public/application/context/utils/use_context_app_fetch.tsx index e5ed24d475497..fc5718abb43f0 100644 --- a/src/plugins/discover/public/application/context/utils/use_context_app_fetch.tsx +++ b/src/plugins/discover/public/application/context/utils/use_context_app_fetch.tsx @@ -11,7 +11,7 @@ import { CONTEXT_TIE_BREAKER_FIELDS_SETTING } from '../../../../common'; import { DiscoverServices } from '../../../build_services'; import { fetchAnchor } from '../services/anchor'; import { fetchSurroundingDocs, SurrDocType } from '../services/context'; -import { MarkdownSimple, toMountPoint } from '../../../../../kibana_react/public'; +import { MarkdownSimple, toMountPoint, wrapWithTheme } from '../../../../../kibana_react/public'; import { IndexPattern, SortDirection } from '../../../../../data/public'; import { ContextFetchState, @@ -42,7 +42,8 @@ export function useContextAppFetch({ useNewFieldsApi, services, }: ContextAppFetchProps) { - const { uiSettings: config, data, toastNotifications, filterManager } = services; + const { uiSettings: config, data, toastNotifications, filterManager, core } = services; + const { theme$ } = core.theme; const searchSource = useMemo(() => { return data.search.searchSource.createEmpty(); @@ -70,11 +71,14 @@ export function useContextAppFetch({ toastNotifications.addDanger({ title: errorTitle, text: toMountPoint( - - {i18n.translate('discover.context.invalidTieBreakerFiledSetting', { - defaultMessage: 'Invalid tie breaker field setting', - })} - + wrapWithTheme( + + {i18n.translate('discover.context.invalidTieBreakerFiledSetting', { + defaultMessage: 'Invalid tie breaker field setting', + })} + , + theme$ + ) ), }); return; @@ -93,7 +97,7 @@ export function useContextAppFetch({ setState(createError('anchorStatus', FailureReason.UNKNOWN, error)); toastNotifications.addDanger({ title: errorTitle, - text: toMountPoint({error.message}), + text: toMountPoint(wrapWithTheme({error.message}, theme$)), }); } }, [ @@ -104,6 +108,7 @@ export function useContextAppFetch({ anchorId, searchSource, useNewFieldsApi, + theme$, ]); const fetchSurroundingRows = useCallback( @@ -135,7 +140,9 @@ export function useContextAppFetch({ setState(createError(statusKey, FailureReason.UNKNOWN, error)); toastNotifications.addDanger({ title: errorTitle, - text: toMountPoint({error.message}), + text: toMountPoint( + wrapWithTheme({error.message}, theme$) + ), }); } }, @@ -148,6 +155,7 @@ export function useContextAppFetch({ indexPattern, toastNotifications, useNewFieldsApi, + theme$, ] ); diff --git a/src/plugins/discover/public/application/index.tsx b/src/plugins/discover/public/application/index.tsx index f6c7d60ed7db8..55407835c0a4b 100644 --- a/src/plugins/discover/public/application/index.tsx +++ b/src/plugins/discover/public/application/index.tsx @@ -8,11 +8,11 @@ import { i18n } from '@kbn/i18n'; import { getServices } from '../kibana_services'; import { discoverRouter } from './discover_router'; -import { toMountPoint } from '../../../kibana_react/public'; +import { toMountPoint, wrapWithTheme } from '../../../kibana_react/public'; export const renderApp = (element: HTMLElement) => { const services = getServices(); - const { history: getHistory, capabilities, chrome, data } = services; + const { history: getHistory, capabilities, chrome, data, core } = services; const history = getHistory(); if (!capabilities.discover.save) { @@ -26,7 +26,9 @@ export const renderApp = (element: HTMLElement) => { iconType: 'glasses', }); } - const unmount = toMountPoint(discoverRouter(services, history))(element); + const unmount = toMountPoint(wrapWithTheme(discoverRouter(services, history), core.theme.theme$))( + element + ); return () => { unmount(); diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.ts index 4b9d48a92e0f5..6e93bc40f7d7f 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.ts @@ -52,6 +52,7 @@ export const getTopNavLinks = ({ openOptionsPopover({ I18nContext: services.core.i18n.Context, anchorElement, + theme$: services.core.theme.theme$, }), testId: 'discoverOptionsButton', }; @@ -95,6 +96,7 @@ export const getTopNavLinks = ({ showOpenSearchPanel({ onOpenSavedSearch, I18nContext: services.core.i18n.Context, + theme$: services.core.theme.theme$, }), }; diff --git a/src/plugins/discover/public/application/main/components/top_nav/open_options_popover.tsx b/src/plugins/discover/public/application/main/components/top_nav/open_options_popover.tsx index 0d359c865220f..ea0cd804efec0 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/open_options_popover.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/open_options_popover.tsx @@ -8,7 +8,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { I18nStart } from 'kibana/public'; +import { CoreTheme, I18nStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { @@ -22,8 +22,10 @@ import { EuiTextAlign, } from '@elastic/eui'; import './open_options_popover.scss'; +import { Observable } from 'rxjs'; import { DOC_TABLE_LEGACY } from '../../../../../common'; import { getServices } from '../../../../kibana_services'; +import { KibanaThemeProvider } from '../../../../../../kibana_react/public'; const container = document.createElement('div'); let isOpen = false; @@ -125,9 +127,11 @@ function onClose() { export function openOptionsPopover({ I18nContext, anchorElement, + theme$, }: { I18nContext: I18nStart['Context']; anchorElement: HTMLElement; + theme$: Observable; }) { if (isOpen) { onClose(); @@ -139,7 +143,9 @@ export function openOptionsPopover({ const element = ( - + + + ); ReactDOM.render(element, container); diff --git a/src/plugins/discover/public/application/main/components/top_nav/show_open_search_panel.tsx b/src/plugins/discover/public/application/main/components/top_nav/show_open_search_panel.tsx index 1a9bfd7e30c57..d506de357675a 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/show_open_search_panel.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/show_open_search_panel.tsx @@ -8,17 +8,21 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { I18nStart } from 'kibana/public'; +import { CoreTheme, I18nStart } from 'kibana/public'; +import { Observable } from 'rxjs'; import { OpenSearchPanel } from './open_search_panel'; +import { KibanaThemeProvider } from '../../../../../../kibana_react/public'; let isOpen = false; export function showOpenSearchPanel({ I18nContext, onOpenSavedSearch, + theme$, }: { I18nContext: I18nStart['Context']; onOpenSavedSearch: (id: string) => void; + theme$: Observable; }) { if (isOpen) { return; @@ -35,7 +39,9 @@ export function showOpenSearchPanel({ document.body.appendChild(container); const element = ( - + + + ); ReactDOM.render(element, container); diff --git a/src/plugins/discover/public/application/not_found/not_found_route.tsx b/src/plugins/discover/public/application/not_found/not_found_route.tsx index 80e4e5c8057f6..7b42e85584428 100644 --- a/src/plugins/discover/public/application/not_found/not_found_route.tsx +++ b/src/plugins/discover/public/application/not_found/not_found_route.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { Redirect } from 'react-router-dom'; -import { toMountPoint } from '../../../../kibana_react/public'; +import { toMountPoint, wrapWithTheme } from '../../../../kibana_react/public'; import { DiscoverServices } from '../../build_services'; import { getUrlTracker } from '../../kibana_services'; @@ -39,17 +39,20 @@ export function NotFoundRoute(props: NotFoundRouteProps) { bannerId = core.overlays.banners.replace( bannerId, toMountPoint( - -

    - -

    -
    + wrapWithTheme( + +

    + +

    +
    , + core.theme.theme$ + ) ) ); @@ -59,7 +62,7 @@ export function NotFoundRoute(props: NotFoundRouteProps) { core.overlays.banners.remove(bannerId); } }, 15000); - }, [core.overlays.banners, history, urlForwarding]); + }, [core.overlays.banners, history, urlForwarding, core.theme.theme$]); return ; } diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index 4a7f0b1c36868..6d7e515e33fa4 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -48,6 +48,7 @@ import { VIEW_MODE } from '../components/view_mode_toggle'; import { updateSearchSource } from './utils/update_search_source'; import { FieldStatsTableSavedSearchEmbeddable } from '../application/main/components/field_stats_table'; import { ElasticSearchHit } from '../types'; +import { KibanaThemeProvider } from '../../../kibana_react/public'; export type SearchProps = Partial & Partial & { @@ -391,15 +392,17 @@ export class SavedSearchEmbeddable Array.isArray(searchProps.columns) ) { ReactDOM.render( - , + + + , domNode ); return; @@ -410,7 +413,14 @@ export class SavedSearchEmbeddable useLegacyTable, refs: domNode, }; - ReactDOM.render(, domNode); + if (searchProps.services) { + ReactDOM.render( + + + , + domNode + ); + } } public reload() { From 1928beade8b18003580ef6264cad9d178d0c8ba7 Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Fri, 3 Dec 2021 10:14:47 +0000 Subject: [PATCH 33/65] [Security Solution][Detections] Adds Rules monitoring table actions (#119644) [Security Solution][Detections] Adds actions for Rules monitoring table: single/bulk enable, disable, duplicate, export, remove (#119644) --- .../detection_rules/prebuilt_rules.spec.ts | 15 + .../cypress/screens/alerts_detection_rules.ts | 2 + .../rules/all_rules_tables/index.test.tsx | 122 ------ .../rules/all_rules_tables/index.tsx | 102 ----- .../rules/use_rule_status.test.tsx | 2 - .../rules/use_rule_status.tsx | 8 +- .../rules/all/columns.test.tsx | 4 - .../detection_engine/rules/all/columns.tsx | 392 ++++++++---------- .../detection_engine/rules/all/index.test.tsx | 2 - .../rules/all/popover_tooltip.tsx | 3 +- .../rules/all/rules_tables.tsx | 75 ++-- .../all/table_header_tooltip_cell.test.tsx | 36 ++ .../rules/all/table_header_tooltip_cell.tsx | 46 ++ 13 files changed, 334 insertions(+), 475 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/table_header_tooltip_cell.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/table_header_tooltip_cell.tsx diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts index b259c0f1d9e33..38f5eec836bd6 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts @@ -13,6 +13,8 @@ import { RULES_EMPTY_PROMPT, RULE_SWITCH, SHOWING_RULES_TEXT, + RULES_MONIROTING_TABLE, + SELECT_ALL_RULES_ON_PAGE_CHECKBOX, } from '../../screens/alerts_detection_rules'; import { goToManageAlertsDetectionRules, waitForAlertsIndexToBeCreated } from '../../tasks/alerts'; @@ -94,6 +96,19 @@ describe('Actions with prebuilt rules', () => { cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'false'); }); + it('Allows to activate all rules on a page and deactivate single one at monitoring table', () => { + cy.get(RULES_MONIROTING_TABLE).click(); + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + activateSelectedRules(); + waitForRuleToChangeStatus(); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); + + selectNumberOfRules(1); + cy.get(RULE_SWITCH).first().click(); + waitForRuleToChangeStatus(); + cy.get(RULE_SWITCH).first().should('have.attr', 'aria-checked', 'false'); + }); + it('Allows to delete all rules at once', () => { selectAllRules(); deleteSelectedRules(); diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts index 39e08e29bdc2a..2fff4c2e92676 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts @@ -97,3 +97,5 @@ export const RULE_DETAILS_DELETE_BTN = '[data-test-subj="rules-details-delete-ru export const ALERT_DETAILS_CELLS = '[data-test-subj="dataGridRowCell"]'; export const SERVER_SIDE_EVENT_COUNT = '[data-test-subj="server-side-event-count"]'; + +export const SELECT_ALL_RULES_ON_PAGE_CHECKBOX = '[data-test-subj="checkboxSelectAll"]'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.test.tsx deleted file mode 100644 index d1dfd6ccfd565..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.test.tsx +++ /dev/null @@ -1,122 +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, { useRef } from 'react'; -import { shallow } from 'enzyme'; - -import '../../../../common/mock/match_media'; -import { AllRulesTables } from './index'; -import { AllRulesTabs } from '../../../pages/detection_engine/rules/all'; - -describe('AllRulesTables', () => { - it('renders correctly', () => { - const Component = () => { - const ref = useRef(null); - - return ( - - ); - }; - const wrapper = shallow(); - - expect(wrapper.dive().find('[data-test-subj="rules-table"]')).toHaveLength(1); - }); - - it('renders rules tab when "selectedTab" is "rules"', () => { - const Component = () => { - const ref = useRef(null); - - return ( - - ); - }; - const wrapper = shallow(); - - expect(wrapper.dive().find('[data-test-subj="rules-table"]')).toHaveLength(1); - expect(wrapper.dive().find('[data-test-subj="monitoring-table"]')).toHaveLength(0); - }); - - it('renders monitoring tab when "selectedTab" is "monitoring"', () => { - const Component = () => { - const ref = useRef(null); - - return ( - - ); - }; - const wrapper = shallow(); - - expect(wrapper.dive().find('[data-test-subj="rules-table"]')).toHaveLength(0); - expect(wrapper.dive().find('[data-test-subj="monitoring-table"]')).toHaveLength(1); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.tsx deleted file mode 100644 index 82e54b5b86072..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.tsx +++ /dev/null @@ -1,102 +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 { - Direction, - EuiBasicTable, - EuiBasicTableColumn, - EuiEmptyPrompt, - EuiTableSelectionType, -} from '@elastic/eui'; - -import React, { memo } from 'react'; -import { Rule, Rules, RulesSortingFields } from '../../../containers/detection_engine/rules/types'; -import { AllRulesTabs } from '../../../pages/detection_engine/rules/all'; -import { - RulesColumns, - RuleStatusRowItemType, -} from '../../../pages/detection_engine/rules/all/columns'; -import * as i18n from '../../../pages/detection_engine/rules/translations'; -import { EuiBasicTableOnChange } from '../../../pages/detection_engine/rules/types'; - -export interface SortingType { - sort: { - field: RulesSortingFields; - direction: Direction; - }; -} - -interface AllRulesTablesProps { - euiBasicTableSelectionProps: EuiTableSelectionType; - hasPermissions: boolean; - monitoringColumns: Array>; - pagination: { - pageIndex: number; - pageSize: number; - totalItemCount: number; - pageSizeOptions: number[]; - }; - rules: Rules; - rulesColumns: RulesColumns[]; - rulesStatuses: RuleStatusRowItemType[]; - sorting: SortingType; - tableOnChangeCallback: ({ page, sort }: EuiBasicTableOnChange) => void; - tableRef?: React.MutableRefObject; - selectedTab: AllRulesTabs; -} - -const emptyPrompt = ( - {i18n.NO_RULES}} titleSize="xs" body={i18n.NO_RULES_BODY} /> -); - -export const AllRulesTablesComponent: React.FC = ({ - euiBasicTableSelectionProps, - hasPermissions, - monitoringColumns, - pagination, - rules, - rulesColumns, - rulesStatuses, - sorting, - tableOnChangeCallback, - tableRef, - selectedTab, -}) => { - return ( - <> - {selectedTab === AllRulesTabs.rules && ( - - )} - {selectedTab === AllRulesTabs.monitoring && ( - - )} - - ); -}; - -export const AllRulesTables = memo(AllRulesTablesComponent); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.test.tsx index 4d01e2ff00ec1..a5809ea776322 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.test.tsx @@ -178,8 +178,6 @@ describe('useRuleStatus', () => { }, failures: [], id: '12345678987654321', - activate: true, - name: 'Test rule', }, ], }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.tsx index 4f524886935cd..d0c75e08ae01b 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.tsx @@ -9,7 +9,7 @@ import { useEffect, useRef, useState } from 'react'; import { isNotFoundError } from '@kbn/securitysolution-t-grid'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; -import { RuleStatusRowItemType } from '../../../pages/detection_engine/rules/all/columns'; +import { EnhancedRuleStatus } from '../../../pages/detection_engine/rules/all/columns'; import { getRuleStatusById, getRulesStatusByIds } from './api'; import * as i18n from './translations'; import { RuleStatus, Rules } from './types'; @@ -18,7 +18,7 @@ type Func = (ruleId: string) => void; export type ReturnRuleStatus = [boolean, RuleStatus | null, Func | null]; export interface ReturnRulesStatuses { loading: boolean; - rulesStatuses: RuleStatusRowItemType[]; + rulesStatuses: EnhancedRuleStatus[]; } /** @@ -78,7 +78,7 @@ export const useRuleStatus = (id: string | undefined | null): ReturnRuleStatus = * */ export const useRulesStatuses = (rules: Rules): ReturnRulesStatuses => { - const [rulesStatuses, setRuleStatuses] = useState([]); + const [rulesStatuses, setRuleStatuses] = useState([]); const [loading, setLoading] = useState(false); const { addError } = useAppToasts(); @@ -98,8 +98,6 @@ export const useRulesStatuses = (rules: Rules): ReturnRulesStatuses => { setRuleStatuses( rules.map((rule) => ({ id: rule.id, - activate: rule.enabled, - name: rule.name, ...ruleStatusesResponse[rule.id], })) ); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.test.tsx index 3920aa40e1f15..59c09c415f50e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.test.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { scopedHistoryMock } from 'src/core/public/mocks'; import uuid from 'uuid'; import '../../../../../common/mock/match_media'; import { deleteRulesAction, duplicateRulesAction, editRuleAction } from './actions'; @@ -18,7 +17,6 @@ jest.mock('./actions', () => ({ editRuleAction: jest.fn(), })); -const history = scopedHistoryMock.create(); const duplicateRulesActionMock = duplicateRulesAction as jest.Mock; const deleteRulesActionMock = deleteRulesAction as jest.Mock; const editRuleActionMock = editRuleAction as jest.Mock; @@ -45,7 +43,6 @@ describe('AllRulesTable Columns', () => { const duplicateRulesActionObject = getActions( dispatch, dispatchToaster, - history, navigateToApp, reFetchRules, refetchPrePackagedRulesStatus, @@ -62,7 +59,6 @@ describe('AllRulesTable Columns', () => { const deleteRulesActionObject = getActions( dispatch, dispatchToaster, - history, navigateToApp, reFetchRules, refetchPrePackagedRulesStatus, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx index 235cdd9c740ee..d586f0e4856d4 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx @@ -10,12 +10,10 @@ import { EuiTableActionsColumnType, EuiText, EuiToolTip, - EuiIcon, EuiLink, EuiBadge, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import * as H from 'history'; import { sum } from 'lodash'; import React, { Dispatch } from 'react'; @@ -40,6 +38,7 @@ import { RulesTableAction } from '../../../../containers/detection_engine/rules/ import { LinkAnchor } from '../../../../../common/components/links'; import { getToolTipContent, canEditRuleWithActions } from '../../../../../common/utils/privileges'; import { PopoverTooltip } from './popover_tooltip'; +import { TableHeaderTooltipCell } from './table_header_tooltip_cell'; import { APP_UI_ID, @@ -48,18 +47,25 @@ import { } from '../../../../../../common/constants'; import { DocLinksStart, NavigateToAppOptions } from '../../../../../../../../../src/core/public'; +type FormatUrl = (path: string) => string; +type HasReadActionsPrivileges = + | boolean + | Readonly<{ + [x: string]: boolean; + }>; + +export type TableItem = Rule & Partial; +export type TableColumn = EuiBasicTableColumn | EuiTableActionsColumnType; + +const extractRuleFromRow = ({ current_status: _, failures, ...rule }: TableItem): Rule => rule; + export const getActions = ( dispatch: React.Dispatch, dispatchToaster: Dispatch, - history: H.History, navigateToApp: (appId: string, options?: NavigateToAppOptions | undefined) => Promise, reFetchRules: () => Promise, refetchPrePackagedRulesStatus: () => Promise, - actionsPrivileges: - | boolean - | Readonly<{ - [x: string]: boolean; - }> + actionsPrivileges: HasReadActionsPrivileges ) => [ { 'data-test-subj': 'editRuleAction', @@ -72,8 +78,9 @@ export const getActions = ( i18n.EDIT_RULE_SETTINGS ), icon: 'controlsHorizontal', - onClick: (rowItem: Rule) => editRuleAction(rowItem.id, navigateToApp), - enabled: (rowItem: Rule) => canEditRuleWithActions(rowItem, actionsPrivileges), + onClick: (rowItem: TableItem) => editRuleAction(rowItem.id, navigateToApp), + enabled: (rowItem: TableItem) => + canEditRuleWithActions(extractRuleFromRow(rowItem), actionsPrivileges), }, { 'data-test-subj': 'duplicateRuleAction', @@ -86,10 +93,10 @@ export const getActions = ( ) : ( i18n.DUPLICATE_RULE ), - enabled: (rowItem: Rule) => canEditRuleWithActions(rowItem, actionsPrivileges), - onClick: async (rowItem: Rule) => { + enabled: (rowItem: TableItem) => canEditRuleWithActions(rowItem, actionsPrivileges), + onClick: async (rowItem: TableItem) => { const createdRules = await duplicateRulesAction( - [rowItem], + [extractRuleFromRow(rowItem)], [rowItem.id], dispatch, dispatchToaster @@ -120,97 +127,139 @@ export const getActions = ( }, ]; -export type RuleStatusRowItemType = RuleStatus & { - name: string; +export type EnhancedRuleStatus = RuleStatus & { id: string; }; -export type RulesColumns = EuiBasicTableColumn | EuiTableActionsColumnType; -export type RulesStatusesColumns = EuiBasicTableColumn; -type FormatUrl = (path: string) => string; -interface GetColumns { + +interface GetColumnsProps { dispatch: React.Dispatch; - dispatchToaster: Dispatch; formatUrl: FormatUrl; - history: H.History; hasMlPermissions: boolean; hasPermissions: boolean; loadingRuleIds: string[]; navigateToApp: (appId: string, options?: NavigateToAppOptions | undefined) => Promise; + hasReadActionsPrivileges: HasReadActionsPrivileges; + dispatchToaster: Dispatch; reFetchRules: () => Promise; refetchPrePackagedRulesStatus: () => Promise; - hasReadActionsPrivileges: - | boolean - | Readonly<{ - [x: string]: boolean; - }>; + docLinks: DocLinksStart; } -export const getColumns = ({ - dispatch, - dispatchToaster, - formatUrl, - history, +const getColumnEnabled = ({ hasMlPermissions, + hasReadActionsPrivileges, + dispatch, hasPermissions, loadingRuleIds, +}: GetColumnsProps): TableColumn => ({ + field: 'enabled', + name: i18n.COLUMN_ACTIVATE, + render: (_, rule: TableItem) => ( + + + + ), + width: '95px', + sortable: true, +}); + +const getColumnRuleName = ({ navigateToApp, formatUrl }: GetColumnsProps): TableColumn => ({ + field: 'name', + name: i18n.COLUMN_RULE, + render: (value: Rule['name'], item: TableItem) => ( + + void }) => { + ev.preventDefault(); + navigateToApp(APP_UI_ID, { + deepLinkId: SecurityPageName.rules, + path: getRuleDetailsUrl(item.id), + }); + }} + href={formatUrl(getRuleDetailsUrl(item.id))} + > + {value} + + + ), + width: '38%', + sortable: true, + truncateText: true, +}); + +const getColumnTags = (): TableColumn => ({ + field: 'tags', + name: null, + align: 'center', + render: (tags: Rule['tags']) => { + if (tags.length === 0) { + return null; + } + + const renderItem = (tag: string, i: number) => ( + + {tag} + + ); + return ( + + ); + }, + width: '65px', + truncateText: true, +}); + +const getActionsColumns = ({ + hasPermissions, + hasReadActionsPrivileges, + dispatch, + dispatchToaster, navigateToApp, reFetchRules, refetchPrePackagedRulesStatus, - hasReadActionsPrivileges, -}: GetColumns): RulesColumns[] => { - const cols: RulesColumns[] = [ - { - field: 'name', - name: i18n.COLUMN_RULE, - render: (value: Rule['name'], item: Rule) => ( - - void }) => { - ev.preventDefault(); - navigateToApp(APP_UI_ID, { - deepLinkId: SecurityPageName.rules, - path: getRuleDetailsUrl(item.id), - }); - }} - href={formatUrl(getRuleDetailsUrl(item.id))} - > - {value} - - - ), - width: '38%', - sortable: true, - truncateText: true, - }, - { - field: 'tags', - name: null, - align: 'center', - render: (tags: Rule['tags']) => { - if (tags.length === 0) { - return null; - } +}: GetColumnsProps): TableColumn[] => + hasPermissions + ? [ + { + actions: getActions( + dispatch, + dispatchToaster, + navigateToApp, + reFetchRules, + refetchPrePackagedRulesStatus, + hasReadActionsPrivileges + ), + width: '40px', + } as EuiTableActionsColumnType, + ] + : []; - const renderItem = (tag: string, i: number) => ( - - {tag} - - ); - return ( - - ); - }, - width: '65px', - truncateText: true, - }, +export const getRulesColumns = (columnsProps: GetColumnsProps): TableColumn[] => { + return [ + getColumnRuleName(columnsProps), + getColumnTags(), { field: 'risk_score', name: i18n.COLUMN_RISK_SCORE, @@ -288,112 +337,41 @@ export const getColumns = ({ width: '65px', truncateText: true, }, - { - align: 'center', - field: 'enabled', - name: i18n.COLUMN_ACTIVATE, - render: (value: Rule['enabled'], item: Rule) => ( - - - - ), - sortable: true, - width: '95px', - truncateText: true, - }, - ]; - const actions: RulesColumns[] = [ - { - actions: getActions( - dispatch, - dispatchToaster, - history, - navigateToApp, - reFetchRules, - refetchPrePackagedRulesStatus, - hasReadActionsPrivileges - ), - width: '40px', - } as EuiTableActionsColumnType, + getColumnEnabled(columnsProps), + ...getActionsColumns(columnsProps), ]; - - return hasPermissions ? [...cols, ...actions] : cols; }; -export const getMonitoringColumns = ( - navigateToApp: (appId: string, options?: NavigateToAppOptions | undefined) => Promise, - formatUrl: FormatUrl, - docLinks: DocLinksStart -): RulesStatusesColumns[] => { - const cols: RulesStatusesColumns[] = [ - { - field: 'name', - name: i18n.COLUMN_RULE, - render: (value: RuleStatus['current_status']['status'], item: RuleStatusRowItemType) => { - return ( - - void }) => { - ev.preventDefault(); - navigateToApp(APP_UI_ID, { - deepLinkId: SecurityPageName.rules, - path: getRuleDetailsUrl(item.id), - }); - }} - href={formatUrl(getRuleDetailsUrl(item.id))} - > - {value} - - - ); - }, - width: '28%', - truncateText: true, - }, +export const getMonitoringColumns = (columnsProps: GetColumnsProps): TableColumn[] => { + const { docLinks } = columnsProps; + return [ + { ...getColumnRuleName(columnsProps), width: '28%' }, + getColumnTags(), { field: 'current_status.bulk_create_time_durations', name: ( - <> - {i18n.COLUMN_INDEXING_TIMES} - - - - + ), - render: (value: RuleStatus['current_status']['bulk_create_time_durations']) => ( + render: (value: RuleStatus['current_status']['bulk_create_time_durations'] | undefined) => ( {value?.length ? sum(value.map(Number)).toFixed() : getEmptyTagValue()} ), - width: '14%', + width: '16%', truncateText: true, }, { field: 'current_status.search_after_time_durations', name: ( - <> - {i18n.COLUMN_QUERY_TIMES} - - - - + ), - render: (value: RuleStatus['current_status']['search_after_time_durations']) => ( + render: (value: RuleStatus['current_status']['search_after_time_durations'] | undefined) => ( {value?.length ? sum(value.map(Number)).toFixed() : getEmptyTagValue()} @@ -404,28 +382,32 @@ export const getMonitoringColumns = ( { field: 'current_status.gap', name: ( - <> - {i18n.COLUMN_GAP} - - -

    - - {'see documentation'} - - ), - }} - /> -

    -
    -
    - + + + +

    + + {'see documentation'} + + ), + }} + /> +

    +
    +
    + + } + /> ), - render: (value: RuleStatus['current_status']['gap']) => ( + render: (value: RuleStatus['current_status']['gap'] | undefined) => ( {value ?? getEmptyTagValue()} @@ -436,7 +418,7 @@ export const getMonitoringColumns = ( { field: 'current_status.status', name: i18n.COLUMN_LAST_RESPONSE, - render: (value: RuleStatus['current_status']['status']) => ( + render: (value: RuleStatus['current_status']['status'] | undefined) => ( ), width: '12%', @@ -445,7 +427,7 @@ export const getMonitoringColumns = ( { field: 'current_status.status_date', name: i18n.COLUMN_LAST_COMPLETE_RUN, - render: (value: RuleStatus['current_status']['status_date']) => { + render: (value: RuleStatus['current_status']['status_date'] | undefined) => { return value == null ? ( getEmptyTagValue() ) : ( @@ -457,20 +439,10 @@ export const getMonitoringColumns = ( /> ); }, - width: '18%', + width: '16%', truncateText: true, }, - { - field: 'activate', - name: i18n.COLUMN_ACTIVATE, - render: (value: Rule['enabled']) => ( - - {value ? i18n.ACTIVE : i18n.INACTIVE} - - ), - width: '95px', - }, + getColumnEnabled(columnsProps), + ...getActionsColumns(columnsProps), ]; - - return cols; }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx index 200bf0c719320..487d0862cf467 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx @@ -166,8 +166,6 @@ describe('AllRules', () => { }, failures: [], id: '12345678987654321', - activate: true, - name: 'Test rule', }, ], }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/popover_tooltip.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/popover_tooltip.tsx index 5cf0a0c0b28fc..22bad4fffade9 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/popover_tooltip.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/popover_tooltip.tsx @@ -31,9 +31,10 @@ const PopoverTooltipComponent = ({ columnName, children }: PopoverTooltipProps) setIsPopoverOpen(!isPopoverOpen)} - size="s" + size="xs" color="primary" iconType="questionInCircle" + style={{ height: 'auto' }} /> } > diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx index bc2172a257635..54c2500d03b03 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx @@ -13,6 +13,7 @@ import { EuiProgress, EuiConfirmModal, EuiWindowEvent, + EuiEmptyPrompt, } from '@elastic/eui'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { debounce } from 'lodash/fp'; @@ -23,7 +24,6 @@ import { useRulesStatuses, CreatePreBuiltRules, FilterOptions, - Rule, RulesSortingFields, } from '../../../../containers/detection_engine/rules'; @@ -33,12 +33,11 @@ import { useKibana, useUiSetting$ } from '../../../../../common/lib/kibana'; import { useStateToaster } from '../../../../../common/components/toasters'; import { Loader } from '../../../../../common/components/loader'; import { PrePackagedRulesPrompt } from '../../../../components/rules/pre_packaged_rules/load_empty_prompt'; -import { AllRulesTables, SortingType } from '../../../../components/rules/all_rules_tables'; import { getPrePackagedRuleStatus } from '../helpers'; import * as i18n from '../translations'; import { EuiBasicTableOnChange } from '../types'; import { getBatchItems } from './batch_actions'; -import { getColumns, getMonitoringColumns } from './columns'; +import { getRulesColumns, getMonitoringColumns, TableItem } from './columns'; import { showRulesTable } from './helpers'; import { RulesTableFilters } from './rules_table_filters/rules_table_filters'; import { useMlCapabilities } from '../../../../../common/components/ml/hooks/use_ml_capabilities'; @@ -167,7 +166,7 @@ export const RulesTables = React.memo( }, [loadingRuleIds, loadingRulesAction]); const sorting = useMemo( - (): SortingType => ({ + () => ({ sort: { field: filterOptions.sortField, direction: filterOptions.sortOrder, @@ -265,12 +264,10 @@ export const RulesTables = React.memo( [updateOptions, setLastRefreshDate] ); - const rulesColumns = useMemo(() => { - return getColumns({ + const [rulesColumns, monitoringColumns] = useMemo(() => { + const props = { dispatch, - dispatchToaster, formatUrl, - history, hasMlPermissions, hasPermissions, loadingRuleIds: @@ -279,10 +276,14 @@ export const RulesTables = React.memo( ? loadingRuleIds : [], navigateToApp, + hasReadActionsPrivileges: hasActionsPrivileges, + dispatchToaster, + history, reFetchRules, refetchPrePackagedRulesStatus, - hasReadActionsPrivileges: hasActionsPrivileges, - }); + docLinks, + }; + return [getRulesColumns(props), getMonitoringColumns(props)]; }, [ dispatch, dispatchToaster, @@ -296,13 +297,9 @@ export const RulesTables = React.memo( loadingRulesAction, navigateToApp, reFetchRules, + docLinks, ]); - const monitoringColumns = useMemo( - () => getMonitoringColumns(navigateToApp, formatUrl, docLinks), - [navigateToApp, formatUrl, docLinks] - ); - useEffect(() => { setRefreshRulesData(reFetchRules); }, [reFetchRules, setRefreshRulesData]); @@ -332,8 +329,8 @@ export const RulesTables = React.memo( const euiBasicTableSelectionProps = useMemo( () => ({ - selectable: (item: Rule) => !loadingRuleIds.includes(item.id), - onSelectionChange: (selected: Rule[]) => { + selectable: (item: TableItem) => !loadingRuleIds.includes(item.id), + onSelectionChange: (selected: TableItem[]) => { /** * EuiBasicTable doesn't provide declarative API to control selected rows. * This limitation requires us to synchronize selection state manually using setSelection(). @@ -447,6 +444,25 @@ export const RulesTables = React.memo( [initLoading, prePackagedRuleStatus, rulesCustomInstalled] ); + const items = useMemo(() => { + const rulesStatusesMap = new Map(rulesStatuses.map((item) => [item.id, item])); + + return rules.map((rule) => { + return { + ...rule, + ...rulesStatusesMap.get(rule.id), + }; + }); + }, [rulesStatuses, rules]); + + const tableProps = + selectedTab === AllRulesTabs.rules + ? { + 'data-test-subj': 'rules-table', + columns: rulesColumns, + } + : { 'data-test-subj': 'monitoring-table', columns: monitoringColumns }; + return ( <> @@ -535,18 +551,23 @@ export const RulesTables = React.memo( onToggleSelectAll={toggleSelectAll} showBulkActions /> - {i18n.NO_RULES}} + titleSize="xs" + body={i18n.NO_RULES_BODY} + /> + } + onChange={tableOnChangeCallback} pagination={paginationMemo} - rules={rules} - rulesColumns={rulesColumns} - rulesStatuses={rulesStatuses} + ref={tableRef} + selection={euiBasicTableSelectionProps} sorting={sorting} - tableOnChangeCallback={tableOnChangeCallback} - tableRef={tableRef} + {...tableProps} /> )} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/table_header_tooltip_cell.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/table_header_tooltip_cell.test.tsx new file mode 100644 index 0000000000000..216dcc98c0e41 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/table_header_tooltip_cell.test.tsx @@ -0,0 +1,36 @@ +/* + * 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 { TableHeaderTooltipCell } from './table_header_tooltip_cell'; + +import { render, screen, fireEvent } from '@testing-library/react'; + +describe('Component TableHeaderTooltipCell', () => { + it('shoud render text with icon and tooltip', async () => { + render(); + + expect(screen.getByText('test title')).toBeInTheDocument(); + expect(screen.getByTestId('tableHeaderIcon')).toBeInTheDocument(); + + fireEvent.mouseOver(screen.getByTestId('tableHeaderIcon')); + expect(await screen.findByText('test tooltip content')).toBeInTheDocument(); + }); + + it('shoud render test element as custom tooltip', () => { + render( + } + /> + ); + + expect(screen.getByText('test title')).toBeInTheDocument(); + expect(screen.getByTestId('customTestTooltip')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/table_header_tooltip_cell.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/table_header_tooltip_cell.tsx new file mode 100644 index 0000000000000..3ec474974917b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/table_header_tooltip_cell.tsx @@ -0,0 +1,46 @@ +/* + * 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 { EuiToolTip, EuiIcon, EuiFlexGrid, EuiFlexItem } from '@elastic/eui'; + +interface Props { + title: string; + tooltipContent?: string; + customTooltip?: React.ReactNode; +} + +/** + * Table header cell component that includes icon(question mark) tooltip with additional details about column + * Icon tooltip will never be truncated and always be visible for user interaction + * @param title string - column header title + * @param tooltipContent string - text content of tooltip + * @param customTooltip React.ReactNode - any custom tooltip + */ +const TableHeaderTooltipCellComponent = ({ title, tooltipContent, customTooltip }: Props) => ( + + + {title} + + {customTooltip ?? ( + + + + )} + +); + +export const TableHeaderTooltipCell = React.memo(TableHeaderTooltipCellComponent); + +TableHeaderTooltipCell.displayName = 'TableHeaderTooltipCell'; From c6e9da28108fa0bd9f14ba85f8ef35fd59b7386b Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Fri, 3 Dec 2021 11:25:11 +0100 Subject: [PATCH 34/65] change ownership of `advanced_settings` to core team (#120331) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c398316e634b9..a64ab63494b35 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -22,7 +22,6 @@ # Vis Editors /x-pack/plugins/lens/ @elastic/kibana-vis-editors -/src/plugins/advanced_settings/ @elastic/kibana-vis-editors /src/plugins/charts/ @elastic/kibana-vis-editors /src/plugins/vis_default_editor/ @elastic/kibana-vis-editors /src/plugins/vis_types/metric/ @elastic/kibana-vis-editors @@ -263,6 +262,7 @@ /src/plugins/home/server/*.ts @elastic/kibana-core /src/plugins/home/server/services/ @elastic/kibana-core /src/plugins/kibana_overview/ @elastic/kibana-core +/src/plugins/advanced_settings/ @elastic/kibana-core /x-pack/plugins/global_search_bar/ @elastic/kibana-core #CC# /src/core/server/csp/ @elastic/kibana-core #CC# /src/plugins/saved_objects/ @elastic/kibana-core From fd741b47e79bea44d8269c27d79d0d59007bc725 Mon Sep 17 00:00:00 2001 From: Ignacio Rivas Date: Fri, 3 Dec 2021 12:01:37 +0100 Subject: [PATCH 35/65] [Watcher] Prevent expensive queries on ES by using PointInTimeFinder to get indexPatterns (#119717) * Use PointInTimeFinder to paginate through indexPatterns in a more performant way * commit using @elastic.co * Start working on tests * Add tests * Add missing types * Fix tests * Update tests responses * Remove unnecessary mocks * Fix type Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../watch_create_threshold.test.tsx | 8 ---- .../client_integration/watch_edit.test.ts | 8 ---- .../watcher/public/application/lib/api.ts | 8 ++-- .../threshold_watch_edit.tsx | 5 +- .../register_get_index_patterns_route.ts | 47 +++++++++++++++++++ .../api/indices/register_indices_routes.ts | 2 + x-pack/test/api_integration/apis/index.ts | 1 + .../api_integration/apis/watcher/index.ts | 14 ++++++ .../api_integration/apis/watcher/watcher.ts | 47 +++++++++++++++++++ 9 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 x-pack/plugins/watcher/server/routes/api/indices/register_get_index_patterns_route.ts create mode 100644 x-pack/test/api_integration/apis/watcher/index.ts create mode 100644 x-pack/test/api_integration/apis/watcher/watcher.ts diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx index 481f59093d7dc..52c3a69938d74 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx @@ -50,14 +50,6 @@ jest.mock('../../public/application/lib/api', () => { return { ...original, - loadIndexPatterns: async () => { - const INDEX_PATTERNS = [ - { attributes: { title: 'index1' } }, - { attributes: { title: 'index2' } }, - { attributes: { title: 'index3' } }, - ]; - return await INDEX_PATTERNS; - }, getHttpClient: () => mockHttpClient, }; }); diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts index 1188cc8469a58..b5fb2aa9d915a 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts @@ -23,14 +23,6 @@ jest.mock('../../public/application/lib/api', () => { return { ...original, - loadIndexPatterns: async () => { - const INDEX_PATTERNS = [ - { attributes: { title: 'index1' } }, - { attributes: { title: 'index2' } }, - { attributes: { title: 'index3' } }, - ]; - return await INDEX_PATTERNS; - }, getHttpClient: () => mockHttpClient, }; }); diff --git a/x-pack/plugins/watcher/public/application/lib/api.ts b/x-pack/plugins/watcher/public/application/lib/api.ts index 61ea124695cb9..0971371f7949b 100644 --- a/x-pack/plugins/watcher/public/application/lib/api.ts +++ b/x-pack/plugins/watcher/public/application/lib/api.ts @@ -151,12 +151,10 @@ export const executeWatch = async (executeWatchDetails: ExecutedWatchDetails, wa }; export const loadIndexPatterns = async () => { - const { savedObjects } = await getSavedObjectsClient().find({ - type: 'index-pattern', - fields: ['title'], - perPage: 10000, + return sendRequest({ + path: `${basePath}/indices/index_patterns`, + method: 'get', }); - return savedObjects; }; const getWatchVisualizationDataDeserializer = (data: { visualizeData: any }) => data?.visualizeData; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx index a4c19827c902a..a885c86bc8817 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx @@ -182,9 +182,8 @@ export const ThresholdWatchEdit = ({ pageTitle }: { pageTitle: string }) => { useEffect(() => { const getIndexPatterns = async () => { - const indexPatternObjects = await loadIndexPatterns(); - const titles = indexPatternObjects.map((indexPattern: any) => indexPattern.attributes.title); - setIndexPatterns(titles); + const { data: indexPatternTitles } = await loadIndexPatterns(); + setIndexPatterns(indexPatternTitles); }; const loadData = async () => { diff --git a/x-pack/plugins/watcher/server/routes/api/indices/register_get_index_patterns_route.ts b/x-pack/plugins/watcher/server/routes/api/indices/register_get_index_patterns_route.ts new file mode 100644 index 0000000000000..237f6dac6a254 --- /dev/null +++ b/x-pack/plugins/watcher/server/routes/api/indices/register_get_index_patterns_route.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsFindResult } from 'src/core/server'; +import { RouteDependencies } from '../../../types'; + +export function registerGetIndexPatternsRoute({ + router, + license, + lib: { handleEsError }, +}: RouteDependencies) { + router.get( + { + path: '/api/watcher/indices/index_patterns', + validate: false, + }, + license.guardApiRoute(async ({ core: { savedObjects } }, request, response) => { + try { + const finder = savedObjects.client.createPointInTimeFinder({ + type: 'index-pattern', + fields: ['title'], + perPage: 1000, + }); + + const responses: string[] = []; + + for await (const result of finder.find()) { + responses.push( + ...result.saved_objects.map( + (indexPattern: SavedObjectsFindResult) => indexPattern.attributes.title + ) + ); + } + + await finder.close(); + + return response.ok({ body: responses }); + } catch (error) { + return handleEsError({ error, response }); + } + }) + ); +} diff --git a/x-pack/plugins/watcher/server/routes/api/indices/register_indices_routes.ts b/x-pack/plugins/watcher/server/routes/api/indices/register_indices_routes.ts index 041457aadf8eb..6e7003f1cafed 100644 --- a/x-pack/plugins/watcher/server/routes/api/indices/register_indices_routes.ts +++ b/x-pack/plugins/watcher/server/routes/api/indices/register_indices_routes.ts @@ -6,8 +6,10 @@ */ import { registerGetRoute } from './register_get_route'; +import { registerGetIndexPatternsRoute } from './register_get_index_patterns_route'; import { RouteDependencies } from '../../../types'; export function registerIndicesRoutes(deps: RouteDependencies) { registerGetRoute(deps); + registerGetIndexPatternsRoute(deps); } diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index 56b2042dc4854..b37d88a5dc426 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -34,5 +34,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./painless_lab')); loadTestFile(require.resolve('./file_upload')); loadTestFile(require.resolve('./ml')); + loadTestFile(require.resolve('./watcher')); }); } diff --git a/x-pack/test/api_integration/apis/watcher/index.ts b/x-pack/test/api_integration/apis/watcher/index.ts new file mode 100644 index 0000000000000..964b7fa0099af --- /dev/null +++ b/x-pack/test/api_integration/apis/watcher/index.ts @@ -0,0 +1,14 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Watcher', () => { + loadTestFile(require.resolve('./watcher')); + }); +} diff --git a/x-pack/test/api_integration/apis/watcher/watcher.ts b/x-pack/test/api_integration/apis/watcher/watcher.ts new file mode 100644 index 0000000000000..734b6c8c6212d --- /dev/null +++ b/x-pack/test/api_integration/apis/watcher/watcher.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const log = getService('log'); + const supertest = getService('supertest'); + const transform = getService('transform'); + + describe('watcher', () => { + before(async () => { + try { + await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + } catch (error) { + log.debug('[Setup error] Error creating index pattern'); + throw error; + } + }); + + after(async () => { + try { + await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + } catch (error) { + log.debug('[Cleanup error] Error deleting index pattern'); + throw error; + } + }); + + describe('POST /api/watcher/indices/index_patterns', () => { + it('returns list of index patterns', async () => { + const response = await supertest + .get('/api/watcher/indices/index_patterns') + .set('kbn-xsrf', 'kibana') + .expect(200); + + expect(response.body).to.contain('ft_ecommerce'); + }); + }); + }); +} From bfa8b3c9b6f3e7d58335cbfe991de519e2f087ec Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 3 Dec 2021 13:34:00 +0200 Subject: [PATCH 36/65] [Canvas] Added KibanaThemeProvider to expression_repeat_image. (#120089) * Added kibanaThemeProvider to expression_repeat_image. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../expression_repeat_image/kibana.json | 2 +- .../components/repeat_image_component.tsx | 4 +- .../repeat_image_renderer.stories.tsx | 4 +- .../public/expression_renderers/index.ts | 6 +- .../repeat_image_renderer.tsx | 74 +++++++++++-------- .../expression_repeat_image/public/index.ts | 5 +- .../expression_repeat_image/public/plugin.ts | 4 +- .../canvas_plugin_src/renderers/external.ts | 4 +- .../shareable_runtime/supported_renderers.js | 4 +- 9 files changed, 59 insertions(+), 48 deletions(-) diff --git a/src/plugins/expression_repeat_image/kibana.json b/src/plugins/expression_repeat_image/kibana.json index 5694e0160042c..0df2eb9842312 100755 --- a/src/plugins/expression_repeat_image/kibana.json +++ b/src/plugins/expression_repeat_image/kibana.json @@ -11,5 +11,5 @@ "ui": true, "requiredPlugins": ["expressions", "presentationUtil"], "optionalPlugins": [], - "requiredBundles": [] + "requiredBundles": ["kibanaReact"] } diff --git a/src/plugins/expression_repeat_image/public/components/repeat_image_component.tsx b/src/plugins/expression_repeat_image/public/components/repeat_image_component.tsx index 7a136b470e943..7da6735c6ce86 100644 --- a/src/plugins/expression_repeat_image/public/components/repeat_image_component.tsx +++ b/src/plugins/expression_repeat_image/public/components/repeat_image_component.tsx @@ -46,7 +46,9 @@ function setImageSize(img: HTMLImageElement, size: number) { } function createImageJSX(img: HTMLImageElement | null) { - if (!img) return null; + if (!img) { + return null; + } const params = img.width > img.height ? { heigth: img.height } : { width: img.width }; return ; } diff --git a/src/plugins/expression_repeat_image/public/expression_renderers/__stories__/repeat_image_renderer.stories.tsx b/src/plugins/expression_repeat_image/public/expression_renderers/__stories__/repeat_image_renderer.stories.tsx index 42f008b2570ea..c727ca9562fad 100644 --- a/src/plugins/expression_repeat_image/public/expression_renderers/__stories__/repeat_image_renderer.stories.tsx +++ b/src/plugins/expression_repeat_image/public/expression_renderers/__stories__/repeat_image_renderer.stories.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { Render } from '../../../../presentation_util/public/__stories__'; -import { repeatImageRenderer } from '../repeat_image_renderer'; +import { getRepeatImageRenderer } from '../repeat_image_renderer'; import { getElasticLogo, getElasticOutline, @@ -31,7 +31,7 @@ const Renderer = ({ emptyImage: elasticOutline, }; - return ; + return ; }; storiesOf('enderers/repeatImage', module).add( diff --git a/src/plugins/expression_repeat_image/public/expression_renderers/index.ts b/src/plugins/expression_repeat_image/public/expression_renderers/index.ts index 5c5625f8c7730..eb161e6e0f2a3 100644 --- a/src/plugins/expression_repeat_image/public/expression_renderers/index.ts +++ b/src/plugins/expression_repeat_image/public/expression_renderers/index.ts @@ -6,8 +6,4 @@ * Side Public License, v 1. */ -import { repeatImageRenderer } from './repeat_image_renderer'; - -export const renderers = [repeatImageRenderer]; - -export { repeatImageRenderer }; +export { getRepeatImageRenderer, repeatImageRendererFactory } from './repeat_image_renderer'; diff --git a/src/plugins/expression_repeat_image/public/expression_renderers/repeat_image_renderer.tsx b/src/plugins/expression_repeat_image/public/expression_renderers/repeat_image_renderer.tsx index 330bf16e038fa..5e5bc04f317d2 100644 --- a/src/plugins/expression_repeat_image/public/expression_renderers/repeat_image_renderer.tsx +++ b/src/plugins/expression_repeat_image/public/expression_renderers/repeat_image_renderer.tsx @@ -7,10 +7,19 @@ */ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { I18nProvider } from '@kbn/i18n-react'; +import { Observable } from 'rxjs'; +import { CoreTheme } from 'kibana/public'; import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { i18n } from '@kbn/i18n'; -import { getElasticOutline, isValidUrl, withSuspense } from '../../../presentation_util/public'; +import { I18nProvider } from '@kbn/i18n-react'; +import { KibanaThemeProvider } from '../../../kibana_react/public'; +import { CoreSetup } from '../../../../core/public'; +import { + defaultTheme$, + getElasticOutline, + isValidUrl, + withSuspense, +} from '../../../presentation_util/public'; import { RepeatImageRendererConfig } from '../../common/types'; const strings = { @@ -27,32 +36,39 @@ const strings = { const LazyRepeatImageComponent = lazy(() => import('../components/repeat_image_component')); const RepeatImageComponent = withSuspense(LazyRepeatImageComponent, null); -export const repeatImageRenderer = (): ExpressionRenderDefinition => ({ - name: 'repeatImage', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render: async ( - domNode: HTMLElement, - config: RepeatImageRendererConfig, - handlers: IInterpreterRenderHandlers - ) => { - const { elasticOutline } = await getElasticOutline(); - const settings = { - ...config, - image: isValidUrl(config.image) ? config.image : elasticOutline, - emptyImage: config.emptyImage || '', - }; +export const getRepeatImageRenderer = + (theme$: Observable = defaultTheme$) => + (): ExpressionRenderDefinition => ({ + name: 'repeatImage', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render: async ( + domNode: HTMLElement, + config: RepeatImageRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + const { elasticOutline } = await getElasticOutline(); + const settings = { + ...config, + image: isValidUrl(config.image) ? config.image : elasticOutline, + emptyImage: config.emptyImage || '', + }; + + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); - handlers.onDestroy(() => { - unmountComponentAtNode(domNode); - }); + render( + + + + + , + domNode + ); + }, + }); - render( - - - , - domNode - ); - }, -}); +export const repeatImageRendererFactory = (core: CoreSetup) => + getRepeatImageRenderer(core.theme.theme$); diff --git a/src/plugins/expression_repeat_image/public/index.ts b/src/plugins/expression_repeat_image/public/index.ts index 21e8f449dcc70..4080ad4f1359f 100755 --- a/src/plugins/expression_repeat_image/public/index.ts +++ b/src/plugins/expression_repeat_image/public/index.ts @@ -6,9 +6,6 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/110893 -/* eslint-disable @kbn/eslint/no_export_all */ - import { ExpressionRepeatImagePlugin } from './plugin'; export type { ExpressionRepeatImagePluginSetup, ExpressionRepeatImagePluginStart } from './plugin'; @@ -17,4 +14,4 @@ export function plugin() { return new ExpressionRepeatImagePlugin(); } -export * from './expression_renderers'; +export { getRepeatImageRenderer, repeatImageRendererFactory } from './expression_renderers'; diff --git a/src/plugins/expression_repeat_image/public/plugin.ts b/src/plugins/expression_repeat_image/public/plugin.ts index d71ce99eb1bd1..2f275f9218d50 100755 --- a/src/plugins/expression_repeat_image/public/plugin.ts +++ b/src/plugins/expression_repeat_image/public/plugin.ts @@ -9,7 +9,7 @@ import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; import { repeatImageFunction } from '../common/expression_functions'; -import { repeatImageRenderer } from './expression_renderers'; +import { repeatImageRendererFactory } from './expression_renderers'; interface SetupDeps { expressions: ExpressionsSetup; @@ -33,7 +33,7 @@ export class ExpressionRepeatImagePlugin { public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionRepeatImagePluginSetup { expressions.registerFunction(repeatImageFunction); - expressions.registerRenderer(repeatImageRenderer); + expressions.registerRenderer(repeatImageRendererFactory(core)); } public start(core: CoreStart): ExpressionRepeatImagePluginStart {} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts index f97ac6e538575..9e2a51065eb6c 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -11,7 +11,7 @@ import { errorRendererFactory, debugRendererFactory, } from '../../../../../src/plugins/expression_error/public'; -import { repeatImageRenderer } from '../../../../../src/plugins/expression_repeat_image/public'; +import { repeatImageRendererFactory } from '../../../../../src/plugins/expression_repeat_image/public'; import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public'; import { shapeRenderer, @@ -22,12 +22,12 @@ export const renderFunctions = [ imageRenderer, revealImageRenderer, shapeRenderer, - repeatImageRenderer, progressRenderer, ]; export const renderFunctionFactories = [ debugRendererFactory, errorRendererFactory, + repeatImageRendererFactory, metricRendererFactory, ]; diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index b71e60e5ef0be..84150a6a9e82e 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -15,7 +15,7 @@ import { getErrorRenderer, getDebugRenderer, } from '../../../../src/plugins/expression_error/public'; -import { repeatImageRenderer as repeatImage } from '../../../../src/plugins/expression_repeat_image/public'; +import { getRepeatImageRenderer } from '../../../../src/plugins/expression_repeat_image/public'; import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public'; import { shapeRenderer as shape, @@ -31,6 +31,7 @@ const renderFunctionsFactories = [ getTableRenderer, getErrorRenderer, getDebugRenderer, + getRepeatImageRenderer, getMetricRenderer, ]; @@ -41,7 +42,6 @@ const renderFunctionsFactories = [ */ export const renderFunctions = [ image, - repeatImage, revealImage, pie, plot, From 5bfaf51816c733df0dba4a6d61e7e8237d10ec57 Mon Sep 17 00:00:00 2001 From: Claudio Procida Date: Fri, 3 Dec 2021 13:09:47 +0100 Subject: [PATCH 37/65] [RAC] Fixes terminology and splits tests to new case (#120335) * Includes errors in the total number of rules * Fixes terminology and splits tests to new case * Removes unused vars * Review feedback --- .../containers/alerts_page/alerts_page.tsx | 11 +- .../services/observability/alerts/common.ts | 4 +- .../apps/observability/alerts/index.ts | 75 +----------- .../apps/observability/alerts/rule_stats.ts | 107 ++++++++++++++++++ .../apps/observability/helpers.ts | 12 ++ .../apps/observability/index.ts | 13 ++- 6 files changed, 133 insertions(+), 89 deletions(-) create mode 100644 x-pack/test/observability_functional/apps/observability/alerts/rule_stats.ts create mode 100644 x-pack/test/observability_functional/apps/observability/helpers.ts diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx index 06040d9a186ff..4462daa1cbf28 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx @@ -107,15 +107,10 @@ function AlertsPage() { // Note that the API uses the semantics of 'alerts' instead of 'rules' const { alertExecutionStatus, ruleMutedStatus, ruleEnabledStatus } = response; if (alertExecutionStatus && ruleMutedStatus && ruleEnabledStatus) { - const total = Object.entries(alertExecutionStatus).reduce((acc, [key, value]) => { - if (key !== 'error') { - acc = acc + value; - } - return acc; - }, 0); - const { error } = alertExecutionStatus; - const { muted } = ruleMutedStatus; + const total = Object.values(alertExecutionStatus).reduce((acc, value) => acc + value, 0); const { disabled } = ruleEnabledStatus; + const { muted } = ruleMutedStatus; + const { error } = alertExecutionStatus; setRuleStats({ ...ruleStats, total, diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts index 2f888d3d733c0..270ab84f01d7a 100644 --- a/x-pack/test/functional/services/observability/alerts/common.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -271,7 +271,7 @@ export function ObservabilityAlertsCommonProvider({ return actionsOverflowButtons[index] || null; }; - const getAlertStatValue = async (testSubj: string) => { + const getRuleStatValue = async (testSubj: string) => { const stat = await testSubjects.find(testSubj); const title = await stat.findByCssSelector('.euiStat__title'); const count = await title.getVisibleText(); @@ -317,6 +317,6 @@ export function ObservabilityAlertsCommonProvider({ viewRuleDetailsButtonClick, viewRuleDetailsLinkClick, getAlertsFlyoutViewRuleDetailsLinkOrFail, - getAlertStatValue, + getRuleStatValue, }; } diff --git a/x-pack/test/observability_functional/apps/observability/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/alerts/index.ts index 12a83f19ca258..40c4e53ba869b 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/index.ts @@ -7,19 +7,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { ObjectRemover } from '../../../../functional_with_es_ssl/lib/object_remover'; -import { - createAlert, - disableAlert, - muteAlert, -} from '../../../../functional_with_es_ssl/lib/alert_api_actions'; -import { generateUniqueKey } from '../../../../functional_with_es_ssl/lib/get_test_data'; - -async function asyncForEach(array: T[], callback: (item: T, index: number) => void) { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index); - } -} +import { asyncForEach } from '../helpers'; const ACTIVE_ALERTS_CELL_COUNT = 78; const RECOVERED_ALERTS_CELL_COUNT = 120; @@ -28,8 +16,6 @@ const TOTAL_ALERTS_CELL_COUNT = 165; export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const find = getService('find'); - const supertest = getService('supertest'); - const objectRemover = new ObjectRemover(supertest); describe('Observability alerts', function () { this.tags('includeFirefox'); @@ -245,65 +231,6 @@ export default ({ getService }: FtrProviderContext) => { expect(await find.existsByCssSelector('[title="Rules and Connectors"]')).to.eql(true); }); }); - - describe('Stat counters', () => { - beforeEach(async () => { - const uniqueKey = generateUniqueKey(); - - const alertToDisable = await createAlert({ - supertest, - objectRemover, - overwrites: { name: 'b', tags: [uniqueKey] }, - }); - await createAlert({ - supertest, - objectRemover, - overwrites: { name: 'c', tags: [uniqueKey] }, - }); - await createAlert({ - supertest, - objectRemover, - overwrites: { name: 'a', tags: [uniqueKey] }, - }); - await createAlert({ - supertest, - objectRemover, - overwrites: { name: 'd', tags: [uniqueKey] }, - }); - await createAlert({ - supertest, - objectRemover, - overwrites: { name: 'e', tags: [uniqueKey] }, - }); - const alertToMute = await createAlert({ - supertest, - objectRemover, - overwrites: { name: 'f', tags: [uniqueKey] }, - }); - - await disableAlert({ supertest, alertId: alertToDisable.id }); - await muteAlert({ supertest, alertId: alertToMute.id }); - - await observability.alerts.common.navigateToTimeWithData(); - }); - - afterEach(async () => { - await objectRemover.removeAll(); - }); - - it('Exist and display expected values', async () => { - const subjToValueMap: { [key: string]: number } = { - statRuleCount: 6, - statDisabled: 1, - statMuted: 1, - statErrors: 0, - }; - await asyncForEach(Object.keys(subjToValueMap), async (subject: string) => { - const value = await observability.alerts.common.getAlertStatValue(subject); - expect(value).to.be(subjToValueMap[subject]); - }); - }); - }); }); }); }; diff --git a/x-pack/test/observability_functional/apps/observability/alerts/rule_stats.ts b/x-pack/test/observability_functional/apps/observability/alerts/rule_stats.ts new file mode 100644 index 0000000000000..41ff88be079d4 --- /dev/null +++ b/x-pack/test/observability_functional/apps/observability/alerts/rule_stats.ts @@ -0,0 +1,107 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { ObjectRemover } from '../../../../functional_with_es_ssl/lib/object_remover'; +import { + createAlert as createRule, + disableAlert as disableRule, + muteAlert as muteRule, +} from '../../../../functional_with_es_ssl/lib/alert_api_actions'; +import { generateUniqueKey } from '../../../../functional_with_es_ssl/lib/get_test_data'; +import { asyncForEach } from '../helpers'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const objectRemover = new ObjectRemover(supertest); + + describe('Observability rules', function () { + this.tags('includeFirefox'); + + const observability = getService('observability'); + + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + const setup = async () => { + await observability.alerts.common.setKibanaTimeZoneToUTC(); + await observability.alerts.common.navigateToTimeWithData(); + }; + await setup(); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + describe('With no data', () => { + it('Shows the no data screen', async () => { + await observability.alerts.common.getNoDataPageOrFail(); + }); + }); + + describe('Stat counters', () => { + beforeEach(async () => { + const uniqueKey = generateUniqueKey(); + + const ruleToDisable = await createRule({ + supertest, + objectRemover, + overwrites: { name: 'b', tags: [uniqueKey] }, + }); + await createRule({ + supertest, + objectRemover, + overwrites: { name: 'c', tags: [uniqueKey] }, + }); + await createRule({ + supertest, + objectRemover, + overwrites: { name: 'a', tags: [uniqueKey] }, + }); + await createRule({ + supertest, + objectRemover, + overwrites: { name: 'd', tags: [uniqueKey] }, + }); + await createRule({ + supertest, + objectRemover, + overwrites: { name: 'e', tags: [uniqueKey] }, + }); + const ruleToMute = await createRule({ + supertest, + objectRemover, + overwrites: { name: 'f', tags: [uniqueKey] }, + }); + + await disableRule({ supertest, alertId: ruleToDisable.id }); + await muteRule({ supertest, alertId: ruleToMute.id }); + + await observability.alerts.common.navigateToTimeWithData(); + }); + + afterEach(async () => { + await objectRemover.removeAll(); + }); + + it('Exist and display expected values', async () => { + const subjToValueMap: { [key: string]: number } = { + statRuleCount: 6, + statDisabled: 1, + statMuted: 1, + statErrors: 0, + }; + await asyncForEach(Object.keys(subjToValueMap), async (subject: string) => { + const value = await observability.alerts.common.getRuleStatValue(subject); + expect(value).to.be(subjToValueMap[subject]); + }); + }); + }); + }); +}; diff --git a/x-pack/test/observability_functional/apps/observability/helpers.ts b/x-pack/test/observability_functional/apps/observability/helpers.ts new file mode 100644 index 0000000000000..72a91881ab97a --- /dev/null +++ b/x-pack/test/observability_functional/apps/observability/helpers.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export async function asyncForEach(array: T[], callback: (item: T, index: number) => void) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index); + } +} diff --git a/x-pack/test/observability_functional/apps/observability/index.ts b/x-pack/test/observability_functional/apps/observability/index.ts index bc78fa138c39b..de681d5c17f2c 100644 --- a/x-pack/test/observability_functional/apps/observability/index.ts +++ b/x-pack/test/observability_functional/apps/observability/index.ts @@ -10,15 +10,18 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('ObservabilityApp', function () { this.tags('ciGroup6'); - loadTestFile(require.resolve('./feature_controls')); - loadTestFile(require.resolve('./exploratory_view')); + loadTestFile(require.resolve('./alerts')); + loadTestFile(require.resolve('./alerts/add_to_case')); loadTestFile(require.resolve('./alerts/alert_disclaimer')); - loadTestFile(require.resolve('./alerts/workflow_status')); + loadTestFile(require.resolve('./alerts/bulk_actions')); loadTestFile(require.resolve('./alerts/pagination')); - loadTestFile(require.resolve('./alerts/add_to_case')); + loadTestFile(require.resolve('./alerts/rule_stats')); loadTestFile(require.resolve('./alerts/state_synchronization')); - loadTestFile(require.resolve('./alerts/bulk_actions')); loadTestFile(require.resolve('./alerts/table_storage')); + loadTestFile(require.resolve('./alerts/workflow_status')); + + loadTestFile(require.resolve('./exploratory_view')); + loadTestFile(require.resolve('./feature_controls')); }); } From 4436b267b74193e397bc7d3907638248af03b6b4 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Fri, 3 Dec 2021 14:31:43 +0100 Subject: [PATCH 38/65] Fix globalsearchg functional tests (#120333) --- .../integration_tests/get_searchable_types.test.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts b/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts index b8224d9a30d08..e3b237bffaf95 100644 --- a/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts +++ b/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts @@ -14,8 +14,7 @@ import { registerInternalSearchableTypesRoute } from '../get_searchable_types'; type SetupServerReturn = UnwrapPromise>; const pluginId = Symbol('globalSearch'); -// FAILING: https://github.com/elastic/kibana/issues/120268 -describe.skip('GET /internal/global_search/searchable_types', () => { +describe('GET /internal/global_search/searchable_types', () => { let server: SetupServerReturn['server']; let httpSetup: SetupServerReturn['httpSetup']; let globalSearchHandlerContext: ReturnType< @@ -47,7 +46,7 @@ describe.skip('GET /internal/global_search/searchable_types', () => { it('calls the handler context with correct parameters', async () => { await supertest(httpSetup.server.listener) - .post('/internal/global_search/searchable_types') + .get('/internal/global_search/searchable_types') .expect(200); expect(globalSearchHandlerContext.getSearchableTypes).toHaveBeenCalledTimes(1); @@ -57,7 +56,7 @@ describe.skip('GET /internal/global_search/searchable_types', () => { globalSearchHandlerContext.getSearchableTypes.mockResolvedValue(['type-a', 'type-b']); const response = await supertest(httpSetup.server.listener) - .post('/internal/global_search/searchable_types') + .get('/internal/global_search/searchable_types') .expect(200); expect(response.body).toEqual({ @@ -69,8 +68,8 @@ describe.skip('GET /internal/global_search/searchable_types', () => { globalSearchHandlerContext.getSearchableTypes.mockRejectedValue(new Error()); const response = await supertest(httpSetup.server.listener) - .post('/internal/global_search/searchable_types') - .expect(200); + .get('/internal/global_search/searchable_types') + .expect(500); expect(response.body).toEqual( expect.objectContaining({ From 265c8dcd434369dbc47fe844a29855ce79e2e398 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Fri, 3 Dec 2021 08:33:09 -0500 Subject: [PATCH 39/65] [Fleet] Configure fleet default output on prem with ES host and CA fingerprint (#120276) --- .../server/kibana_config_writer.test.ts | 27 ++++++++++ .../server/kibana_config_writer.ts | 52 ++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/plugins/interactive_setup/server/kibana_config_writer.test.ts b/src/plugins/interactive_setup/server/kibana_config_writer.test.ts index 005e280fcc744..82d02882698d4 100644 --- a/src/plugins/interactive_setup/server/kibana_config_writer.test.ts +++ b/src/plugins/interactive_setup/server/kibana_config_writer.test.ts @@ -7,6 +7,7 @@ */ jest.mock('fs/promises'); +jest.mock('crypto'); import { constants } from 'fs'; import { loggingSystemMock } from 'src/core/server/mocks'; @@ -28,6 +29,16 @@ describe('KibanaConfigWriter', () => { mockReadFile.mockResolvedValue(''); + const mockCrypto = jest.requireMock('crypto'); + mockCrypto.X509Certificate = function (cert: string) { + if (cert === 'invalid-cert') { + throw new Error('Invalid certificate'); + } + return { + fingerprint256: 'fingerprint256', + }; + }; + kibanaConfigWriter = new KibanaConfigWriter( '/some/path/kibana.yml', '/data', @@ -120,6 +131,7 @@ describe('KibanaConfigWriter', () => { elasticsearch.hosts: [some-host] elasticsearch.serviceAccountToken: some-value elasticsearch.ssl.certificateAuthorities: [/data/ca_1234.crt] + xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_sha256: fingerprint256}] ", ], @@ -186,6 +198,7 @@ describe('KibanaConfigWriter', () => { elasticsearch.username: username elasticsearch.password: password elasticsearch.ssl.certificateAuthorities: [/data/ca_1234.crt] + xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_sha256: fingerprint256}] ", ], @@ -193,6 +206,18 @@ describe('KibanaConfigWriter', () => { `); }); + it('throws if it cannot parse CA certificate', async () => { + await expect( + kibanaConfigWriter.writeConfig({ + caCert: 'invalid-cert', + host: 'some-host', + serviceAccountToken: { name: 'some-token', value: 'some-value' }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Invalid certificate]`); + + expect(mockWriteFile).not.toHaveBeenCalled(); + }); + it('can successfully write elasticsearch config without CA certificate', async () => { await expect( kibanaConfigWriter.writeConfig({ @@ -250,6 +275,7 @@ describe('KibanaConfigWriter', () => { elasticsearch.hosts: [some-host] elasticsearch.serviceAccountToken: some-value elasticsearch.ssl.certificateAuthorities: [/data/ca_1234.crt] + xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_sha256: fingerprint256}] ", ], @@ -303,6 +329,7 @@ describe('KibanaConfigWriter', () => { monitoring.ui.container.elasticsearch.enabled: true elasticsearch.serviceAccountToken: some-value elasticsearch.ssl.certificateAuthorities: [/data/ca_1234.crt] + xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_sha256: fingerprint256}] ", ], diff --git a/src/plugins/interactive_setup/server/kibana_config_writer.ts b/src/plugins/interactive_setup/server/kibana_config_writer.ts index 949bc25ddd253..af177fee33bce 100644 --- a/src/plugins/interactive_setup/server/kibana_config_writer.ts +++ b/src/plugins/interactive_setup/server/kibana_config_writer.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { X509Certificate } from 'crypto'; import { constants } from 'fs'; import fs from 'fs/promises'; import yaml from 'js-yaml'; @@ -30,6 +31,16 @@ export type WriteConfigParameters = { | {} ); +interface FleetOutputConfig { + id: string; + name: string; + is_default: boolean; + is_default_monitoring: boolean; + type: 'elasticsearch'; + hosts: string[]; + ca_sha256: string; +} + export class KibanaConfigWriter { constructor( private readonly configPath: string, @@ -61,7 +72,9 @@ export class KibanaConfigWriter { */ public async writeConfig(params: WriteConfigParameters) { const caPath = path.join(this.dataDirectoryPath, `ca_${Date.now()}.crt`); - const config: Record = { 'elasticsearch.hosts': [params.host] }; + const config: Record = { + 'elasticsearch.hosts': [params.host], + }; if ('serviceAccountToken' in params && params.serviceAccountToken) { config['elasticsearch.serviceAccountToken'] = params.serviceAccountToken.value; } else if ('username' in params && params.username) { @@ -72,6 +85,21 @@ export class KibanaConfigWriter { config['elasticsearch.ssl.certificateAuthorities'] = [caPath]; } + // If a certificate is passed configure Fleet default output + if (params.caCert) { + try { + config['xpack.fleet.outputs'] = KibanaConfigWriter.getFleetDefaultOutputConfig( + params.caCert, + params.host + ); + } catch (err) { + this.logger.error( + `Failed to generate Fleet default output: ${getDetailedErrorMessage(err)}.` + ); + throw err; + } + } + // Load and parse existing configuration file to check if it already has values for the config // entries we want to write. const existingConfig = await this.loadAndParseKibanaConfig(); @@ -152,6 +180,28 @@ export class KibanaConfigWriter { return { raw: rawConfig, parsed: parsedConfig }; } + /** + * Build config for Fleet outputs + * @param caCert + * @param host + */ + private static getFleetDefaultOutputConfig(caCert: string, host: string): FleetOutputConfig[] { + const cert = new X509Certificate(caCert); + const certFingerprint = cert.fingerprint256; + + return [ + { + id: 'fleet-default-output', + name: 'default', + is_default: true, + is_default_monitoring: true, + type: 'elasticsearch', + hosts: [host], + ca_sha256: certFingerprint, + }, + ]; + } + /** * Comments out all non-commented entries in the Kibana configuration file. * @param rawConfig Content of the Kibana configuration file. From 89b1f9056c29ac9c35e29fde14d5a7e27b7d731f Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Fri, 3 Dec 2021 08:42:54 -0500 Subject: [PATCH 40/65] Prevent endless loop for saved object migrations (#120146) --- dev_docs/tutorials/saved_objects.mdx | 2 + .../core/saved-objects-service.asciidoc | 3 ++ .../migrations/core/document_migrator.test.ts | 45 +++---------------- .../migrations/core/document_migrator.ts | 16 +------ .../service/lib/repository.test.ts | 23 ++++++++-- .../saved_objects/service/lib/repository.ts | 3 ++ 6 files changed, 35 insertions(+), 57 deletions(-) diff --git a/dev_docs/tutorials/saved_objects.mdx b/dev_docs/tutorials/saved_objects.mdx index 9583e195d1c82..a9d8cd7c6ec1c 100644 --- a/dev_docs/tutorials/saved_objects.mdx +++ b/dev_docs/tutorials/saved_objects.mdx @@ -252,6 +252,8 @@ Having said that, if a document is encountered that is not in the expected shape fail an upgrade than to silently ignore a corrupt document which can cause unexpected behaviour at some future point in time. When such a scenario is encountered, the error should be verbose and informative so that the corrupt document can be corrected, if possible. +**WARNING:** Do not attempt to change the `migrationVersion`, `id`, or `type` fields within a migration function, this is not supported. + ### Testing Migrations Bugs in a migration function cause downtime for our users and therefore have a very high impact. Follow the . diff --git a/docs/developer/architecture/core/saved-objects-service.asciidoc b/docs/developer/architecture/core/saved-objects-service.asciidoc index a7ce86ea46359..54a5c319c6222 100644 --- a/docs/developer/architecture/core/saved-objects-service.asciidoc +++ b/docs/developer/architecture/core/saved-objects-service.asciidoc @@ -259,6 +259,9 @@ upgrade. In most scenarios, it is better to fail an upgrade than to silently ignore a corrupt document which can cause unexpected behaviour at some future point in time. +WARNING: Do not attempt to change the `migrationVersion`, `id`, or `type` fields +within a migration function, this is not supported. + It is critical that you have extensive tests to ensure that migrations behave as expected with all possible input documents. Given how simple it is to test all the branch conditions in a migration function and the high impact of a bug diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts index 64c1c4ce2fa9f..f92d505c058ed 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts @@ -664,39 +664,6 @@ describe('DocumentMigrator', () => { ); }); - it('allows updating a migrationVersion prop to a later version', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'cat', - migrations: { - '1.0.0': setAttr('migrationVersion.cat', '2.9.1'), - '2.0.0': () => { - throw new Error('POW!'); - }, - '2.9.1': () => { - throw new Error('BANG!'); - }, - '3.0.0': setAttr('attributes.name', 'Shiny'), - }, - }), - }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'smelly', - type: 'cat', - attributes: { name: 'Boo' }, - migrationVersion: { cat: '0.5.6' }, - }); - expect(actual).toEqual({ - id: 'smelly', - type: 'cat', - attributes: { name: 'Shiny' }, - migrationVersion: { cat: '3.0.0' }, - coreMigrationVersion: kibanaVersion, - }); - }); - it('allows adding props to migrationVersion', () => { const migrator = new DocumentMigrator({ ...testOpts(), @@ -1072,7 +1039,8 @@ describe('DocumentMigrator', () => { name: 'dog', namespaceType: 'single', migrations: { - '1.0.0': setAttr('migrationVersion.dog', '2.0.0'), + '1.1.0': setAttr('attributes.age', '12'), + '1.5.0': setAttr('attributes.color', 'tri-color'), '2.0.0': (doc) => doc, // noop }, }, @@ -1083,9 +1051,10 @@ describe('DocumentMigrator', () => { const obj = { id: 'sleepy', type: 'dog', - attributes: { name: 'Patches' }, - migrationVersion: {}, + attributes: { name: 'Patches', age: '11' }, + migrationVersion: { dog: '1.1.0' }, // skip the first migration transform, only apply the second and third references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], + coreMigrationVersion: undefined, // this is intentional }; it('in the default space', () => { @@ -1095,7 +1064,7 @@ describe('DocumentMigrator', () => { { id: 'sleepy', type: 'dog', - attributes: { name: 'Patches' }, + attributes: { name: 'Patches', age: '11', color: 'tri-color' }, migrationVersion: { dog: '2.0.0' }, references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], // no change coreMigrationVersion: kibanaVersion, @@ -1111,7 +1080,7 @@ describe('DocumentMigrator', () => { { id: 'sleepy', type: 'dog', - attributes: { name: 'Patches' }, + attributes: { name: 'Patches', age: '11', color: 'tri-color' }, migrationVersion: { dog: '2.0.0' }, references: [{ id: 'uuidv5', type: 'toy', name: 'BALL!' }], // changed coreMigrationVersion: kibanaVersion, diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts index da16dbc5e69e8..5f2870fb6e244 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts @@ -27,15 +27,7 @@ * handle property addition / deletion / renaming. * * A caveat is that this means we must restrict what a migration can do to the doc's - * migrationVersion itself. We allow only these kinds of changes: - * - * - Add a new property to migrationVersion - * - Move a migrationVersion property forward to a later version - * - * Migrations *cannot* move a migrationVersion property backwards (e.g. from 2.0.0 to 1.0.0), and they - * cannot clear a migrationVersion property, as allowing either of these could produce infinite loops. - * However, we do wish to allow migrations to modify migrationVersion if they wish, so that - * they could transform a type from "foo 1.0.0" to "bar 3.0.0". + * migrationVersion itself. Migrations should *not* make any changes to the migrationVersion property. * * One last gotcha is that any docs which have no migrationVersion are assumed to be up-to-date. * This is because Kibana UI and other clients really can't be expected build the migrationVersion @@ -753,12 +745,6 @@ function migrateProp( let additionalDocs: SavedObjectUnsanitizedDoc[] = []; for (const { version, transform, transformType } of applicableTransforms(migrations, doc, prop)) { - const currentVersion = propVersion(doc, prop); - if (currentVersion && Semver.gt(currentVersion, version)) { - // the previous transform function increased the object's migrationVersion; break out of the loop - break; - } - if (convertNamespaceTypes || (transformType !== 'convert' && transformType !== 'reference')) { // migrate transforms are always applied, but conversion transforms and reference transforms are only applied during index migrations const result = transform(doc); diff --git a/src/core/server/saved_objects/service/lib/repository.test.ts b/src/core/server/saved_objects/service/lib/repository.test.ts index 46a532cdefef4..ab692b146e7f6 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.ts +++ b/src/core/server/saved_objects/service/lib/repository.test.ts @@ -976,8 +976,9 @@ describe('SavedObjectsRepository', () => { describe('migration', () => { it(`migrates the docs and serializes the migrated docs`, async () => { migrator.migrateDocument.mockImplementation(mockMigrateDocument); - await bulkCreateSuccess([obj1, obj2]); - const docs = [obj1, obj2].map((x) => ({ ...x, ...mockTimestampFields })); + const modifiedObj1 = { ...obj1, coreMigrationVersion: '8.0.0' }; + await bulkCreateSuccess([modifiedObj1, obj2]); + const docs = [modifiedObj1, obj2].map((x) => ({ ...x, ...mockTimestampFields })); expectMigrationArgs(docs[0], true, 1); expectMigrationArgs(docs[1], true, 2); @@ -2556,8 +2557,22 @@ describe('SavedObjectsRepository', () => { it(`migrates a document and serializes the migrated doc`, async () => { const migrationVersion = mockMigrationVersion; - await createSuccess(type, attributes, { id, references, migrationVersion }); - const doc = { type, id, attributes, references, migrationVersion, ...mockTimestampFields }; + const coreMigrationVersion = '8.0.0'; + await createSuccess(type, attributes, { + id, + references, + migrationVersion, + coreMigrationVersion, + }); + const doc = { + type, + id, + attributes, + references, + migrationVersion, + coreMigrationVersion, + ...mockTimestampFields, + }; expectMigrationArgs(doc); const migratedDoc = migrator.migrateDocument(doc); diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index 9be58f1b71861..0d17525016043 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -305,6 +305,7 @@ export class SavedObjectsRepository { const { id = SavedObjectsUtils.generateId(), migrationVersion, + coreMigrationVersion, overwrite = false, references = [], refresh = DEFAULT_REFRESH_SETTING, @@ -359,6 +360,7 @@ export class SavedObjectsRepository { originId, attributes, migrationVersion, + coreMigrationVersion, updated_at: time, ...(Array.isArray(references) && { references }), }); @@ -523,6 +525,7 @@ export class SavedObjectsRepository { type: object.type, attributes: object.attributes, migrationVersion: object.migrationVersion, + coreMigrationVersion: object.coreMigrationVersion, ...(savedObjectNamespace && { namespace: savedObjectNamespace }), ...(savedObjectNamespaces && { namespaces: savedObjectNamespaces }), updated_at: time, From 6640357eb6e2a4b5db39e468dc315bce74109259 Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Fri, 3 Dec 2021 14:54:44 +0100 Subject: [PATCH 41/65] [CTI] Threat Intel Card on Overview page needs to accommodate Fleet TI integrations (#115940) * Add support integrations * Fix types * fix unit tests * Fix tests and types * fix eslint * fix file case * add cy tests * Revert test * Add tests * Add support of installed integrations * Fix types * Add isntalled ingtegration case for cypress tests * Fix cypress tests * Fix comments * Fix capital naming * Fix again capital naming * Add dynamic dashboard for a new integrations packages * intermidiate changes, to keep it remote * Big refactoring * Tests and refactoring * Remove unused constanrs * Fix e2e tests * PR comments fix * fix ts * Fix translations * Remove stubs * Rename isSomeIntegrationsDisabled -> allIntegrationsInstalled * Add buildQuery tests * Fix type * Add tests for Enable Source button * Remove copied file * Move api call to api.ts * Rename fetchFleetIntegrations * Remove __mocks__ Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../security_solution/common/cti/constants.ts | 13 +- .../security_solution/cti/index.ts | 37 +++- .../security_solution/index.ts | 7 + .../overview/cti_link_panel.spec.ts | 13 +- .../cypress/screens/overview.ts | 4 +- .../overview/components/link_panel/helpers.ts | 7 - .../overview/components/link_panel/index.ts | 1 - .../components/link_panel/link_panel.tsx | 20 +- .../overview/components/link_panel/types.ts | 1 + .../cti_disabled_module.tsx | 11 +- .../cti_enabled_module.test.tsx | 49 +---- .../overview_cti_links/cti_enabled_module.tsx | 49 ++--- .../overview_cti_links/cti_no_events.test.tsx | 70 ------- .../overview_cti_links/cti_no_events.tsx | 42 ----- .../cti_with_events.test.tsx | 57 ------ .../overview_cti_links/cti_with_events.tsx | 49 ----- .../overview_cti_links/index.test.tsx | 38 ++-- .../components/overview_cti_links/index.tsx | 36 ++-- .../components/overview_cti_links/mock.ts | 13 +- .../threat_intel_panel_view.tsx | 62 +++---- .../overview_cti_links/translations.ts | 21 ++- .../use_integrations_page_link.tsx | 11 ++ .../containers/overview_cti_links/api.ts | 28 +++ .../containers/overview_cti_links/helpers.ts | 60 ------ .../containers/overview_cti_links/index.tsx | 116 +++++------- .../use_all_ti_data_sources.ts | 22 +++ .../use_cti_event_counts.ts | 64 ------- .../use_is_threat_intel_module_enabled.ts | 32 ---- .../use_request_event_counts.ts | 54 ------ .../overview_cti_links/use_ti_data_sources.ts | 174 ++++++++++++++++++ .../overview_cti_links/use_ti_integrations.ts | 55 ++++++ .../public/overview/pages/overview.test.tsx | 28 ++- .../public/overview/pages/overview.tsx | 25 ++- .../security_solution/factory/cti/index.ts | 2 + .../factory/cti/threat_intel_source/index.ts | 33 ++++ .../query.threat_intel_source.dsl.test.ts | 71 +++++++ .../query.threat_intel_source.dsl.ts | 59 ++++++ .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../es_archives/threat_indicator/data.json | 5 +- .../threat_indicator/mappings.json | 8 + 41 files changed, 731 insertions(+), 720 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/use_integrations_page_link.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/api.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/helpers.ts create mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_all_ti_data_sources.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_cti_event_counts.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_is_threat_intel_module_enabled.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts create mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts create mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_integrations.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/index.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.ts diff --git a/x-pack/plugins/security_solution/common/cti/constants.ts b/x-pack/plugins/security_solution/common/cti/constants.ts index b33541c5057d8..7a88b065d8701 100644 --- a/x-pack/plugins/security_solution/common/cti/constants.ts +++ b/x-pack/plugins/security_solution/common/cti/constants.ts @@ -58,14 +58,5 @@ export const EVENT_ENRICHMENT_INDICATOR_FIELD_MAP = { export const DEFAULT_EVENT_ENRICHMENT_FROM = 'now-30d'; export const DEFAULT_EVENT_ENRICHMENT_TO = 'now'; -export const CTI_DATASET_KEY_MAP: { [key: string]: string } = { - 'AbuseCH URL': 'ti_abusech.url', - 'AbuseCH Malware': 'ti_abusech.malware', - 'AbuseCH MalwareBazaar': 'ti_abusech.malwarebazaar', - 'AlienVault OTX': 'ti_otx.threat', - 'Anomali Limo': 'ti_anomali.limo', - 'Anomali Threatstream': 'ti_anomali.threatstream', - MISP: 'ti_misp.threat', - ThreatQuotient: 'ti_threatq.threat', - Cybersixgill: 'ti_cybersixgill.threat', -}; +export const TI_INTEGRATION_PREFIX = 'ti'; +export const OTHER_TI_DATASET_KEY = '_others_ti_'; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts index 26bf4ce6740a9..a6e7eef88724b 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts @@ -5,13 +5,16 @@ * 2.0. */ -import type { IEsSearchResponse } from 'src/plugins/data/public'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IEsSearchResponse, IEsSearchRequest } from 'src/plugins/data/public'; +import { FactoryQueryTypes } from '../..'; import { EVENT_ENRICHMENT_INDICATOR_FIELD_MAP } from '../../../cti/constants'; -import { Inspect } from '../../common'; +import { Inspect, Maybe, TimerangeInput } from '../../common'; import { RequestBasicOptions } from '..'; export enum CtiQueries { eventEnrichment = 'eventEnrichment', + dataSource = 'dataSource', } export interface CtiEventEnrichmentRequestOptions extends RequestBasicOptions { @@ -40,3 +43,33 @@ export const validEventFields = Object.keys(EVENT_ENRICHMENT_INDICATOR_FIELD_MAP export const isValidEventField = (field: string): field is EventField => validEventFields.includes(field as EventField); + +export interface CtiDataSourceRequestOptions extends IEsSearchRequest { + defaultIndex: string[]; + factoryQueryType?: FactoryQueryTypes; + timerange?: TimerangeInput; +} + +export interface BucketItem { + key: string; + doc_count: number; +} +export interface Bucket { + buckets: Array; +} + +export type DatasetBucket = { + name?: Bucket; + dashboard?: Bucket; +} & BucketItem; + +export interface CtiDataSourceStrategyResponse extends Omit { + inspect?: Maybe; + rawResponse: { + aggregations?: Record & { + dataset?: { + buckets: DatasetBucket[]; + }; + }; + }; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts index 00cbdb941c11b..340093995b297 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts @@ -72,6 +72,8 @@ import { CtiEventEnrichmentRequestOptions, CtiEventEnrichmentStrategyResponse, CtiQueries, + CtiDataSourceRequestOptions, + CtiDataSourceStrategyResponse, } from './cti'; import { HostRulesRequestOptions, @@ -85,6 +87,7 @@ import { UserRulesStrategyResponse, } from './ueba'; +export * from './cti'; export * from './hosts'; export * from './matrix_histogram'; export * from './network'; @@ -178,6 +181,8 @@ export type StrategyResponseType = T extends HostsQ ? MatrixHistogramStrategyResponse : T extends CtiQueries.eventEnrichment ? CtiEventEnrichmentStrategyResponse + : T extends CtiQueries.dataSource + ? CtiDataSourceStrategyResponse : never; export type StrategyRequestType = T extends HostsQueries.hosts @@ -238,6 +243,8 @@ export type StrategyRequestType = T extends HostsQu ? MatrixHistogramRequestOptions : T extends CtiQueries.eventEnrichment ? CtiEventEnrichmentRequestOptions + : T extends CtiQueries.dataSource + ? CtiDataSourceRequestOptions : never; export interface DocValueFieldsInput { diff --git a/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts b/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts index 095401ff31422..75ff13b66b29c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts @@ -10,9 +10,8 @@ import { OVERVIEW_CTI_LINKS, OVERVIEW_CTI_LINKS_ERROR_INNER_PANEL, OVERVIEW_CTI_LINKS_INFO_INNER_PANEL, - OVERVIEW_CTI_LINKS_WARNING_INNER_PANEL, OVERVIEW_CTI_TOTAL_EVENT_COUNT, - OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON, + OVERVIEW_CTI_ENABLE_INTEGRATIONS_BUTTON, } from '../../screens/overview'; import { loginAndWaitForPage } from '../../tasks/login'; @@ -28,12 +27,11 @@ describe('CTI Link Panel', () => { it('renders disabled threat intel module as expected', () => { loginAndWaitForPage(OVERVIEW_URL); cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_ERROR_INNER_PANEL}`).should('exist'); - cy.get(`${OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 indicators'); cy.get(`${OVERVIEW_CTI_ENABLE_MODULE_BUTTON}`).should('exist'); cy.get(`${OVERVIEW_CTI_ENABLE_MODULE_BUTTON}`) .should('have.attr', 'href') - .and('match', /filebeat-module-threatintel.html/); + .and('match', /app\/integrations\/browse\?q=threat%20intelligence/); }); describe('enabled threat intel module', () => { @@ -49,17 +47,16 @@ describe('CTI Link Panel', () => { loginAndWaitForPage( `${OVERVIEW_URL}?sourcerer=(timerange:(from:%272021-07-08T04:00:00.000Z%27,kind:absolute,to:%272021-07-09T03:59:59.999Z%27))` ); - cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_WARNING_INNER_PANEL}`).should('exist'); cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_INFO_INNER_PANEL}`).should('exist'); - cy.get(`${OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 indicators'); }); it('renders dashboard module as expected when there are events in the selected time period', () => { loginAndWaitForPage(OVERVIEW_URL); - cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_WARNING_INNER_PANEL}`).should('not.exist'); cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_INFO_INNER_PANEL}`).should('exist'); - cy.get(`${OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); + cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_ENABLE_INTEGRATIONS_BUTTON}`).should('exist'); + cy.get(OVERVIEW_CTI_LINKS).should('not.contain.text', 'Anomali'); + cy.get(OVERVIEW_CTI_LINKS).should('contain.text', 'AbuseCH malware'); cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 1 indicator'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/overview.ts b/x-pack/plugins/security_solution/cypress/screens/overview.ts index 1945b7e3ce3e7..bc335ff6680ee 100644 --- a/x-pack/plugins/security_solution/cypress/screens/overview.ts +++ b/x-pack/plugins/security_solution/cypress/screens/overview.ts @@ -150,9 +150,9 @@ export const OVERVIEW_REVENT_TIMELINES = '[data-test-subj="overview-recent-timel export const OVERVIEW_CTI_LINKS = '[data-test-subj="cti-dashboard-links"]'; export const OVERVIEW_CTI_LINKS_ERROR_INNER_PANEL = '[data-test-subj="cti-inner-panel-danger"]'; -export const OVERVIEW_CTI_LINKS_WARNING_INNER_PANEL = '[data-test-subj="cti-inner-panel-warning"]'; export const OVERVIEW_CTI_LINKS_INFO_INNER_PANEL = '[data-test-subj="cti-inner-panel-info"]'; -export const OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON = '[data-test-subj="cti-view-dashboard-button"]'; +export const OVERVIEW_CTI_ENABLE_INTEGRATIONS_BUTTON = + '[data-test-subj="cti-enable-integrations-button"]'; export const OVERVIEW_CTI_TOTAL_EVENT_COUNT = `${OVERVIEW_CTI_LINKS} [data-test-subj="header-panel-subtitle"]`; export const OVERVIEW_CTI_ENABLE_MODULE_BUTTON = '[data-test-subj="cti-enable-module-button"]'; diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/helpers.ts b/x-pack/plugins/security_solution/public/overview/components/link_panel/helpers.ts index 45d26d9269f6e..e2adaaae35547 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/helpers.ts +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/helpers.ts @@ -5,13 +5,6 @@ * 2.0. */ -import { LinkPanelListItem } from '.'; - -export const isLinkPanelListItem = ( - item: LinkPanelListItem | Partial -): item is LinkPanelListItem => - typeof item.title === 'string' && typeof item.path === 'string' && typeof item.count === 'number'; - export interface EventCounts { [key: string]: number; } diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/index.ts b/x-pack/plugins/security_solution/public/overview/components/link_panel/index.ts index 9d404abcf2223..9a827b137ae78 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/index.ts +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/index.ts @@ -6,6 +6,5 @@ */ export { InnerLinkPanel } from './inner_link_panel'; -export { isLinkPanelListItem } from './helpers'; export { LinkPanel } from './link_panel'; export type { LinkPanelListItem } from './types'; diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/link_panel.tsx b/x-pack/plugins/security_solution/public/overview/components/link_panel/link_panel.tsx index ed67fdb1c96f6..00a225635fb8b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/link_panel.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/link_panel.tsx @@ -71,7 +71,7 @@ const LinkPanelComponent = ({ splitPanel, subtitle, }: { - button: React.ReactNode; + button?: React.ReactNode; columns: Array>; dataTestSubj: string; defaultSortField?: string; @@ -134,14 +134,16 @@ const LinkPanelComponent = ({ {splitPanel} {infoPanel} - + {chunkedItems.length > 0 && ( + + )} diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/types.ts b/x-pack/plugins/security_solution/public/overview/components/link_panel/types.ts index f6c0fb6f3837f..1b8836fc2438d 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/types.ts +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/types.ts @@ -21,4 +21,5 @@ export interface LinkPanelViewProps { listItems: LinkPanelListItem[]; splitPanel?: JSX.Element; totalCount?: number; + allIntegrationsInstalled?: boolean; } diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx index 2697e4a571ad8..36f386e49c5c7 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx @@ -6,24 +6,21 @@ */ import React from 'react'; -import { EMPTY_LIST_ITEMS } from '../../containers/overview_cti_links/helpers'; -import { useKibana } from '../../../common/lib/kibana'; import * as i18n from './translations'; import { DisabledLinkPanel } from '../link_panel/disabled_link_panel'; import { ThreatIntelPanelView } from './threat_intel_panel_view'; +import { useIntegrationsPageLink } from './use_integrations_page_link'; export const CtiDisabledModuleComponent = () => { - const threatIntelDocLink = `${ - useKibana().services.docLinks.links.filebeat.base - }/filebeat-module-threatintel.html`; + const integrationsLink = useIntegrationsPageLink(); return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.test.tsx index db83d9e1bcfe5..fc36a0c4337cf 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.test.tsx @@ -19,20 +19,15 @@ import { mockGlobalState, SUB_PLUGINS_REDUCER, } from '../../../common/mock'; -import { mockTheme, mockProps, mockCtiEventCountsResponse, mockCtiLinksResponse } from './mock'; -import { useCtiEventCounts } from '../../containers/overview_cti_links/use_cti_event_counts'; +import { mockTheme, mockProps, mockTiDataSources, mockCtiLinksResponse } from './mock'; import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; -import { useRequestEventCounts } from '../../containers/overview_cti_links/use_request_event_counts'; +import { useTiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; jest.mock('../../../common/lib/kibana'); -jest.mock('../../containers/overview_cti_links/use_cti_event_counts'); -const useCTIEventCountsMock = useCtiEventCounts as jest.Mock; -useCTIEventCountsMock.mockReturnValue(mockCtiEventCountsResponse); - -jest.mock('../../containers/overview_cti_links/use_request_event_counts'); -const useRequestEventCountsMock = useRequestEventCounts as jest.Mock; -useRequestEventCountsMock.mockReturnValue([true, {}]); +jest.mock('../../containers/overview_cti_links/use_ti_data_sources'); +const useTiDataSourcesMock = useTiDataSources as jest.Mock; +useTiDataSourcesMock.mockReturnValue(mockTiDataSources); jest.mock('../../containers/overview_cti_links'); const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; @@ -54,42 +49,12 @@ describe('CtiEnabledModule', () => { - - - - - ); - - expect(screen.getByTestId('cti-with-events')).toBeInTheDocument(); - }); - - it('renders CtiWithNoEvents when there are no events', () => { - useCTIEventCountsMock.mockReturnValueOnce({ totalCount: 0 }); - render( - - - - - - - - ); - - expect(screen.getByTestId('cti-with-no-events')).toBeInTheDocument(); - }); - - it('renders null while event counts are loading', () => { - useCTIEventCountsMock.mockReturnValueOnce({ totalCount: -1 }); - const { container } = render( - - - - + ); - expect(container.firstChild).toBeNull(); + expect(screen.getByText('Showing: 5 indicators')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.tsx index 5a40c79d6e5ec..a339676ac361f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.tsx @@ -7,37 +7,28 @@ import React from 'react'; import { ThreatIntelLinkPanelProps } from '.'; -import { useCtiEventCounts } from '../../containers/overview_cti_links/use_cti_event_counts'; -import { CtiNoEvents } from './cti_no_events'; -import { CtiWithEvents } from './cti_with_events'; +import { useTiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; +import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; +import { ThreatIntelPanelView } from './threat_intel_panel_view'; -export type CtiEnabledModuleProps = Omit; +export const CtiEnabledModuleComponent: React.FC = (props) => { + const { to, from, allIntegrationsInstalled, allTiDataSources, setQuery, deleteQuery } = props; + const { tiDataSources, totalCount } = useTiDataSources({ + to, + from, + allTiDataSources, + setQuery, + deleteQuery, + }); + const { listItems } = useCtiDashboardLinks({ to, from, tiDataSources }); -export const CtiEnabledModuleComponent: React.FC = (props) => { - const { eventCountsByDataset, totalCount } = useCtiEventCounts(props); - const { to, from } = props; - - switch (totalCount) { - case -1: - return null; - case 0: - return ( -
    - -
    - ); - default: - return ( -
    - -
    - ); - } + return ( + + ); }; export const CtiEnabledModule = React.memo(CtiEnabledModuleComponent); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.test.tsx deleted file mode 100644 index 8f624dabd64d1..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.test.tsx +++ /dev/null @@ -1,70 +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 { Provider } from 'react-redux'; -import { cloneDeep } from 'lodash/fp'; -import { render, screen } from '@testing-library/react'; -import { I18nProvider } from '@kbn/i18n-react'; -import { CtiNoEvents } from './cti_no_events'; -import { ThemeProvider } from 'styled-components'; -import { createStore, State } from '../../../common/store'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - SUB_PLUGINS_REDUCER, -} from '../../../common/mock'; -import { mockEmptyCtiLinksResponse, mockTheme, mockProps } from './mock'; -import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; - -jest.mock('../../../common/lib/kibana'); - -jest.mock('../../containers/overview_cti_links'); -const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; -useCtiDashboardLinksMock.mockReturnValue(mockEmptyCtiLinksResponse); - -describe('CtiNoEvents', () => { - const state: State = mockGlobalState; - - const { storage } = createSecuritySolutionStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - beforeEach(() => { - const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - }); - - it('renders warning inner panel', () => { - render( - - - - - - - - ); - - expect(screen.getByTestId('cti-dashboard-links')).toBeInTheDocument(); - expect(screen.getByTestId('cti-inner-panel-warning')).toBeInTheDocument(); - }); - - it('renders event counts as 0', () => { - render( - - - - - - - - ); - - expect(screen.getByText('Showing: 0 indicators')).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.tsx deleted file mode 100644 index fa7ac50c08765..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.tsx +++ /dev/null @@ -1,42 +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 { useCtiDashboardLinks } from '../../containers/overview_cti_links'; -import { ThreatIntelPanelView } from './threat_intel_panel_view'; -import { InnerLinkPanel } from '../link_panel'; -import * as i18n from './translations'; -import { emptyEventCountsByDataset } from '../../containers/overview_cti_links/helpers'; - -const warning = ( - -); - -export const CtiNoEventsComponent = ({ to, from }: { to: string; from: string }) => { - const { buttonHref, listItems, isPluginDisabled } = useCtiDashboardLinks( - emptyEventCountsByDataset, - to, - from - ); - - return ( - - ); -}; - -export const CtiNoEvents = React.memo(CtiNoEventsComponent); -CtiNoEvents.displayName = 'CtiNoEvents'; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.test.tsx deleted file mode 100644 index a50e3e91ab9e5..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.test.tsx +++ /dev/null @@ -1,57 +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 { Provider } from 'react-redux'; -import { cloneDeep } from 'lodash/fp'; -import { mount } from 'enzyme'; -import { I18nProvider } from '@kbn/i18n-react'; -import { CtiWithEvents } from './cti_with_events'; -import { ThemeProvider } from 'styled-components'; -import { createStore, State } from '../../../common/store'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - SUB_PLUGINS_REDUCER, -} from '../../../common/mock'; -import { mockCtiLinksResponse, mockTheme, mockCtiWithEventsProps } from './mock'; -import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; - -jest.mock('../../../common/lib/kibana'); - -jest.mock('../../containers/overview_cti_links'); -const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; -useCtiDashboardLinksMock.mockReturnValue(mockCtiLinksResponse); - -describe('CtiWithEvents', () => { - const state: State = mockGlobalState; - - const { storage } = createSecuritySolutionStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - beforeEach(() => { - const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - }); - - it('renders total event count as expected', () => { - const wrapper = mount( - - - - - - - - ); - - expect(wrapper.find('[data-test-subj="cti-total-event-count"]').text()).toEqual( - `Showing: ${mockCtiWithEventsProps.totalCount} indicators` - ); - }); -}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.tsx deleted file mode 100644 index f78451e205b1e..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.tsx +++ /dev/null @@ -1,49 +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 { isEqual } from 'lodash'; -import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; -import { ThreatIntelPanelView } from './threat_intel_panel_view'; - -export const CtiWithEventsComponent = ({ - eventCountsByDataset, - from, - to, - totalCount, -}: { - eventCountsByDataset: { [key: string]: number }; - from: string; - to: string; - totalCount: number; -}) => { - const { buttonHref, isPluginDisabled, listItems } = useCtiDashboardLinks( - eventCountsByDataset, - to, - from - ); - - return ( - - ); -}; - -CtiWithEventsComponent.displayName = 'CtiWithEvents'; - -export const CtiWithEvents = React.memo( - CtiWithEventsComponent, - (prevProps, nextProps) => - prevProps.to === nextProps.to && - prevProps.from === nextProps.from && - prevProps.totalCount === nextProps.totalCount && - isEqual(prevProps.eventCountsByDataset, nextProps.eventCountsByDataset) -); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.test.tsx index dfd9c6c9a7fcd..71d6d5eb0c583 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.test.tsx @@ -19,19 +19,19 @@ import { mockGlobalState, SUB_PLUGINS_REDUCER, } from '../../../common/mock'; -import { mockTheme, mockProps, mockCtiEventCountsResponse } from './mock'; -import { useRequestEventCounts } from '../../containers/overview_cti_links/use_request_event_counts'; -import { useCtiEventCounts } from '../../containers/overview_cti_links/use_cti_event_counts'; +import { mockTheme, mockProps, mockTiDataSources, mockCtiLinksResponse } from './mock'; +import { useTiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; +import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; jest.mock('../../../common/lib/kibana'); -jest.mock('../../containers/overview_cti_links/use_request_event_counts'); -const useRequestEventCountsMock = useRequestEventCounts as jest.Mock; -useRequestEventCountsMock.mockReturnValue([true, {}]); +jest.mock('../../containers/overview_cti_links/use_ti_data_sources'); +const useTiDataSourcesMock = useTiDataSources as jest.Mock; +useTiDataSourcesMock.mockReturnValue(mockTiDataSources); -jest.mock('../../containers/overview_cti_links/use_cti_event_counts'); -const useCTIEventCountsMock = useCtiEventCounts as jest.Mock; -useCTIEventCountsMock.mockReturnValue(mockCtiEventCountsResponse); +jest.mock('../../containers/overview_cti_links'); +const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; +useCtiDashboardLinksMock.mockReturnValue(mockCtiLinksResponse); describe('ThreatIntelLinkPanel', () => { const state: State = mockGlobalState; @@ -49,40 +49,44 @@ describe('ThreatIntelLinkPanel', () => { - + ); expect(wrapper.find('[data-test-subj="cti-enabled-module"]').length).toEqual(1); + expect(wrapper.find('[data-test-subj="cti-enable-integrations-button"]').length).toEqual(0); }); - it('renders CtiDisabledModule when Threat Intel module is disabled', () => { + it('renders Enable source buttons when not all integrations installed', () => { const wrapper = mount( - + ); - - expect(wrapper.find('[data-test-subj="cti-disabled-module"]').length).toEqual(1); + expect(wrapper.find('[data-test-subj="cti-enable-integrations-button"]').length).not.toBe(0); }); - it('renders null while Threat Intel module state is loading', () => { + it('renders CtiDisabledModule when Threat Intel module is disabled', () => { const wrapper = mount( - + ); - expect(wrapper.html()).toEqual(''); + expect(wrapper.find('[data-test-subj="cti-disabled-module"]').length).toEqual(1); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.tsx index 5348c12fb6c8e..c89199c2cb0c5 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; +import { TiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; import { CtiEnabledModule } from './cti_enabled_module'; import { CtiDisabledModule } from './cti_disabled_module'; @@ -15,27 +16,26 @@ export type ThreatIntelLinkPanelProps = Pick< GlobalTimeArgs, 'from' | 'to' | 'deleteQuery' | 'setQuery' > & { - isThreatIntelModuleEnabled: boolean | undefined; + allIntegrationsInstalled: boolean | undefined; + allTiDataSources: TiDataSources[]; }; const ThreatIntelLinkPanelComponent: React.FC = (props) => { - switch (props.isThreatIntelModuleEnabled) { - case true: - return ( -
    - -
    - ); - case false: - return ( -
    - -
    - ); - case undefined: - default: - return null; - } + const { allIntegrationsInstalled, allTiDataSources } = props; + const isThreatIntelModuleEnabled = allTiDataSources.length > 0; + return isThreatIntelModuleEnabled ? ( +
    + +
    + ) : ( +
    + +
    + ); }; export const ThreatIntelLinkPanel = React.memo(ThreatIntelLinkPanelComponent); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/mock.ts b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/mock.ts index 1d02acaf65f48..c4cf876cbdc7d 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/mock.ts +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/mock.ts @@ -15,6 +15,13 @@ export const mockTheme = getMockTheme({ }, }); +export const mockTiDataSources = { + totalCount: 5, + tiDataSources: [ + { dataset: 'ti_abusech', name: 'AbuseCH', count: 5, path: '/dashboard_path_abuseurl' }, + ], +}; + export const mockEventCountsByDataset = { abuseurl: 1, abusemalware: 1, @@ -31,8 +38,6 @@ export const mockCtiEventCountsResponse = { }; export const mockCtiLinksResponse = { - isPluginDisabled: false, - buttonHref: '/button', listItems: [ { title: 'abuseurl', count: 1, path: '/dashboard_path_abuseurl' }, { title: 'abusemalware', count: 2, path: '/dashboard_path_abusemalware' }, @@ -63,6 +68,10 @@ export const mockProps = { from: '2020-01-21T20:49:57.080Z', setQuery: jest.fn(), deleteQuery: jest.fn(), + allIntegrationsInstalled: true, + allTiDataSources: [ + { dataset: 'ti_abusech', name: 'AbuseCH', count: 5, path: '/dashboard_path_abuseurl' }, + ], }; export const mockCtiWithEventsProps = { diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx index 189f230c02c8d..3697d27015fdc 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx @@ -9,14 +9,14 @@ import React, { useMemo } from 'react'; import { EuiButton, EuiTableFieldDataColumnType } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useKibana } from '../../../common/lib/kibana'; import * as i18n from './translations'; import { LinkPanel, InnerLinkPanel, LinkPanelListItem } from '../link_panel'; import { LinkPanelViewProps } from '../link_panel/types'; import { shortenCountIntoString } from '../../../common/utils/shorten_count_into_string'; import { Link } from '../link_panel/link'; -import { ID as CTIEventCountQueryId } from '../../containers/overview_cti_links/use_cti_event_counts'; +import { ID as CTIEventCountQueryId } from '../../containers/overview_cti_links/use_ti_data_sources'; import { LINK_COPY } from '../overview_risky_host_links/translations'; +import { useIntegrationsPageLink } from './use_integrations_page_link'; const columns: Array> = [ { name: 'Name', field: 'title', sortable: true, truncateText: true, width: '100%' }, @@ -39,51 +39,43 @@ const columns: Array> = [ ]; export const ThreatIntelPanelView: React.FC = ({ - buttonHref = '', - isPluginDisabled, isInspectEnabled = true, listItems, splitPanel, totalCount = 0, + allIntegrationsInstalled, }) => { - const threatIntelDashboardDocLink = `${ - useKibana().services.docLinks.links.filebeat.base - }/load-kibana-dashboards.html`; + const integrationsLink = useIntegrationsPageLink(); return ( ( - - {i18n.VIEW_DASHBOARD} - - ), - [buttonHref] - ), columns, dataTestSubj: 'cti-dashboard-links', infoPanel: useMemo( - () => - isPluginDisabled ? ( - - {i18n.INFO_BUTTON} - - } - /> - ) : null, - [isPluginDisabled, threatIntelDashboardDocLink] + () => ( + <> + {allIntegrationsInstalled === false ? ( + + {i18n.DANGER_BUTTON} + + } + /> + ) : null} + + ), + [allIntegrationsInstalled, integrationsLink] ), inspectQueryId: isInspectEnabled ? CTIEventCountQueryId : undefined, listItems, diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts index 4a64462b27ad5..e112942b09749 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts @@ -53,15 +53,14 @@ export const DANGER_TITLE = i18n.translate( export const DANGER_BODY = i18n.translate( 'xpack.securitySolution.overview.ctiDashboardEnableThreatIntel', { - defaultMessage: - 'You need to enable the filebeat threatintel module in order to view data from different sources.', + defaultMessage: 'You need to enable threat intel sources in order to view data.', } ); export const DANGER_BUTTON = i18n.translate( - 'xpack.securitySolution.overview.ctiDashboardDangerPanelButton', + 'xpack.securitySolution.overview.ctiDashboardDangerButton', { - defaultMessage: 'Enable Module', + defaultMessage: 'Enable sources', } ); @@ -72,3 +71,17 @@ export const PANEL_TITLE = i18n.translate('xpack.securitySolution.overview.ctiDa export const VIEW_DASHBOARD = i18n.translate('xpack.securitySolution.overview.ctiViewDasboard', { defaultMessage: 'View dashboard', }); + +export const SOME_MODULES_DISABLE_TITLE = i18n.translate( + 'xpack.securitySolution.overview.ctiDashboardSomeModulesDisabledTItle', + { + defaultMessage: 'Some threat intel sources are disabled', + } +); + +export const OTHER_DATA_SOURCE_TITLE = i18n.translate( + 'xpack.securitySolution.overview.ctiDashboardOtherDatasourceTitle', + { + defaultMessage: 'Others', + } +); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/use_integrations_page_link.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/use_integrations_page_link.tsx new file mode 100644 index 0000000000000..de710c2f1b17c --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/use_integrations_page_link.tsx @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useBasePath } from '../../../common/lib/kibana'; + +export const useIntegrationsPageLink = () => + `${useBasePath()}/app/integrations/browse?q=threat%20intelligence`; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/api.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/api.ts new file mode 100644 index 0000000000000..ad737ac410e3b --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/api.ts @@ -0,0 +1,28 @@ +/* + * 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 { KibanaServices } from '../../../common/lib/kibana'; +import { EPM_API_ROUTES } from '../../../../../fleet/common'; + +export interface IntegrationResponse { + id: string; + status: string; + savedObject?: { + attributes?: { + installed_kibana: Array<{ + type: string; + id: string; + }>; + }; + }; +} + +export const fetchFleetIntegrations = () => + KibanaServices.get().http.fetch<{ + response: IntegrationResponse[]; + }>(EPM_API_ROUTES.LIST_PATTERN, { + method: 'GET', + }); diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/helpers.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/helpers.ts deleted file mode 100644 index 9ac61cc9487ee..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/helpers.ts +++ /dev/null @@ -1,60 +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 { SavedObjectAttributes } from '@kbn/securitysolution-io-ts-alerting-types'; -import { CTI_DATASET_KEY_MAP } from '../../../../common/cti/constants'; -import { LinkPanelListItem } from '../../components/link_panel'; -import { EventCounts } from '../../components/link_panel/helpers'; - -export const ctiTitles = Object.keys(CTI_DATASET_KEY_MAP) as string[]; - -export const EMPTY_LIST_ITEMS: LinkPanelListItem[] = ctiTitles.map((title) => ({ - title, - count: 0, - path: '', -})); - -const TAG_REQUEST_BODY_SEARCH = 'threat intel'; -export const TAG_REQUEST_BODY = { - type: 'tag', - search: TAG_REQUEST_BODY_SEARCH, - searchFields: ['name'], -}; - -export const DASHBOARD_SO_TITLE_PREFIX = '[Filebeat Threat Intel] '; -export const OVERVIEW_DASHBOARD_LINK_TITLE = 'Overview'; - -export const getCtiListItemsWithoutLinks = (eventCounts: EventCounts): LinkPanelListItem[] => { - return EMPTY_LIST_ITEMS.map((item) => ({ - ...item, - count: eventCounts[CTI_DATASET_KEY_MAP[item.title]] ?? 0, - })); -}; - -export const isOverviewItem = (item: { path?: string; title?: string }) => - item.title === OVERVIEW_DASHBOARD_LINK_TITLE; - -export const createLinkFromDashboardSO = ( - dashboardSO: { attributes?: SavedObjectAttributes }, - eventCountsByDataset: EventCounts, - path: string -) => { - const title = - typeof dashboardSO.attributes?.title === 'string' - ? dashboardSO.attributes.title.replace(DASHBOARD_SO_TITLE_PREFIX, '') - : undefined; - return { - title, - count: typeof title === 'string' ? eventCountsByDataset[CTI_DATASET_KEY_MAP[title]] : undefined, - path, - }; -}; - -export const emptyEventCountsByDataset = Object.values(CTI_DATASET_KEY_MAP).reduce((acc, id) => { - acc[id] = 0; - return acc; -}, {} as { [key: string]: number }); diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx index a546d20e49583..b1310e363eef0 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx @@ -6,34 +6,29 @@ */ import { useState, useEffect, useCallback } from 'react'; import { SavedObjectAttributes } from '@kbn/securitysolution-io-ts-alerting-types'; +import { TiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; +import { LinkPanelListItem } from '../../components/link_panel'; import { useKibana } from '../../../common/lib/kibana'; -import { - TAG_REQUEST_BODY, - createLinkFromDashboardSO, - getCtiListItemsWithoutLinks, - isOverviewItem, - EMPTY_LIST_ITEMS, -} from './helpers'; -import { LinkPanelListItem, isLinkPanelListItem } from '../../components/link_panel'; -export const useCtiDashboardLinks = ( - eventCountsByDataset: { [key: string]: number }, - to: string, - from: string -) => { - const createDashboardUrl = useKibana().services.dashboard?.dashboardUrlGenerator?.createUrl; - const savedObjectsClient = useKibana().services.savedObjects.client; - - const [buttonHref, setButtonHref] = useState(); - const [listItems, setListItems] = useState(EMPTY_LIST_ITEMS); +const TAG_REQUEST_BODY_SEARCH = 'threat intel'; +export const TAG_REQUEST_BODY = { + type: 'tag', + search: TAG_REQUEST_BODY_SEARCH, + searchFields: ['name'], +}; - const [isPluginDisabled, setIsDashboardPluginDisabled] = useState(false); - const handleDisabledPlugin = useCallback(() => { - if (!isPluginDisabled) { - setIsDashboardPluginDisabled(true); - } - setListItems(getCtiListItemsWithoutLinks(eventCountsByDataset)); - }, [setIsDashboardPluginDisabled, setListItems, eventCountsByDataset, isPluginDisabled]); +export const useCtiDashboardLinks = ({ + to, + from, + tiDataSources = [], +}: { + to: string; + from: string; + tiDataSources?: TiDataSources[]; +}) => { + const [installedDashboardIds, setInstalledDashboardIds] = useState([]); + const dashboardLocator = useKibana().services.dashboard?.locator; + const savedObjectsClient = useKibana().services.savedObjects.client; const handleTagsReceived = useCallback( (TagsSO?) => { @@ -49,9 +44,7 @@ export const useCtiDashboardLinks = ( ); useEffect(() => { - if (!createDashboardUrl || !savedObjectsClient) { - handleDisabledPlugin(); - } else { + if (savedObjectsClient) { savedObjectsClient .find(TAG_REQUEST_BODY) .then(handleTagsReceived) @@ -63,53 +56,40 @@ export const useCtiDashboardLinks = ( }>; }) => { if (DashboardsSO?.savedObjects?.length) { - const dashboardUrls = await Promise.all( - DashboardsSO.savedObjects.map((SO) => - createDashboardUrl({ - dashboardId: SO.id, - timeRange: { - to, - from, - }, - }) - ) + setInstalledDashboardIds( + DashboardsSO.savedObjects.map((SO) => SO.id ?? '').filter(Boolean) ); - const items = DashboardsSO.savedObjects - ?.reduce((acc: LinkPanelListItem[], dashboardSO, i) => { - const item = createLinkFromDashboardSO( - dashboardSO, - eventCountsByDataset, - dashboardUrls[i] - ); - if (isOverviewItem(item)) { - setButtonHref(item.path); - } else if (isLinkPanelListItem(item)) { - acc.push(item); - } - return acc; - }, []) - .sort((a, b) => (a.title > b.title ? 1 : -1)); - setListItems(items); - } else { - handleDisabledPlugin(); } } ); } - }, [ - createDashboardUrl, - eventCountsByDataset, - from, - handleDisabledPlugin, - handleTagsReceived, - isPluginDisabled, - savedObjectsClient, - to, - ]); + }, [handleTagsReceived, savedObjectsClient]); + + const listItems = tiDataSources.map((tiDataSource) => { + const listItem: LinkPanelListItem = { + title: tiDataSource.name, + count: tiDataSource.count, + path: '', + }; + + if ( + tiDataSource.dashboardId && + installedDashboardIds.includes(tiDataSource.dashboardId) && + dashboardLocator + ) { + listItem.path = dashboardLocator.getRedirectUrl({ + dashboardId: tiDataSource.dashboardId, + timeRange: { + to, + from, + }, + }); + } + + return listItem; + }); return { - buttonHref, - isPluginDisabled, listItems, }; }; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_all_ti_data_sources.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_all_ti_data_sources.ts new file mode 100644 index 0000000000000..5686be269121a --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_all_ti_data_sources.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useMemo } from 'react'; +import { useTiDataSources } from './use_ti_data_sources'; + +export const useAllTiDataSources = () => { + const { to, from } = useMemo( + () => ({ + to: new Date().toISOString(), + from: new Date(0).toISOString(), + }), + [] + ); + + const { tiDataSources, isInitiallyLoaded } = useTiDataSources({ to, from }); + + return { tiDataSources, isInitiallyLoaded }; +}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_cti_event_counts.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_cti_event_counts.ts deleted file mode 100644 index c8076ab6a4484..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_cti_event_counts.ts +++ /dev/null @@ -1,64 +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 { useEffect, useState, useMemo } from 'react'; -import { useRequestEventCounts } from './use_request_event_counts'; -import { emptyEventCountsByDataset } from './helpers'; -import { CtiEnabledModuleProps } from '../../components/overview_cti_links/cti_enabled_module'; - -export const ID = 'ctiEventCountQuery'; - -export const useCtiEventCounts = ({ deleteQuery, from, setQuery, to }: CtiEnabledModuleProps) => { - const [isInitialLoading, setIsInitialLoading] = useState(true); - - const [loading, { data, inspect, totalCount, refetch }] = useRequestEventCounts(to, from); - - const eventCountsByDataset = useMemo( - () => - data.reduce( - (acc, item) => { - if (item.y && item.g) { - const id = item.g; - acc[id] += item.y; - } - return acc; - }, - { ...emptyEventCountsByDataset } as { [key: string]: number } - ), - [data] - ); - - useEffect(() => { - if (isInitialLoading && data) { - setIsInitialLoading(false); - } - }, [isInitialLoading, data]); - - useEffect(() => { - if (!loading && !isInitialLoading) { - setQuery({ id: ID, inspect, loading, refetch }); - } - }, [setQuery, inspect, loading, refetch, isInitialLoading, setIsInitialLoading]); - - useEffect(() => { - return () => { - if (deleteQuery) { - deleteQuery({ id: ID }); - } - }; - }, [deleteQuery]); - - useEffect(() => { - refetch(); - }, [to, from, refetch]); - - return { - eventCountsByDataset, - loading, - totalCount, - }; -}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_is_threat_intel_module_enabled.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_is_threat_intel_module_enabled.ts deleted file mode 100644 index 0dc0e8a3fe1f2..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_is_threat_intel_module_enabled.ts +++ /dev/null @@ -1,32 +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 { useState, useEffect, useMemo } from 'react'; -import { useRequestEventCounts } from './use_request_event_counts'; - -export const useIsThreatIntelModuleEnabled = () => { - const [isThreatIntelModuleEnabled, setIsThreatIntelModuleEnabled] = useState< - boolean | undefined - >(); - - const { to, from } = useMemo( - () => ({ - to: new Date().toISOString(), - from: new Date(0).toISOString(), - }), - [] - ); - - const [, { totalCount }] = useRequestEventCounts(to, from); - - useEffect(() => { - if (totalCount !== -1) { - setIsThreatIntelModuleEnabled(totalCount > 0); - } - }, [totalCount]); - - return isThreatIntelModuleEnabled; -}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts deleted file mode 100644 index a1bf4d9d35f65..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts +++ /dev/null @@ -1,54 +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 { useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; -import { convertToBuildEsQuery } from '../../../common/lib/keury'; -import { getEsQueryConfig } from '../../../../../../../src/plugins/data/common'; -import { MatrixHistogramType } from '../../../../common/search_strategy'; -import { EVENT_DATASET } from '../../../../common/cti/constants'; -import { useMatrixHistogram } from '../../../common/containers/matrix_histogram'; -import { useKibana } from '../../../common/lib/kibana'; -import { DEFAULT_THREAT_INDEX_KEY } from '../../../../common/constants'; - -export const useRequestEventCounts = (to: string, from: string) => { - const { uiSettings } = useKibana().services; - const defaultThreatIndices = uiSettings.get(DEFAULT_THREAT_INDEX_KEY); - - const [filterQuery] = convertToBuildEsQuery({ - config: getEsQueryConfig(uiSettings), - indexPattern: { - fields: [ - { - name: 'event.kind', - type: 'string', - }, - ], - title: defaultThreatIndices.toString(), - }, - queries: [{ query: 'event.type:indicator', language: 'kuery' }], - filters: [], - }); - - const matrixHistogramRequest = useMemo(() => { - return { - endDate: to, - errorMessage: i18n.translate('xpack.securitySolution.overview.errorFetchingEvents', { - defaultMessage: 'Error fetching events', - }), - filterQuery, - histogramType: MatrixHistogramType.events, - indexNames: defaultThreatIndices, - stackByField: EVENT_DATASET, - startDate: from, - size: 0, - }; - }, [to, from, filterQuery, defaultThreatIndices]); - - const results = useMatrixHistogram(matrixHistogramRequest); - - return results; -}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts new file mode 100644 index 0000000000000..865af2266f2e0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts @@ -0,0 +1,174 @@ +/* + * 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 { Observable } from 'rxjs'; +import { filter } from 'rxjs/operators'; +import { useEffect, useState } from 'react'; +import { useObservable, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; +import { useKibana } from '../../../common/lib/kibana'; +import { + DataPublicPluginStart, + isCompleteResponse, + isErrorResponse, +} from '../../../../../../../src/plugins/data/public'; +import { + Bucket, + CtiQueries, + CtiDataSourceStrategyResponse, + CtiDataSourceRequestOptions, +} from '../../../../common'; +import { DEFAULT_THREAT_INDEX_KEY } from '../../../../common/constants'; +import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; +import { OTHER_DATA_SOURCE_TITLE } from '../../components/overview_cti_links/translations'; +import { OTHER_TI_DATASET_KEY } from '../../../../common/cti/constants'; + +type GetThreatIntelSourcProps = CtiDataSourceRequestOptions & { + data: DataPublicPluginStart; + signal: AbortSignal; +}; +export const ID = 'ctiEventCountQuery'; + +export const getTiDataSources = ({ + data, + defaultIndex, + timerange, + signal, +}: GetThreatIntelSourcProps): Observable => + data.search.search( + { + defaultIndex, + factoryQueryType: CtiQueries.dataSource, + timerange, + }, + { + strategy: 'securitySolutionSearchStrategy', + abortSignal: signal, + } + ); + +export const getTiDataSourcesComplete = ( + props: GetThreatIntelSourcProps +): Observable => { + return getTiDataSources(props).pipe( + filter((response) => { + return isErrorResponse(response) || isCompleteResponse(response); + }) + ); +}; + +const getTiDataSourcesWithOptionalSignal = withOptionalSignal(getTiDataSourcesComplete); + +export const useTiDataSourcesComplete = () => useObservable(getTiDataSourcesWithOptionalSignal); + +export interface TiDataSources { + dataset: string; + name: string; + count: number; + dashboardId?: string; +} +interface TiDataSourcesProps extends Partial { + allTiDataSources?: TiDataSources[]; +} + +export const useTiDataSources = ({ + to, + from, + allTiDataSources, + setQuery, + deleteQuery, +}: TiDataSourcesProps) => { + const [tiDataSources, setTiDataSources] = useState([]); + const [isInitiallyLoaded, setIsInitiallyLoaded] = useState(false); + const { data, uiSettings } = useKibana().services; + const defaultThreatIndices = uiSettings.get(DEFAULT_THREAT_INDEX_KEY); + const { result, start, loading } = useTiDataSourcesComplete(); + + useEffect(() => { + start({ + data, + timerange: to && from ? { to, from, interval: '' } : undefined, + defaultIndex: defaultThreatIndices, + }); + }, [to, from, start, data, defaultThreatIndices]); + + useEffect(() => { + if (!loading && result?.rawResponse && result?.inspect && setQuery) { + setQuery({ + id: ID, + inspect: { + dsl: result?.inspect?.dsl ?? [], + response: [JSON.stringify(result.rawResponse, null, 2)], + }, + loading, + refetch: () => {}, + }); + } + }, [setQuery, loading, result]); + + useEffect(() => { + return () => { + if (deleteQuery) { + deleteQuery({ id: ID }); + } + }; + }, [deleteQuery]); + + useEffect(() => { + if (result && !isInitiallyLoaded) { + setIsInitiallyLoaded(true); + } + }, [isInitiallyLoaded, result]); + + useEffect(() => { + if (!loading && result) { + const datasets = result?.rawResponse?.aggregations?.dataset?.buckets ?? []; + const getChildAggregationValue = (aggregation?: Bucket) => aggregation?.buckets?.[0]?.key; + + const integrationMap = datasets.reduce((acc: Record, dataset) => { + const datasetName = getChildAggregationValue(dataset?.name); + if (datasetName) { + return { + ...acc, + [dataset.key]: { + dataset: dataset?.key, + name: datasetName, + dashboardId: getChildAggregationValue(dataset?.dashboard), + count: dataset?.doc_count, + }, + }; + } else { + const otherTiDatasetKey = OTHER_TI_DATASET_KEY; + const otherDatasetCount = acc[otherTiDatasetKey]?.count ?? 0; + return { + ...acc, + [otherTiDatasetKey]: { + dataset: otherTiDatasetKey, + name: OTHER_DATA_SOURCE_TITLE, + count: otherDatasetCount + (dataset?.doc_count ?? 0), + }, + }; + } + }, {}); + + if (Array.isArray(allTiDataSources)) { + allTiDataSources.forEach((integration) => { + if (!integrationMap[integration.dataset]) { + integrationMap[integration.dataset] = { + ...integration, + count: 0, + }; + } + }); + } + + setTiDataSources(Object.values(integrationMap)); + } + }, [result, loading, allTiDataSources]); + + const totalCount = tiDataSources.reduce((acc, val) => acc + val.count, 0); + + return { tiDataSources, totalCount, isInitiallyLoaded }; +}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_integrations.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_integrations.ts new file mode 100644 index 0000000000000..24bdc191b3d66 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_integrations.ts @@ -0,0 +1,55 @@ +/* + * 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 { useEffect, useState } from 'react'; + +import { installationStatuses } from '../../../../../fleet/common'; +import { TI_INTEGRATION_PREFIX } from '../../../../common/cti/constants'; +import { fetchFleetIntegrations, IntegrationResponse } from './api'; + +export interface Integration { + id: string; + dashboardIds: string[]; +} + +interface TiIntegrationStatus { + allIntegrationsInstalled: boolean; +} + +export const useTiIntegrations = () => { + const [tiIntegrationsStatus, setTiIntegrationsStatus] = useState( + null + ); + + useEffect(() => { + const getPackages = async () => { + try { + const { response: integrations } = await fetchFleetIntegrations(); + const tiIntegrations = integrations.filter((integration: IntegrationResponse) => + integration.id.startsWith(TI_INTEGRATION_PREFIX) + ); + + const allIntegrationsInstalled = tiIntegrations.every( + (integration: IntegrationResponse) => + integration.status === installationStatuses.Installed + ); + + setTiIntegrationsStatus({ + allIntegrationsInstalled, + }); + } catch (e) { + setTiIntegrationsStatus({ + allIntegrationsInstalled: false, + }); + } + }; + + getPackages(); + }, []); + + return tiIntegrationsStatus; +}; diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx index 2539490be16fb..b38072464c653 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx @@ -23,12 +23,9 @@ import { } from '../../common/components/user_privileges'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useFetchIndex } from '../../common/containers/source'; -import { useIsThreatIntelModuleEnabled } from '../containers/overview_cti_links/use_is_threat_intel_module_enabled'; -import { useCtiEventCounts } from '../containers/overview_cti_links/use_cti_event_counts'; -import { - mockCtiEventCountsResponse, - mockCtiLinksResponse, -} from '../components/overview_cti_links/mock'; +import { useAllTiDataSources } from '../containers/overview_cti_links/use_all_ti_data_sources'; +import { useTiIntegrations } from '../containers/overview_cti_links/use_ti_integrations'; +import { mockCtiLinksResponse, mockTiDataSources } from '../components/overview_cti_links/mock'; import { useCtiDashboardLinks } from '../containers/overview_cti_links'; import { EndpointPrivileges } from '../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; @@ -73,18 +70,17 @@ jest.mock('../../common/components/user_privileges', () => { jest.mock('../../common/containers/local_storage/use_messages_storage'); jest.mock('../containers/overview_cti_links'); -jest.mock('../containers/overview_cti_links/use_cti_event_counts'); const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; useCtiDashboardLinksMock.mockReturnValue(mockCtiLinksResponse); -jest.mock('../containers/overview_cti_links/use_cti_event_counts'); -const useCTIEventCountsMock = useCtiEventCounts as jest.Mock; -useCTIEventCountsMock.mockReturnValue(mockCtiEventCountsResponse); +jest.mock('../containers/overview_cti_links/use_all_ti_data_sources'); +const useAllTiDataSourcesMock = useAllTiDataSources as jest.Mock; +useAllTiDataSourcesMock.mockReturnValue(mockTiDataSources); -jest.mock('../containers/overview_cti_links/use_is_threat_intel_module_enabled'); -const useIsThreatIntelModuleEnabledMock = useIsThreatIntelModuleEnabled as jest.Mock; -useIsThreatIntelModuleEnabledMock.mockReturnValue(true); +jest.mock('../containers/overview_cti_links/use_ti_integrations'); +const useTiIntegrationsMock = useTiIntegrations as jest.Mock; +useTiIntegrationsMock.mockReturnValue({}); jest.mock('../containers/overview_risky_host_links/use_hosts_risk_score'); const useHostsRiskScoreMock = useHostsRiskScore as jest.Mock; @@ -303,8 +299,8 @@ describe('Overview', () => { }); describe('Threat Intel Dashboard Links', () => { - it('invokes useIsThreatIntelModuleEnabled hook only once', () => { - useIsThreatIntelModuleEnabledMock.mockClear(); + it('invokes useAllTiDataSourcesMock hook only once', () => { + useAllTiDataSourcesMock.mockClear(); mount( @@ -312,7 +308,7 @@ describe('Overview', () => { ); - expect(useIsThreatIntelModuleEnabledMock).toHaveBeenCalledTimes(1); + expect(useAllTiDataSourcesMock).toHaveBeenCalledTimes(1); }); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx index 67ee6c55ac06f..1df49fed07358 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx @@ -30,7 +30,8 @@ import { ENDPOINT_METADATA_INDEX } from '../../../common/constants'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useDeepEqualSelector } from '../../common/hooks/use_selector'; import { ThreatIntelLinkPanel } from '../components/overview_cti_links'; -import { useIsThreatIntelModuleEnabled } from '../containers/overview_cti_links/use_is_threat_intel_module_enabled'; +import { useAllTiDataSources } from '../containers/overview_cti_links/use_all_ti_data_sources'; +import { useTiIntegrations } from '../containers/overview_cti_links/use_ti_integrations'; import { useUserPrivileges } from '../../common/components/user_privileges'; import { RiskyHostLinks } from '../components/overview_risky_host_links'; import { useAlertsPrivileges } from '../../detections/containers/detection_engine/alerts/use_alerts_privileges'; @@ -75,7 +76,10 @@ const OverviewComponent = () => { endpointPrivileges: { canAccessFleet }, } = useUserPrivileges(); const { hasIndexRead, hasKibanaREAD } = useAlertsPrivileges(); - const isThreatIntelModuleEnabled = useIsThreatIntelModuleEnabled(); + const { tiDataSources: allTiDataSources, isInitiallyLoaded: allTiDataSourcesLoaded } = + useAllTiDataSources(); + const tiIntegrationStatus = useTiIntegrations(); + const isTiLoaded = tiIntegrationStatus && allTiDataSourcesLoaded; const riskyHostsEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); @@ -150,13 +154,16 @@ const OverviewComponent = () => { - + {isTiLoaded && ( + + )} {riskyHostsEnabled && ( diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/index.ts index 5857a0417239c..e43af97e84af0 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/index.ts @@ -9,7 +9,9 @@ import type { FactoryQueryTypes } from '../../../../../common/search_strategy/se import { CtiQueries } from '../../../../../common/search_strategy/security_solution/cti'; import type { SecuritySolutionFactory } from '../types'; import { eventEnrichment } from './event_enrichment'; +import { dataSource } from './threat_intel_source'; export const ctiFactoryTypes: Record> = { [CtiQueries.eventEnrichment]: eventEnrichment, + [CtiQueries.dataSource]: dataSource, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/index.ts new file mode 100644 index 0000000000000..0951503b04cd4 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SecuritySolutionFactory } from '../../types'; +import { + CtiDataSourceStrategyResponse, + CtiQueries, + CtiDataSourceRequestOptions, +} from '../../../../../../common'; +import { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; +import { inspectStringifyObject } from '../../../../../utils/build_query'; +import { buildTiDataSourceQuery } from './query.threat_intel_source.dsl'; + +export const dataSource: SecuritySolutionFactory = { + buildDsl: (options: CtiDataSourceRequestOptions) => buildTiDataSourceQuery(options), + parse: async ( + options: CtiDataSourceRequestOptions, + response: IEsSearchResponse + ): Promise => { + const inspect = { + dsl: [inspectStringifyObject(buildTiDataSourceQuery(options))], + }; + + return { + ...response, + inspect, + }; + }, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts new file mode 100644 index 0000000000000..832006930a326 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts @@ -0,0 +1,71 @@ +/* + * 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 { buildTiDataSourceQuery } from './query.threat_intel_source.dsl'; +import { CtiQueries } from '../../../../../../common'; + +export const mockOptions = { + defaultIndex: ['logs-ti_*', 'filebeat-8*'], + docValueFields: [], + factoryQueryType: CtiQueries.dataSource, + filterQuery: '', + timerange: { + interval: '12h', + from: '2020-09-06T15:23:52.757Z', + to: '2020-09-07T15:23:52.757Z', + }, +}; + +export const expectedDsl = { + body: { + aggs: { + dataset: { + terms: { + field: 'event.dataset', + }, + aggs: { + name: { + terms: { + field: 'threat.feed.name', + }, + }, + dashboard: { + terms: { + field: 'threat.feed.dashboard_id', + }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: '2020-09-06T15:23:52.757Z', + lte: '2020-09-07T15:23:52.757Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + }, + ignore_unavailable: true, + index: ['logs-ti_*', 'filebeat-8*'], + size: 0, + track_total_hits: true, + allow_no_indices: true, +}; + +describe('buildbuildTiDataSourceQueryQuery', () => { + test('build query from options correctly', () => { + expect(buildTiDataSourceQuery(mockOptions)).toEqual(expectedDsl); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.ts new file mode 100644 index 0000000000000..08463146a683e --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.ts @@ -0,0 +1,59 @@ +/* + * 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 { CtiDataSourceRequestOptions } from '../../../../../../common'; + +export const buildTiDataSourceQuery = ({ + timerange, + defaultIndex, +}: CtiDataSourceRequestOptions) => { + const filter = []; + + if (timerange) { + filter.push({ + range: { + '@timestamp': { + gte: timerange.from, + lte: timerange.to, + format: 'strict_date_optional_time', + }, + }, + }); + } + + const dslQuery = { + size: 0, + index: defaultIndex, + allow_no_indices: true, + ignore_unavailable: true, + track_total_hits: true, + body: { + aggs: { + dataset: { + terms: { field: 'event.dataset' }, + aggs: { + name: { + terms: { field: 'threat.feed.name' }, + }, + dashboard: { + terms: { + field: 'threat.feed.dashboard_id', + }, + }, + }, + }, + }, + query: { + bool: { + filter, + }, + }, + }, + }; + + return dslQuery; +}; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 76d3f07facf05..58d04788e98eb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -23438,7 +23438,6 @@ "xpack.securitySolution.overview.auditBeatProcessTitle": "プロセス", "xpack.securitySolution.overview.auditBeatSocketTitle": "ソケット", "xpack.securitySolution.overview.auditBeatUserTitle": "ユーザー", - "xpack.securitySolution.overview.ctiDashboardDangerPanelButton": "モジュールを有効にする", "xpack.securitySolution.overview.ctiDashboardDangerPanelTitle": "表示する脅威インテリジェンスデータがありません", "xpack.securitySolution.overview.ctiDashboardEnableThreatIntel": "別のソースからデータを表示するには、filebeat脅威インテリジェンスモジュールを有効にする必要があります。", "xpack.securitySolution.overview.ctiDashboardInfoPanelBody": "このガイドに従い、ダッシュボードを有効にして、ビジュアライゼーションにソースを表示できるようにしてください。", @@ -23460,7 +23459,6 @@ "xpack.securitySolution.overview.endpointNotice.message": "脅威防御、検出、深いセキュリティデータの可視化を実現し、ホストを保護します。", "xpack.securitySolution.overview.endpointNotice.title": "Endpoint Security", "xpack.securitySolution.overview.endpointNotice.tryButton": "Endpoint Securityを試す", - "xpack.securitySolution.overview.errorFetchingEvents": "イベントの取得エラー", "xpack.securitySolution.overview.eventsTitle": "イベント数", "xpack.securitySolution.overview.filebeatCiscoTitle": "Cisco", "xpack.securitySolution.overview.filebeatNetflowTitle": "Netflow", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 01997e32f243e..da71c1796066f 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -23828,7 +23828,6 @@ "xpack.securitySolution.overview.auditBeatProcessTitle": "进程", "xpack.securitySolution.overview.auditBeatSocketTitle": "套接字", "xpack.securitySolution.overview.auditBeatUserTitle": "用户", - "xpack.securitySolution.overview.ctiDashboardDangerPanelButton": "启用模块", "xpack.securitySolution.overview.ctiDashboardDangerPanelTitle": "没有可显示的威胁情报数据", "xpack.securitySolution.overview.ctiDashboardEnableThreatIntel": "您需要启用 filebeat threatintel 模块,以便查看不同源的数据。", "xpack.securitySolution.overview.ctiDashboardInfoPanelBody": "按照此指南启用您的仪表板,以便可以在可视化中查看您的源。", @@ -23851,7 +23850,6 @@ "xpack.securitySolution.overview.endpointNotice.message": "使用威胁防御、检测和深度安全数据可见性功能保护您的主机。", "xpack.securitySolution.overview.endpointNotice.title": "Endpoint Security", "xpack.securitySolution.overview.endpointNotice.tryButton": "试用 Endpoint Security", - "xpack.securitySolution.overview.errorFetchingEvents": "提取事件时出错", "xpack.securitySolution.overview.eventsTitle": "事件计数", "xpack.securitySolution.overview.filebeatCiscoTitle": "Cisco", "xpack.securitySolution.overview.filebeatNetflowTitle": "NetFlow", diff --git a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json index a2e0c2d2921dc..ec5e2aae6e2e2 100644 --- a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json @@ -31,6 +31,9 @@ } }, "type": "file" + }, + "feed": { + "name": "AbuseCH malware" } }, "abusemalware": { @@ -72,4 +75,4 @@ } } } -} +} \ No newline at end of file diff --git a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json index 8840cd4bee0dd..bc5f6e3db9169 100644 --- a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json @@ -796,6 +796,14 @@ "type": "keyword" } } + }, + "feed":{ + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } } } } From 4ad3044daedb66319d2231d4b62b332880fb54d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Fri, 3 Dec 2021 15:35:36 +0100 Subject: [PATCH 42/65] [Security solution] [Endpoint] Adds missing parenthesis to fix trusted apps query (#120326) * Initial commit to add search bar for trusted apps in policy view page * Retrieve all assigned trusted apps to ensure if there are something assigned without the search filters or not. Also fixes unit tests * remove useless if condition * Adds more unit tests and fixes some pr suggestions * Fix weird bug when loading empty state * Fix ts errors due changes in api mocks * Fixes unit test * Remove grid loader to use paginated results one. Fix selectors and tests * Remove unused imports due ts errors * remove unused import * Adds parenthesis to fix a query error. Also fixes related unit test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../policy_details/middleware/policy_trusted_apps_middleware.ts | 2 +- .../trusted_apps/layout/policy_trusted_apps_layout.test.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_trusted_apps_middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_trusted_apps_middleware.ts index e9cbda1f487cb..1630d63aee5c6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_trusted_apps_middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_trusted_apps_middleware.ts @@ -157,7 +157,7 @@ const checkIfPolicyHasTrustedAppsAssigned = async ( } try { const policyId = policyIdFromParams(state); - const kuery = `exception-list-agnostic.attributes.tags:"policy:${policyId}" OR exception-list-agnostic.attributes.tags:"policy:all"`; + const kuery = `(exception-list-agnostic.attributes.tags:"policy:${policyId}" OR exception-list-agnostic.attributes.tags:"policy:all")`; const trustedApps = await trustedAppsService.getTrustedAppsList({ page: 1, per_page: 100, diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx index 40997de054c7f..089bbd4bcb4e8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx @@ -136,7 +136,7 @@ describe('Policy trusted apps layout', () => { mockedApis.responseProvider.trustedAppsList.mockImplementation( (options: HttpFetchOptionsWithPath) => { const hasAnyQuery = - 'exception-list-agnostic.attributes.tags:"policy:1234" OR exception-list-agnostic.attributes.tags:"policy:all"'; + '(exception-list-agnostic.attributes.tags:"policy:1234" OR exception-list-agnostic.attributes.tags:"policy:all")'; if (options.query?.filter === hasAnyQuery) { const exceptionsGenerator = new ExceptionsListItemGenerator('seed'); return { From 3b516b6dd1e1fc38ca931f7c04b44e39fa679d24 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Fri, 3 Dec 2021 15:39:48 +0100 Subject: [PATCH 43/65] :bug: Keep the behindText color flag for treemap (#120228) --- .../plugins/lens/public/pie_visualization/render_function.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index 539d69207f5f9..3b9fdaf094822 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -186,7 +186,7 @@ export function PieComponent( const outputColor = paletteService.get(palette.name).getCategoricalColor( seriesLayers, { - behindText: categoryDisplay !== 'hide', + behindText: categoryDisplay !== 'hide' || isTreemapOrMosaicShape(shape), maxDepth: bucketColumns.length, totalSeries: totalSeriesCount, syncColors, From a0db43e07bfe611bc7d89a234c8511a2a49769fd Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Fri, 3 Dec 2021 10:50:17 -0500 Subject: [PATCH 44/65] Revert " [CTI] Threat Intel Card on Overview page needs to accommodate Fleet TI integrations (#115940)" This reverts commit 6640357eb6e2a4b5db39e468dc315bce74109259. --- .../security_solution/common/cti/constants.ts | 13 +- .../security_solution/cti/index.ts | 37 +--- .../security_solution/index.ts | 7 - .../overview/cti_link_panel.spec.ts | 13 +- .../cypress/screens/overview.ts | 4 +- .../overview/components/link_panel/helpers.ts | 7 + .../overview/components/link_panel/index.ts | 1 + .../components/link_panel/link_panel.tsx | 20 +- .../overview/components/link_panel/types.ts | 1 - .../cti_disabled_module.tsx | 11 +- .../cti_enabled_module.test.tsx | 49 ++++- .../overview_cti_links/cti_enabled_module.tsx | 49 +++-- .../overview_cti_links/cti_no_events.test.tsx | 70 +++++++ .../overview_cti_links/cti_no_events.tsx | 42 +++++ .../cti_with_events.test.tsx | 57 ++++++ .../overview_cti_links/cti_with_events.tsx | 49 +++++ .../overview_cti_links/index.test.tsx | 38 ++-- .../components/overview_cti_links/index.tsx | 36 ++-- .../components/overview_cti_links/mock.ts | 13 +- .../threat_intel_panel_view.tsx | 62 ++++--- .../overview_cti_links/translations.ts | 21 +-- .../use_integrations_page_link.tsx | 11 -- .../containers/overview_cti_links/api.ts | 28 --- .../containers/overview_cti_links/helpers.ts | 60 ++++++ .../containers/overview_cti_links/index.tsx | 116 +++++++----- .../use_all_ti_data_sources.ts | 22 --- .../use_cti_event_counts.ts | 64 +++++++ .../use_is_threat_intel_module_enabled.ts | 32 ++++ .../use_request_event_counts.ts | 54 ++++++ .../overview_cti_links/use_ti_data_sources.ts | 174 ------------------ .../overview_cti_links/use_ti_integrations.ts | 55 ------ .../public/overview/pages/overview.test.tsx | 28 +-- .../public/overview/pages/overview.tsx | 25 +-- .../security_solution/factory/cti/index.ts | 2 - .../factory/cti/threat_intel_source/index.ts | 33 ---- .../query.threat_intel_source.dsl.test.ts | 71 ------- .../query.threat_intel_source.dsl.ts | 59 ------ .../translations/translations/ja-JP.json | 2 + .../translations/translations/zh-CN.json | 2 + .../es_archives/threat_indicator/data.json | 5 +- .../threat_indicator/mappings.json | 8 - 41 files changed, 720 insertions(+), 731 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.test.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.test.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/components/overview_cti_links/use_integrations_page_link.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/api.ts create mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/helpers.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_all_ti_data_sources.ts create mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_cti_event_counts.ts create mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_is_threat_intel_module_enabled.ts create mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_integrations.ts delete mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/index.ts delete mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts delete mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.ts diff --git a/x-pack/plugins/security_solution/common/cti/constants.ts b/x-pack/plugins/security_solution/common/cti/constants.ts index 7a88b065d8701..b33541c5057d8 100644 --- a/x-pack/plugins/security_solution/common/cti/constants.ts +++ b/x-pack/plugins/security_solution/common/cti/constants.ts @@ -58,5 +58,14 @@ export const EVENT_ENRICHMENT_INDICATOR_FIELD_MAP = { export const DEFAULT_EVENT_ENRICHMENT_FROM = 'now-30d'; export const DEFAULT_EVENT_ENRICHMENT_TO = 'now'; -export const TI_INTEGRATION_PREFIX = 'ti'; -export const OTHER_TI_DATASET_KEY = '_others_ti_'; +export const CTI_DATASET_KEY_MAP: { [key: string]: string } = { + 'AbuseCH URL': 'ti_abusech.url', + 'AbuseCH Malware': 'ti_abusech.malware', + 'AbuseCH MalwareBazaar': 'ti_abusech.malwarebazaar', + 'AlienVault OTX': 'ti_otx.threat', + 'Anomali Limo': 'ti_anomali.limo', + 'Anomali Threatstream': 'ti_anomali.threatstream', + MISP: 'ti_misp.threat', + ThreatQuotient: 'ti_threatq.threat', + Cybersixgill: 'ti_cybersixgill.threat', +}; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts index a6e7eef88724b..26bf4ce6740a9 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts @@ -5,16 +5,13 @@ * 2.0. */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { IEsSearchResponse, IEsSearchRequest } from 'src/plugins/data/public'; -import { FactoryQueryTypes } from '../..'; +import type { IEsSearchResponse } from 'src/plugins/data/public'; import { EVENT_ENRICHMENT_INDICATOR_FIELD_MAP } from '../../../cti/constants'; -import { Inspect, Maybe, TimerangeInput } from '../../common'; +import { Inspect } from '../../common'; import { RequestBasicOptions } from '..'; export enum CtiQueries { eventEnrichment = 'eventEnrichment', - dataSource = 'dataSource', } export interface CtiEventEnrichmentRequestOptions extends RequestBasicOptions { @@ -43,33 +40,3 @@ export const validEventFields = Object.keys(EVENT_ENRICHMENT_INDICATOR_FIELD_MAP export const isValidEventField = (field: string): field is EventField => validEventFields.includes(field as EventField); - -export interface CtiDataSourceRequestOptions extends IEsSearchRequest { - defaultIndex: string[]; - factoryQueryType?: FactoryQueryTypes; - timerange?: TimerangeInput; -} - -export interface BucketItem { - key: string; - doc_count: number; -} -export interface Bucket { - buckets: Array; -} - -export type DatasetBucket = { - name?: Bucket; - dashboard?: Bucket; -} & BucketItem; - -export interface CtiDataSourceStrategyResponse extends Omit { - inspect?: Maybe; - rawResponse: { - aggregations?: Record & { - dataset?: { - buckets: DatasetBucket[]; - }; - }; - }; -} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts index 340093995b297..00cbdb941c11b 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts @@ -72,8 +72,6 @@ import { CtiEventEnrichmentRequestOptions, CtiEventEnrichmentStrategyResponse, CtiQueries, - CtiDataSourceRequestOptions, - CtiDataSourceStrategyResponse, } from './cti'; import { HostRulesRequestOptions, @@ -87,7 +85,6 @@ import { UserRulesStrategyResponse, } from './ueba'; -export * from './cti'; export * from './hosts'; export * from './matrix_histogram'; export * from './network'; @@ -181,8 +178,6 @@ export type StrategyResponseType = T extends HostsQ ? MatrixHistogramStrategyResponse : T extends CtiQueries.eventEnrichment ? CtiEventEnrichmentStrategyResponse - : T extends CtiQueries.dataSource - ? CtiDataSourceStrategyResponse : never; export type StrategyRequestType = T extends HostsQueries.hosts @@ -243,8 +238,6 @@ export type StrategyRequestType = T extends HostsQu ? MatrixHistogramRequestOptions : T extends CtiQueries.eventEnrichment ? CtiEventEnrichmentRequestOptions - : T extends CtiQueries.dataSource - ? CtiDataSourceRequestOptions : never; export interface DocValueFieldsInput { diff --git a/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts b/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts index 75ff13b66b29c..095401ff31422 100644 --- a/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts @@ -10,8 +10,9 @@ import { OVERVIEW_CTI_LINKS, OVERVIEW_CTI_LINKS_ERROR_INNER_PANEL, OVERVIEW_CTI_LINKS_INFO_INNER_PANEL, + OVERVIEW_CTI_LINKS_WARNING_INNER_PANEL, OVERVIEW_CTI_TOTAL_EVENT_COUNT, - OVERVIEW_CTI_ENABLE_INTEGRATIONS_BUTTON, + OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON, } from '../../screens/overview'; import { loginAndWaitForPage } from '../../tasks/login'; @@ -27,11 +28,12 @@ describe('CTI Link Panel', () => { it('renders disabled threat intel module as expected', () => { loginAndWaitForPage(OVERVIEW_URL); cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_ERROR_INNER_PANEL}`).should('exist'); + cy.get(`${OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 indicators'); cy.get(`${OVERVIEW_CTI_ENABLE_MODULE_BUTTON}`).should('exist'); cy.get(`${OVERVIEW_CTI_ENABLE_MODULE_BUTTON}`) .should('have.attr', 'href') - .and('match', /app\/integrations\/browse\?q=threat%20intelligence/); + .and('match', /filebeat-module-threatintel.html/); }); describe('enabled threat intel module', () => { @@ -47,16 +49,17 @@ describe('CTI Link Panel', () => { loginAndWaitForPage( `${OVERVIEW_URL}?sourcerer=(timerange:(from:%272021-07-08T04:00:00.000Z%27,kind:absolute,to:%272021-07-09T03:59:59.999Z%27))` ); + cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_WARNING_INNER_PANEL}`).should('exist'); cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_INFO_INNER_PANEL}`).should('exist'); + cy.get(`${OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 indicators'); }); it('renders dashboard module as expected when there are events in the selected time period', () => { loginAndWaitForPage(OVERVIEW_URL); + cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_WARNING_INNER_PANEL}`).should('not.exist'); cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_LINKS_INFO_INNER_PANEL}`).should('exist'); - cy.get(`${OVERVIEW_CTI_LINKS} ${OVERVIEW_CTI_ENABLE_INTEGRATIONS_BUTTON}`).should('exist'); - cy.get(OVERVIEW_CTI_LINKS).should('not.contain.text', 'Anomali'); - cy.get(OVERVIEW_CTI_LINKS).should('contain.text', 'AbuseCH malware'); + cy.get(`${OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 1 indicator'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/overview.ts b/x-pack/plugins/security_solution/cypress/screens/overview.ts index bc335ff6680ee..1945b7e3ce3e7 100644 --- a/x-pack/plugins/security_solution/cypress/screens/overview.ts +++ b/x-pack/plugins/security_solution/cypress/screens/overview.ts @@ -150,9 +150,9 @@ export const OVERVIEW_REVENT_TIMELINES = '[data-test-subj="overview-recent-timel export const OVERVIEW_CTI_LINKS = '[data-test-subj="cti-dashboard-links"]'; export const OVERVIEW_CTI_LINKS_ERROR_INNER_PANEL = '[data-test-subj="cti-inner-panel-danger"]'; +export const OVERVIEW_CTI_LINKS_WARNING_INNER_PANEL = '[data-test-subj="cti-inner-panel-warning"]'; export const OVERVIEW_CTI_LINKS_INFO_INNER_PANEL = '[data-test-subj="cti-inner-panel-info"]'; -export const OVERVIEW_CTI_ENABLE_INTEGRATIONS_BUTTON = - '[data-test-subj="cti-enable-integrations-button"]'; +export const OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON = '[data-test-subj="cti-view-dashboard-button"]'; export const OVERVIEW_CTI_TOTAL_EVENT_COUNT = `${OVERVIEW_CTI_LINKS} [data-test-subj="header-panel-subtitle"]`; export const OVERVIEW_CTI_ENABLE_MODULE_BUTTON = '[data-test-subj="cti-enable-module-button"]'; diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/helpers.ts b/x-pack/plugins/security_solution/public/overview/components/link_panel/helpers.ts index e2adaaae35547..45d26d9269f6e 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/helpers.ts +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/helpers.ts @@ -5,6 +5,13 @@ * 2.0. */ +import { LinkPanelListItem } from '.'; + +export const isLinkPanelListItem = ( + item: LinkPanelListItem | Partial +): item is LinkPanelListItem => + typeof item.title === 'string' && typeof item.path === 'string' && typeof item.count === 'number'; + export interface EventCounts { [key: string]: number; } diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/index.ts b/x-pack/plugins/security_solution/public/overview/components/link_panel/index.ts index 9a827b137ae78..9d404abcf2223 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/index.ts +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/index.ts @@ -6,5 +6,6 @@ */ export { InnerLinkPanel } from './inner_link_panel'; +export { isLinkPanelListItem } from './helpers'; export { LinkPanel } from './link_panel'; export type { LinkPanelListItem } from './types'; diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/link_panel.tsx b/x-pack/plugins/security_solution/public/overview/components/link_panel/link_panel.tsx index 00a225635fb8b..ed67fdb1c96f6 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/link_panel.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/link_panel.tsx @@ -71,7 +71,7 @@ const LinkPanelComponent = ({ splitPanel, subtitle, }: { - button?: React.ReactNode; + button: React.ReactNode; columns: Array>; dataTestSubj: string; defaultSortField?: string; @@ -134,16 +134,14 @@ const LinkPanelComponent = ({ {splitPanel} {infoPanel} - {chunkedItems.length > 0 && ( - - )} + diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/types.ts b/x-pack/plugins/security_solution/public/overview/components/link_panel/types.ts index 1b8836fc2438d..f6c0fb6f3837f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/types.ts +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/types.ts @@ -21,5 +21,4 @@ export interface LinkPanelViewProps { listItems: LinkPanelListItem[]; splitPanel?: JSX.Element; totalCount?: number; - allIntegrationsInstalled?: boolean; } diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx index 36f386e49c5c7..2697e4a571ad8 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx @@ -6,21 +6,24 @@ */ import React from 'react'; +import { EMPTY_LIST_ITEMS } from '../../containers/overview_cti_links/helpers'; +import { useKibana } from '../../../common/lib/kibana'; import * as i18n from './translations'; import { DisabledLinkPanel } from '../link_panel/disabled_link_panel'; import { ThreatIntelPanelView } from './threat_intel_panel_view'; -import { useIntegrationsPageLink } from './use_integrations_page_link'; export const CtiDisabledModuleComponent = () => { - const integrationsLink = useIntegrationsPageLink(); + const threatIntelDocLink = `${ + useKibana().services.docLinks.links.filebeat.base + }/filebeat-module-threatintel.html`; return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.test.tsx index fc36a0c4337cf..db83d9e1bcfe5 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.test.tsx @@ -19,15 +19,20 @@ import { mockGlobalState, SUB_PLUGINS_REDUCER, } from '../../../common/mock'; -import { mockTheme, mockProps, mockTiDataSources, mockCtiLinksResponse } from './mock'; +import { mockTheme, mockProps, mockCtiEventCountsResponse, mockCtiLinksResponse } from './mock'; +import { useCtiEventCounts } from '../../containers/overview_cti_links/use_cti_event_counts'; import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; -import { useTiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; +import { useRequestEventCounts } from '../../containers/overview_cti_links/use_request_event_counts'; jest.mock('../../../common/lib/kibana'); -jest.mock('../../containers/overview_cti_links/use_ti_data_sources'); -const useTiDataSourcesMock = useTiDataSources as jest.Mock; -useTiDataSourcesMock.mockReturnValue(mockTiDataSources); +jest.mock('../../containers/overview_cti_links/use_cti_event_counts'); +const useCTIEventCountsMock = useCtiEventCounts as jest.Mock; +useCTIEventCountsMock.mockReturnValue(mockCtiEventCountsResponse); + +jest.mock('../../containers/overview_cti_links/use_request_event_counts'); +const useRequestEventCountsMock = useRequestEventCounts as jest.Mock; +useRequestEventCountsMock.mockReturnValue([true, {}]); jest.mock('../../containers/overview_cti_links'); const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; @@ -49,12 +54,42 @@ describe('CtiEnabledModule', () => { - + + + + + ); + + expect(screen.getByTestId('cti-with-events')).toBeInTheDocument(); + }); + + it('renders CtiWithNoEvents when there are no events', () => { + useCTIEventCountsMock.mockReturnValueOnce({ totalCount: 0 }); + render( + + + + + + + + ); + + expect(screen.getByTestId('cti-with-no-events')).toBeInTheDocument(); + }); + + it('renders null while event counts are loading', () => { + useCTIEventCountsMock.mockReturnValueOnce({ totalCount: -1 }); + const { container } = render( + + + + ); - expect(screen.getByText('Showing: 5 indicators')).toBeInTheDocument(); + expect(container.firstChild).toBeNull(); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.tsx index a339676ac361f..5a40c79d6e5ec 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_enabled_module.tsx @@ -7,28 +7,37 @@ import React from 'react'; import { ThreatIntelLinkPanelProps } from '.'; -import { useTiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; -import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; -import { ThreatIntelPanelView } from './threat_intel_panel_view'; +import { useCtiEventCounts } from '../../containers/overview_cti_links/use_cti_event_counts'; +import { CtiNoEvents } from './cti_no_events'; +import { CtiWithEvents } from './cti_with_events'; -export const CtiEnabledModuleComponent: React.FC = (props) => { - const { to, from, allIntegrationsInstalled, allTiDataSources, setQuery, deleteQuery } = props; - const { tiDataSources, totalCount } = useTiDataSources({ - to, - from, - allTiDataSources, - setQuery, - deleteQuery, - }); - const { listItems } = useCtiDashboardLinks({ to, from, tiDataSources }); +export type CtiEnabledModuleProps = Omit; - return ( - - ); +export const CtiEnabledModuleComponent: React.FC = (props) => { + const { eventCountsByDataset, totalCount } = useCtiEventCounts(props); + const { to, from } = props; + + switch (totalCount) { + case -1: + return null; + case 0: + return ( +
    + +
    + ); + default: + return ( +
    + +
    + ); + } }; export const CtiEnabledModule = React.memo(CtiEnabledModuleComponent); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.test.tsx new file mode 100644 index 0000000000000..8f624dabd64d1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.test.tsx @@ -0,0 +1,70 @@ +/* + * 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 { Provider } from 'react-redux'; +import { cloneDeep } from 'lodash/fp'; +import { render, screen } from '@testing-library/react'; +import { I18nProvider } from '@kbn/i18n-react'; +import { CtiNoEvents } from './cti_no_events'; +import { ThemeProvider } from 'styled-components'; +import { createStore, State } from '../../../common/store'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, +} from '../../../common/mock'; +import { mockEmptyCtiLinksResponse, mockTheme, mockProps } from './mock'; +import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; + +jest.mock('../../../common/lib/kibana'); + +jest.mock('../../containers/overview_cti_links'); +const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; +useCtiDashboardLinksMock.mockReturnValue(mockEmptyCtiLinksResponse); + +describe('CtiNoEvents', () => { + const state: State = mockGlobalState; + + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + beforeEach(() => { + const myState = cloneDeep(state); + store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + }); + + it('renders warning inner panel', () => { + render( + + + + + + + + ); + + expect(screen.getByTestId('cti-dashboard-links')).toBeInTheDocument(); + expect(screen.getByTestId('cti-inner-panel-warning')).toBeInTheDocument(); + }); + + it('renders event counts as 0', () => { + render( + + + + + + + + ); + + expect(screen.getByText('Showing: 0 indicators')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.tsx new file mode 100644 index 0000000000000..fa7ac50c08765 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_no_events.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; +import { ThreatIntelPanelView } from './threat_intel_panel_view'; +import { InnerLinkPanel } from '../link_panel'; +import * as i18n from './translations'; +import { emptyEventCountsByDataset } from '../../containers/overview_cti_links/helpers'; + +const warning = ( + +); + +export const CtiNoEventsComponent = ({ to, from }: { to: string; from: string }) => { + const { buttonHref, listItems, isPluginDisabled } = useCtiDashboardLinks( + emptyEventCountsByDataset, + to, + from + ); + + return ( + + ); +}; + +export const CtiNoEvents = React.memo(CtiNoEventsComponent); +CtiNoEvents.displayName = 'CtiNoEvents'; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.test.tsx new file mode 100644 index 0000000000000..a50e3e91ab9e5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.test.tsx @@ -0,0 +1,57 @@ +/* + * 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 { Provider } from 'react-redux'; +import { cloneDeep } from 'lodash/fp'; +import { mount } from 'enzyme'; +import { I18nProvider } from '@kbn/i18n-react'; +import { CtiWithEvents } from './cti_with_events'; +import { ThemeProvider } from 'styled-components'; +import { createStore, State } from '../../../common/store'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, +} from '../../../common/mock'; +import { mockCtiLinksResponse, mockTheme, mockCtiWithEventsProps } from './mock'; +import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; + +jest.mock('../../../common/lib/kibana'); + +jest.mock('../../containers/overview_cti_links'); +const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; +useCtiDashboardLinksMock.mockReturnValue(mockCtiLinksResponse); + +describe('CtiWithEvents', () => { + const state: State = mockGlobalState; + + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + beforeEach(() => { + const myState = cloneDeep(state); + store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + }); + + it('renders total event count as expected', () => { + const wrapper = mount( + + + + + + + + ); + + expect(wrapper.find('[data-test-subj="cti-total-event-count"]').text()).toEqual( + `Showing: ${mockCtiWithEventsProps.totalCount} indicators` + ); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.tsx new file mode 100644 index 0000000000000..f78451e205b1e --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_with_events.tsx @@ -0,0 +1,49 @@ +/* + * 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 { isEqual } from 'lodash'; +import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; +import { ThreatIntelPanelView } from './threat_intel_panel_view'; + +export const CtiWithEventsComponent = ({ + eventCountsByDataset, + from, + to, + totalCount, +}: { + eventCountsByDataset: { [key: string]: number }; + from: string; + to: string; + totalCount: number; +}) => { + const { buttonHref, isPluginDisabled, listItems } = useCtiDashboardLinks( + eventCountsByDataset, + to, + from + ); + + return ( + + ); +}; + +CtiWithEventsComponent.displayName = 'CtiWithEvents'; + +export const CtiWithEvents = React.memo( + CtiWithEventsComponent, + (prevProps, nextProps) => + prevProps.to === nextProps.to && + prevProps.from === nextProps.from && + prevProps.totalCount === nextProps.totalCount && + isEqual(prevProps.eventCountsByDataset, nextProps.eventCountsByDataset) +); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.test.tsx index 71d6d5eb0c583..dfd9c6c9a7fcd 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.test.tsx @@ -19,19 +19,19 @@ import { mockGlobalState, SUB_PLUGINS_REDUCER, } from '../../../common/mock'; -import { mockTheme, mockProps, mockTiDataSources, mockCtiLinksResponse } from './mock'; -import { useTiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; -import { useCtiDashboardLinks } from '../../containers/overview_cti_links'; +import { mockTheme, mockProps, mockCtiEventCountsResponse } from './mock'; +import { useRequestEventCounts } from '../../containers/overview_cti_links/use_request_event_counts'; +import { useCtiEventCounts } from '../../containers/overview_cti_links/use_cti_event_counts'; jest.mock('../../../common/lib/kibana'); -jest.mock('../../containers/overview_cti_links/use_ti_data_sources'); -const useTiDataSourcesMock = useTiDataSources as jest.Mock; -useTiDataSourcesMock.mockReturnValue(mockTiDataSources); +jest.mock('../../containers/overview_cti_links/use_request_event_counts'); +const useRequestEventCountsMock = useRequestEventCounts as jest.Mock; +useRequestEventCountsMock.mockReturnValue([true, {}]); -jest.mock('../../containers/overview_cti_links'); -const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; -useCtiDashboardLinksMock.mockReturnValue(mockCtiLinksResponse); +jest.mock('../../containers/overview_cti_links/use_cti_event_counts'); +const useCTIEventCountsMock = useCtiEventCounts as jest.Mock; +useCTIEventCountsMock.mockReturnValue(mockCtiEventCountsResponse); describe('ThreatIntelLinkPanel', () => { const state: State = mockGlobalState; @@ -49,44 +49,40 @@ describe('ThreatIntelLinkPanel', () => { - + ); expect(wrapper.find('[data-test-subj="cti-enabled-module"]').length).toEqual(1); - expect(wrapper.find('[data-test-subj="cti-enable-integrations-button"]').length).toEqual(0); }); - it('renders Enable source buttons when not all integrations installed', () => { + it('renders CtiDisabledModule when Threat Intel module is disabled', () => { const wrapper = mount( - + ); - expect(wrapper.find('[data-test-subj="cti-enable-integrations-button"]').length).not.toBe(0); + + expect(wrapper.find('[data-test-subj="cti-disabled-module"]').length).toEqual(1); }); - it('renders CtiDisabledModule when Threat Intel module is disabled', () => { + it('renders null while Threat Intel module state is loading', () => { const wrapper = mount( - + ); - expect(wrapper.find('[data-test-subj="cti-disabled-module"]').length).toEqual(1); + expect(wrapper.html()).toEqual(''); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.tsx index c89199c2cb0c5..5348c12fb6c8e 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/index.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; -import { TiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; import { CtiEnabledModule } from './cti_enabled_module'; import { CtiDisabledModule } from './cti_disabled_module'; @@ -16,26 +15,27 @@ export type ThreatIntelLinkPanelProps = Pick< GlobalTimeArgs, 'from' | 'to' | 'deleteQuery' | 'setQuery' > & { - allIntegrationsInstalled: boolean | undefined; - allTiDataSources: TiDataSources[]; + isThreatIntelModuleEnabled: boolean | undefined; }; const ThreatIntelLinkPanelComponent: React.FC = (props) => { - const { allIntegrationsInstalled, allTiDataSources } = props; - const isThreatIntelModuleEnabled = allTiDataSources.length > 0; - return isThreatIntelModuleEnabled ? ( -
    - -
    - ) : ( -
    - -
    - ); + switch (props.isThreatIntelModuleEnabled) { + case true: + return ( +
    + +
    + ); + case false: + return ( +
    + +
    + ); + case undefined: + default: + return null; + } }; export const ThreatIntelLinkPanel = React.memo(ThreatIntelLinkPanelComponent); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/mock.ts b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/mock.ts index c4cf876cbdc7d..1d02acaf65f48 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/mock.ts +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/mock.ts @@ -15,13 +15,6 @@ export const mockTheme = getMockTheme({ }, }); -export const mockTiDataSources = { - totalCount: 5, - tiDataSources: [ - { dataset: 'ti_abusech', name: 'AbuseCH', count: 5, path: '/dashboard_path_abuseurl' }, - ], -}; - export const mockEventCountsByDataset = { abuseurl: 1, abusemalware: 1, @@ -38,6 +31,8 @@ export const mockCtiEventCountsResponse = { }; export const mockCtiLinksResponse = { + isPluginDisabled: false, + buttonHref: '/button', listItems: [ { title: 'abuseurl', count: 1, path: '/dashboard_path_abuseurl' }, { title: 'abusemalware', count: 2, path: '/dashboard_path_abusemalware' }, @@ -68,10 +63,6 @@ export const mockProps = { from: '2020-01-21T20:49:57.080Z', setQuery: jest.fn(), deleteQuery: jest.fn(), - allIntegrationsInstalled: true, - allTiDataSources: [ - { dataset: 'ti_abusech', name: 'AbuseCH', count: 5, path: '/dashboard_path_abuseurl' }, - ], }; export const mockCtiWithEventsProps = { diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx index 3697d27015fdc..189f230c02c8d 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx @@ -9,14 +9,14 @@ import React, { useMemo } from 'react'; import { EuiButton, EuiTableFieldDataColumnType } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useKibana } from '../../../common/lib/kibana'; import * as i18n from './translations'; import { LinkPanel, InnerLinkPanel, LinkPanelListItem } from '../link_panel'; import { LinkPanelViewProps } from '../link_panel/types'; import { shortenCountIntoString } from '../../../common/utils/shorten_count_into_string'; import { Link } from '../link_panel/link'; -import { ID as CTIEventCountQueryId } from '../../containers/overview_cti_links/use_ti_data_sources'; +import { ID as CTIEventCountQueryId } from '../../containers/overview_cti_links/use_cti_event_counts'; import { LINK_COPY } from '../overview_risky_host_links/translations'; -import { useIntegrationsPageLink } from './use_integrations_page_link'; const columns: Array> = [ { name: 'Name', field: 'title', sortable: true, truncateText: true, width: '100%' }, @@ -39,43 +39,51 @@ const columns: Array> = [ ]; export const ThreatIntelPanelView: React.FC = ({ + buttonHref = '', + isPluginDisabled, isInspectEnabled = true, listItems, splitPanel, totalCount = 0, - allIntegrationsInstalled, }) => { - const integrationsLink = useIntegrationsPageLink(); + const threatIntelDashboardDocLink = `${ + useKibana().services.docLinks.links.filebeat.base + }/load-kibana-dashboards.html`; return ( ( + + {i18n.VIEW_DASHBOARD} + + ), + [buttonHref] + ), columns, dataTestSubj: 'cti-dashboard-links', infoPanel: useMemo( - () => ( - <> - {allIntegrationsInstalled === false ? ( - - {i18n.DANGER_BUTTON} - - } - /> - ) : null} - - ), - [allIntegrationsInstalled, integrationsLink] + () => + isPluginDisabled ? ( + + {i18n.INFO_BUTTON} + + } + /> + ) : null, + [isPluginDisabled, threatIntelDashboardDocLink] ), inspectQueryId: isInspectEnabled ? CTIEventCountQueryId : undefined, listItems, diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts index e112942b09749..4a64462b27ad5 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts @@ -53,14 +53,15 @@ export const DANGER_TITLE = i18n.translate( export const DANGER_BODY = i18n.translate( 'xpack.securitySolution.overview.ctiDashboardEnableThreatIntel', { - defaultMessage: 'You need to enable threat intel sources in order to view data.', + defaultMessage: + 'You need to enable the filebeat threatintel module in order to view data from different sources.', } ); export const DANGER_BUTTON = i18n.translate( - 'xpack.securitySolution.overview.ctiDashboardDangerButton', + 'xpack.securitySolution.overview.ctiDashboardDangerPanelButton', { - defaultMessage: 'Enable sources', + defaultMessage: 'Enable Module', } ); @@ -71,17 +72,3 @@ export const PANEL_TITLE = i18n.translate('xpack.securitySolution.overview.ctiDa export const VIEW_DASHBOARD = i18n.translate('xpack.securitySolution.overview.ctiViewDasboard', { defaultMessage: 'View dashboard', }); - -export const SOME_MODULES_DISABLE_TITLE = i18n.translate( - 'xpack.securitySolution.overview.ctiDashboardSomeModulesDisabledTItle', - { - defaultMessage: 'Some threat intel sources are disabled', - } -); - -export const OTHER_DATA_SOURCE_TITLE = i18n.translate( - 'xpack.securitySolution.overview.ctiDashboardOtherDatasourceTitle', - { - defaultMessage: 'Others', - } -); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/use_integrations_page_link.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/use_integrations_page_link.tsx deleted file mode 100644 index de710c2f1b17c..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/use_integrations_page_link.tsx +++ /dev/null @@ -1,11 +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 { useBasePath } from '../../../common/lib/kibana'; - -export const useIntegrationsPageLink = () => - `${useBasePath()}/app/integrations/browse?q=threat%20intelligence`; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/api.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/api.ts deleted file mode 100644 index ad737ac410e3b..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/api.ts +++ /dev/null @@ -1,28 +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 { KibanaServices } from '../../../common/lib/kibana'; -import { EPM_API_ROUTES } from '../../../../../fleet/common'; - -export interface IntegrationResponse { - id: string; - status: string; - savedObject?: { - attributes?: { - installed_kibana: Array<{ - type: string; - id: string; - }>; - }; - }; -} - -export const fetchFleetIntegrations = () => - KibanaServices.get().http.fetch<{ - response: IntegrationResponse[]; - }>(EPM_API_ROUTES.LIST_PATTERN, { - method: 'GET', - }); diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/helpers.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/helpers.ts new file mode 100644 index 0000000000000..9ac61cc9487ee --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/helpers.ts @@ -0,0 +1,60 @@ +/* + * 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 { SavedObjectAttributes } from '@kbn/securitysolution-io-ts-alerting-types'; +import { CTI_DATASET_KEY_MAP } from '../../../../common/cti/constants'; +import { LinkPanelListItem } from '../../components/link_panel'; +import { EventCounts } from '../../components/link_panel/helpers'; + +export const ctiTitles = Object.keys(CTI_DATASET_KEY_MAP) as string[]; + +export const EMPTY_LIST_ITEMS: LinkPanelListItem[] = ctiTitles.map((title) => ({ + title, + count: 0, + path: '', +})); + +const TAG_REQUEST_BODY_SEARCH = 'threat intel'; +export const TAG_REQUEST_BODY = { + type: 'tag', + search: TAG_REQUEST_BODY_SEARCH, + searchFields: ['name'], +}; + +export const DASHBOARD_SO_TITLE_PREFIX = '[Filebeat Threat Intel] '; +export const OVERVIEW_DASHBOARD_LINK_TITLE = 'Overview'; + +export const getCtiListItemsWithoutLinks = (eventCounts: EventCounts): LinkPanelListItem[] => { + return EMPTY_LIST_ITEMS.map((item) => ({ + ...item, + count: eventCounts[CTI_DATASET_KEY_MAP[item.title]] ?? 0, + })); +}; + +export const isOverviewItem = (item: { path?: string; title?: string }) => + item.title === OVERVIEW_DASHBOARD_LINK_TITLE; + +export const createLinkFromDashboardSO = ( + dashboardSO: { attributes?: SavedObjectAttributes }, + eventCountsByDataset: EventCounts, + path: string +) => { + const title = + typeof dashboardSO.attributes?.title === 'string' + ? dashboardSO.attributes.title.replace(DASHBOARD_SO_TITLE_PREFIX, '') + : undefined; + return { + title, + count: typeof title === 'string' ? eventCountsByDataset[CTI_DATASET_KEY_MAP[title]] : undefined, + path, + }; +}; + +export const emptyEventCountsByDataset = Object.values(CTI_DATASET_KEY_MAP).reduce((acc, id) => { + acc[id] = 0; + return acc; +}, {} as { [key: string]: number }); diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx index b1310e363eef0..a546d20e49583 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx @@ -6,30 +6,35 @@ */ import { useState, useEffect, useCallback } from 'react'; import { SavedObjectAttributes } from '@kbn/securitysolution-io-ts-alerting-types'; -import { TiDataSources } from '../../containers/overview_cti_links/use_ti_data_sources'; -import { LinkPanelListItem } from '../../components/link_panel'; import { useKibana } from '../../../common/lib/kibana'; +import { + TAG_REQUEST_BODY, + createLinkFromDashboardSO, + getCtiListItemsWithoutLinks, + isOverviewItem, + EMPTY_LIST_ITEMS, +} from './helpers'; +import { LinkPanelListItem, isLinkPanelListItem } from '../../components/link_panel'; -const TAG_REQUEST_BODY_SEARCH = 'threat intel'; -export const TAG_REQUEST_BODY = { - type: 'tag', - search: TAG_REQUEST_BODY_SEARCH, - searchFields: ['name'], -}; - -export const useCtiDashboardLinks = ({ - to, - from, - tiDataSources = [], -}: { - to: string; - from: string; - tiDataSources?: TiDataSources[]; -}) => { - const [installedDashboardIds, setInstalledDashboardIds] = useState([]); - const dashboardLocator = useKibana().services.dashboard?.locator; +export const useCtiDashboardLinks = ( + eventCountsByDataset: { [key: string]: number }, + to: string, + from: string +) => { + const createDashboardUrl = useKibana().services.dashboard?.dashboardUrlGenerator?.createUrl; const savedObjectsClient = useKibana().services.savedObjects.client; + const [buttonHref, setButtonHref] = useState(); + const [listItems, setListItems] = useState(EMPTY_LIST_ITEMS); + + const [isPluginDisabled, setIsDashboardPluginDisabled] = useState(false); + const handleDisabledPlugin = useCallback(() => { + if (!isPluginDisabled) { + setIsDashboardPluginDisabled(true); + } + setListItems(getCtiListItemsWithoutLinks(eventCountsByDataset)); + }, [setIsDashboardPluginDisabled, setListItems, eventCountsByDataset, isPluginDisabled]); + const handleTagsReceived = useCallback( (TagsSO?) => { if (TagsSO?.savedObjects?.length) { @@ -44,7 +49,9 @@ export const useCtiDashboardLinks = ({ ); useEffect(() => { - if (savedObjectsClient) { + if (!createDashboardUrl || !savedObjectsClient) { + handleDisabledPlugin(); + } else { savedObjectsClient .find(TAG_REQUEST_BODY) .then(handleTagsReceived) @@ -56,40 +63,53 @@ export const useCtiDashboardLinks = ({ }>; }) => { if (DashboardsSO?.savedObjects?.length) { - setInstalledDashboardIds( - DashboardsSO.savedObjects.map((SO) => SO.id ?? '').filter(Boolean) + const dashboardUrls = await Promise.all( + DashboardsSO.savedObjects.map((SO) => + createDashboardUrl({ + dashboardId: SO.id, + timeRange: { + to, + from, + }, + }) + ) ); + const items = DashboardsSO.savedObjects + ?.reduce((acc: LinkPanelListItem[], dashboardSO, i) => { + const item = createLinkFromDashboardSO( + dashboardSO, + eventCountsByDataset, + dashboardUrls[i] + ); + if (isOverviewItem(item)) { + setButtonHref(item.path); + } else if (isLinkPanelListItem(item)) { + acc.push(item); + } + return acc; + }, []) + .sort((a, b) => (a.title > b.title ? 1 : -1)); + setListItems(items); + } else { + handleDisabledPlugin(); } } ); } - }, [handleTagsReceived, savedObjectsClient]); - - const listItems = tiDataSources.map((tiDataSource) => { - const listItem: LinkPanelListItem = { - title: tiDataSource.name, - count: tiDataSource.count, - path: '', - }; - - if ( - tiDataSource.dashboardId && - installedDashboardIds.includes(tiDataSource.dashboardId) && - dashboardLocator - ) { - listItem.path = dashboardLocator.getRedirectUrl({ - dashboardId: tiDataSource.dashboardId, - timeRange: { - to, - from, - }, - }); - } - - return listItem; - }); + }, [ + createDashboardUrl, + eventCountsByDataset, + from, + handleDisabledPlugin, + handleTagsReceived, + isPluginDisabled, + savedObjectsClient, + to, + ]); return { + buttonHref, + isPluginDisabled, listItems, }; }; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_all_ti_data_sources.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_all_ti_data_sources.ts deleted file mode 100644 index 5686be269121a..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_all_ti_data_sources.ts +++ /dev/null @@ -1,22 +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 { useMemo } from 'react'; -import { useTiDataSources } from './use_ti_data_sources'; - -export const useAllTiDataSources = () => { - const { to, from } = useMemo( - () => ({ - to: new Date().toISOString(), - from: new Date(0).toISOString(), - }), - [] - ); - - const { tiDataSources, isInitiallyLoaded } = useTiDataSources({ to, from }); - - return { tiDataSources, isInitiallyLoaded }; -}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_cti_event_counts.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_cti_event_counts.ts new file mode 100644 index 0000000000000..c8076ab6a4484 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_cti_event_counts.ts @@ -0,0 +1,64 @@ +/* + * 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 { useEffect, useState, useMemo } from 'react'; +import { useRequestEventCounts } from './use_request_event_counts'; +import { emptyEventCountsByDataset } from './helpers'; +import { CtiEnabledModuleProps } from '../../components/overview_cti_links/cti_enabled_module'; + +export const ID = 'ctiEventCountQuery'; + +export const useCtiEventCounts = ({ deleteQuery, from, setQuery, to }: CtiEnabledModuleProps) => { + const [isInitialLoading, setIsInitialLoading] = useState(true); + + const [loading, { data, inspect, totalCount, refetch }] = useRequestEventCounts(to, from); + + const eventCountsByDataset = useMemo( + () => + data.reduce( + (acc, item) => { + if (item.y && item.g) { + const id = item.g; + acc[id] += item.y; + } + return acc; + }, + { ...emptyEventCountsByDataset } as { [key: string]: number } + ), + [data] + ); + + useEffect(() => { + if (isInitialLoading && data) { + setIsInitialLoading(false); + } + }, [isInitialLoading, data]); + + useEffect(() => { + if (!loading && !isInitialLoading) { + setQuery({ id: ID, inspect, loading, refetch }); + } + }, [setQuery, inspect, loading, refetch, isInitialLoading, setIsInitialLoading]); + + useEffect(() => { + return () => { + if (deleteQuery) { + deleteQuery({ id: ID }); + } + }; + }, [deleteQuery]); + + useEffect(() => { + refetch(); + }, [to, from, refetch]); + + return { + eventCountsByDataset, + loading, + totalCount, + }; +}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_is_threat_intel_module_enabled.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_is_threat_intel_module_enabled.ts new file mode 100644 index 0000000000000..0dc0e8a3fe1f2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_is_threat_intel_module_enabled.ts @@ -0,0 +1,32 @@ +/* + * 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 { useState, useEffect, useMemo } from 'react'; +import { useRequestEventCounts } from './use_request_event_counts'; + +export const useIsThreatIntelModuleEnabled = () => { + const [isThreatIntelModuleEnabled, setIsThreatIntelModuleEnabled] = useState< + boolean | undefined + >(); + + const { to, from } = useMemo( + () => ({ + to: new Date().toISOString(), + from: new Date(0).toISOString(), + }), + [] + ); + + const [, { totalCount }] = useRequestEventCounts(to, from); + + useEffect(() => { + if (totalCount !== -1) { + setIsThreatIntelModuleEnabled(totalCount > 0); + } + }, [totalCount]); + + return isThreatIntelModuleEnabled; +}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts new file mode 100644 index 0000000000000..a1bf4d9d35f65 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts @@ -0,0 +1,54 @@ +/* + * 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 { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { convertToBuildEsQuery } from '../../../common/lib/keury'; +import { getEsQueryConfig } from '../../../../../../../src/plugins/data/common'; +import { MatrixHistogramType } from '../../../../common/search_strategy'; +import { EVENT_DATASET } from '../../../../common/cti/constants'; +import { useMatrixHistogram } from '../../../common/containers/matrix_histogram'; +import { useKibana } from '../../../common/lib/kibana'; +import { DEFAULT_THREAT_INDEX_KEY } from '../../../../common/constants'; + +export const useRequestEventCounts = (to: string, from: string) => { + const { uiSettings } = useKibana().services; + const defaultThreatIndices = uiSettings.get(DEFAULT_THREAT_INDEX_KEY); + + const [filterQuery] = convertToBuildEsQuery({ + config: getEsQueryConfig(uiSettings), + indexPattern: { + fields: [ + { + name: 'event.kind', + type: 'string', + }, + ], + title: defaultThreatIndices.toString(), + }, + queries: [{ query: 'event.type:indicator', language: 'kuery' }], + filters: [], + }); + + const matrixHistogramRequest = useMemo(() => { + return { + endDate: to, + errorMessage: i18n.translate('xpack.securitySolution.overview.errorFetchingEvents', { + defaultMessage: 'Error fetching events', + }), + filterQuery, + histogramType: MatrixHistogramType.events, + indexNames: defaultThreatIndices, + stackByField: EVENT_DATASET, + startDate: from, + size: 0, + }; + }, [to, from, filterQuery, defaultThreatIndices]); + + const results = useMatrixHistogram(matrixHistogramRequest); + + return results; +}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts deleted file mode 100644 index 865af2266f2e0..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts +++ /dev/null @@ -1,174 +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 { Observable } from 'rxjs'; -import { filter } from 'rxjs/operators'; -import { useEffect, useState } from 'react'; -import { useObservable, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; -import { useKibana } from '../../../common/lib/kibana'; -import { - DataPublicPluginStart, - isCompleteResponse, - isErrorResponse, -} from '../../../../../../../src/plugins/data/public'; -import { - Bucket, - CtiQueries, - CtiDataSourceStrategyResponse, - CtiDataSourceRequestOptions, -} from '../../../../common'; -import { DEFAULT_THREAT_INDEX_KEY } from '../../../../common/constants'; -import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; -import { OTHER_DATA_SOURCE_TITLE } from '../../components/overview_cti_links/translations'; -import { OTHER_TI_DATASET_KEY } from '../../../../common/cti/constants'; - -type GetThreatIntelSourcProps = CtiDataSourceRequestOptions & { - data: DataPublicPluginStart; - signal: AbortSignal; -}; -export const ID = 'ctiEventCountQuery'; - -export const getTiDataSources = ({ - data, - defaultIndex, - timerange, - signal, -}: GetThreatIntelSourcProps): Observable => - data.search.search( - { - defaultIndex, - factoryQueryType: CtiQueries.dataSource, - timerange, - }, - { - strategy: 'securitySolutionSearchStrategy', - abortSignal: signal, - } - ); - -export const getTiDataSourcesComplete = ( - props: GetThreatIntelSourcProps -): Observable => { - return getTiDataSources(props).pipe( - filter((response) => { - return isErrorResponse(response) || isCompleteResponse(response); - }) - ); -}; - -const getTiDataSourcesWithOptionalSignal = withOptionalSignal(getTiDataSourcesComplete); - -export const useTiDataSourcesComplete = () => useObservable(getTiDataSourcesWithOptionalSignal); - -export interface TiDataSources { - dataset: string; - name: string; - count: number; - dashboardId?: string; -} -interface TiDataSourcesProps extends Partial { - allTiDataSources?: TiDataSources[]; -} - -export const useTiDataSources = ({ - to, - from, - allTiDataSources, - setQuery, - deleteQuery, -}: TiDataSourcesProps) => { - const [tiDataSources, setTiDataSources] = useState([]); - const [isInitiallyLoaded, setIsInitiallyLoaded] = useState(false); - const { data, uiSettings } = useKibana().services; - const defaultThreatIndices = uiSettings.get(DEFAULT_THREAT_INDEX_KEY); - const { result, start, loading } = useTiDataSourcesComplete(); - - useEffect(() => { - start({ - data, - timerange: to && from ? { to, from, interval: '' } : undefined, - defaultIndex: defaultThreatIndices, - }); - }, [to, from, start, data, defaultThreatIndices]); - - useEffect(() => { - if (!loading && result?.rawResponse && result?.inspect && setQuery) { - setQuery({ - id: ID, - inspect: { - dsl: result?.inspect?.dsl ?? [], - response: [JSON.stringify(result.rawResponse, null, 2)], - }, - loading, - refetch: () => {}, - }); - } - }, [setQuery, loading, result]); - - useEffect(() => { - return () => { - if (deleteQuery) { - deleteQuery({ id: ID }); - } - }; - }, [deleteQuery]); - - useEffect(() => { - if (result && !isInitiallyLoaded) { - setIsInitiallyLoaded(true); - } - }, [isInitiallyLoaded, result]); - - useEffect(() => { - if (!loading && result) { - const datasets = result?.rawResponse?.aggregations?.dataset?.buckets ?? []; - const getChildAggregationValue = (aggregation?: Bucket) => aggregation?.buckets?.[0]?.key; - - const integrationMap = datasets.reduce((acc: Record, dataset) => { - const datasetName = getChildAggregationValue(dataset?.name); - if (datasetName) { - return { - ...acc, - [dataset.key]: { - dataset: dataset?.key, - name: datasetName, - dashboardId: getChildAggregationValue(dataset?.dashboard), - count: dataset?.doc_count, - }, - }; - } else { - const otherTiDatasetKey = OTHER_TI_DATASET_KEY; - const otherDatasetCount = acc[otherTiDatasetKey]?.count ?? 0; - return { - ...acc, - [otherTiDatasetKey]: { - dataset: otherTiDatasetKey, - name: OTHER_DATA_SOURCE_TITLE, - count: otherDatasetCount + (dataset?.doc_count ?? 0), - }, - }; - } - }, {}); - - if (Array.isArray(allTiDataSources)) { - allTiDataSources.forEach((integration) => { - if (!integrationMap[integration.dataset]) { - integrationMap[integration.dataset] = { - ...integration, - count: 0, - }; - } - }); - } - - setTiDataSources(Object.values(integrationMap)); - } - }, [result, loading, allTiDataSources]); - - const totalCount = tiDataSources.reduce((acc, val) => acc + val.count, 0); - - return { tiDataSources, totalCount, isInitiallyLoaded }; -}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_integrations.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_integrations.ts deleted file mode 100644 index 24bdc191b3d66..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_integrations.ts +++ /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 { useEffect, useState } from 'react'; - -import { installationStatuses } from '../../../../../fleet/common'; -import { TI_INTEGRATION_PREFIX } from '../../../../common/cti/constants'; -import { fetchFleetIntegrations, IntegrationResponse } from './api'; - -export interface Integration { - id: string; - dashboardIds: string[]; -} - -interface TiIntegrationStatus { - allIntegrationsInstalled: boolean; -} - -export const useTiIntegrations = () => { - const [tiIntegrationsStatus, setTiIntegrationsStatus] = useState( - null - ); - - useEffect(() => { - const getPackages = async () => { - try { - const { response: integrations } = await fetchFleetIntegrations(); - const tiIntegrations = integrations.filter((integration: IntegrationResponse) => - integration.id.startsWith(TI_INTEGRATION_PREFIX) - ); - - const allIntegrationsInstalled = tiIntegrations.every( - (integration: IntegrationResponse) => - integration.status === installationStatuses.Installed - ); - - setTiIntegrationsStatus({ - allIntegrationsInstalled, - }); - } catch (e) { - setTiIntegrationsStatus({ - allIntegrationsInstalled: false, - }); - } - }; - - getPackages(); - }, []); - - return tiIntegrationsStatus; -}; diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx index b38072464c653..2539490be16fb 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx @@ -23,9 +23,12 @@ import { } from '../../common/components/user_privileges'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useFetchIndex } from '../../common/containers/source'; -import { useAllTiDataSources } from '../containers/overview_cti_links/use_all_ti_data_sources'; -import { useTiIntegrations } from '../containers/overview_cti_links/use_ti_integrations'; -import { mockCtiLinksResponse, mockTiDataSources } from '../components/overview_cti_links/mock'; +import { useIsThreatIntelModuleEnabled } from '../containers/overview_cti_links/use_is_threat_intel_module_enabled'; +import { useCtiEventCounts } from '../containers/overview_cti_links/use_cti_event_counts'; +import { + mockCtiEventCountsResponse, + mockCtiLinksResponse, +} from '../components/overview_cti_links/mock'; import { useCtiDashboardLinks } from '../containers/overview_cti_links'; import { EndpointPrivileges } from '../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; @@ -70,17 +73,18 @@ jest.mock('../../common/components/user_privileges', () => { jest.mock('../../common/containers/local_storage/use_messages_storage'); jest.mock('../containers/overview_cti_links'); +jest.mock('../containers/overview_cti_links/use_cti_event_counts'); const useCtiDashboardLinksMock = useCtiDashboardLinks as jest.Mock; useCtiDashboardLinksMock.mockReturnValue(mockCtiLinksResponse); -jest.mock('../containers/overview_cti_links/use_all_ti_data_sources'); -const useAllTiDataSourcesMock = useAllTiDataSources as jest.Mock; -useAllTiDataSourcesMock.mockReturnValue(mockTiDataSources); +jest.mock('../containers/overview_cti_links/use_cti_event_counts'); +const useCTIEventCountsMock = useCtiEventCounts as jest.Mock; +useCTIEventCountsMock.mockReturnValue(mockCtiEventCountsResponse); -jest.mock('../containers/overview_cti_links/use_ti_integrations'); -const useTiIntegrationsMock = useTiIntegrations as jest.Mock; -useTiIntegrationsMock.mockReturnValue({}); +jest.mock('../containers/overview_cti_links/use_is_threat_intel_module_enabled'); +const useIsThreatIntelModuleEnabledMock = useIsThreatIntelModuleEnabled as jest.Mock; +useIsThreatIntelModuleEnabledMock.mockReturnValue(true); jest.mock('../containers/overview_risky_host_links/use_hosts_risk_score'); const useHostsRiskScoreMock = useHostsRiskScore as jest.Mock; @@ -299,8 +303,8 @@ describe('Overview', () => { }); describe('Threat Intel Dashboard Links', () => { - it('invokes useAllTiDataSourcesMock hook only once', () => { - useAllTiDataSourcesMock.mockClear(); + it('invokes useIsThreatIntelModuleEnabled hook only once', () => { + useIsThreatIntelModuleEnabledMock.mockClear(); mount( @@ -308,7 +312,7 @@ describe('Overview', () => { ); - expect(useAllTiDataSourcesMock).toHaveBeenCalledTimes(1); + expect(useIsThreatIntelModuleEnabledMock).toHaveBeenCalledTimes(1); }); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx index 1df49fed07358..67ee6c55ac06f 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx @@ -30,8 +30,7 @@ import { ENDPOINT_METADATA_INDEX } from '../../../common/constants'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useDeepEqualSelector } from '../../common/hooks/use_selector'; import { ThreatIntelLinkPanel } from '../components/overview_cti_links'; -import { useAllTiDataSources } from '../containers/overview_cti_links/use_all_ti_data_sources'; -import { useTiIntegrations } from '../containers/overview_cti_links/use_ti_integrations'; +import { useIsThreatIntelModuleEnabled } from '../containers/overview_cti_links/use_is_threat_intel_module_enabled'; import { useUserPrivileges } from '../../common/components/user_privileges'; import { RiskyHostLinks } from '../components/overview_risky_host_links'; import { useAlertsPrivileges } from '../../detections/containers/detection_engine/alerts/use_alerts_privileges'; @@ -76,10 +75,7 @@ const OverviewComponent = () => { endpointPrivileges: { canAccessFleet }, } = useUserPrivileges(); const { hasIndexRead, hasKibanaREAD } = useAlertsPrivileges(); - const { tiDataSources: allTiDataSources, isInitiallyLoaded: allTiDataSourcesLoaded } = - useAllTiDataSources(); - const tiIntegrationStatus = useTiIntegrations(); - const isTiLoaded = tiIntegrationStatus && allTiDataSourcesLoaded; + const isThreatIntelModuleEnabled = useIsThreatIntelModuleEnabled(); const riskyHostsEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); @@ -154,16 +150,13 @@ const OverviewComponent = () => { - {isTiLoaded && ( - - )} + {riskyHostsEnabled && ( diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/index.ts index e43af97e84af0..5857a0417239c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/index.ts @@ -9,9 +9,7 @@ import type { FactoryQueryTypes } from '../../../../../common/search_strategy/se import { CtiQueries } from '../../../../../common/search_strategy/security_solution/cti'; import type { SecuritySolutionFactory } from '../types'; import { eventEnrichment } from './event_enrichment'; -import { dataSource } from './threat_intel_source'; export const ctiFactoryTypes: Record> = { [CtiQueries.eventEnrichment]: eventEnrichment, - [CtiQueries.dataSource]: dataSource, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/index.ts deleted file mode 100644 index 0951503b04cd4..0000000000000 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/index.ts +++ /dev/null @@ -1,33 +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 { SecuritySolutionFactory } from '../../types'; -import { - CtiDataSourceStrategyResponse, - CtiQueries, - CtiDataSourceRequestOptions, -} from '../../../../../../common'; -import { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; -import { inspectStringifyObject } from '../../../../../utils/build_query'; -import { buildTiDataSourceQuery } from './query.threat_intel_source.dsl'; - -export const dataSource: SecuritySolutionFactory = { - buildDsl: (options: CtiDataSourceRequestOptions) => buildTiDataSourceQuery(options), - parse: async ( - options: CtiDataSourceRequestOptions, - response: IEsSearchResponse - ): Promise => { - const inspect = { - dsl: [inspectStringifyObject(buildTiDataSourceQuery(options))], - }; - - return { - ...response, - inspect, - }; - }, -}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts deleted file mode 100644 index 832006930a326..0000000000000 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts +++ /dev/null @@ -1,71 +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 { buildTiDataSourceQuery } from './query.threat_intel_source.dsl'; -import { CtiQueries } from '../../../../../../common'; - -export const mockOptions = { - defaultIndex: ['logs-ti_*', 'filebeat-8*'], - docValueFields: [], - factoryQueryType: CtiQueries.dataSource, - filterQuery: '', - timerange: { - interval: '12h', - from: '2020-09-06T15:23:52.757Z', - to: '2020-09-07T15:23:52.757Z', - }, -}; - -export const expectedDsl = { - body: { - aggs: { - dataset: { - terms: { - field: 'event.dataset', - }, - aggs: { - name: { - terms: { - field: 'threat.feed.name', - }, - }, - dashboard: { - terms: { - field: 'threat.feed.dashboard_id', - }, - }, - }, - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: '2020-09-06T15:23:52.757Z', - lte: '2020-09-07T15:23:52.757Z', - format: 'strict_date_optional_time', - }, - }, - }, - ], - }, - }, - }, - ignore_unavailable: true, - index: ['logs-ti_*', 'filebeat-8*'], - size: 0, - track_total_hits: true, - allow_no_indices: true, -}; - -describe('buildbuildTiDataSourceQueryQuery', () => { - test('build query from options correctly', () => { - expect(buildTiDataSourceQuery(mockOptions)).toEqual(expectedDsl); - }); -}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.ts deleted file mode 100644 index 08463146a683e..0000000000000 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CtiDataSourceRequestOptions } from '../../../../../../common'; - -export const buildTiDataSourceQuery = ({ - timerange, - defaultIndex, -}: CtiDataSourceRequestOptions) => { - const filter = []; - - if (timerange) { - filter.push({ - range: { - '@timestamp': { - gte: timerange.from, - lte: timerange.to, - format: 'strict_date_optional_time', - }, - }, - }); - } - - const dslQuery = { - size: 0, - index: defaultIndex, - allow_no_indices: true, - ignore_unavailable: true, - track_total_hits: true, - body: { - aggs: { - dataset: { - terms: { field: 'event.dataset' }, - aggs: { - name: { - terms: { field: 'threat.feed.name' }, - }, - dashboard: { - terms: { - field: 'threat.feed.dashboard_id', - }, - }, - }, - }, - }, - query: { - bool: { - filter, - }, - }, - }, - }; - - return dslQuery; -}; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 58d04788e98eb..76d3f07facf05 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -23438,6 +23438,7 @@ "xpack.securitySolution.overview.auditBeatProcessTitle": "プロセス", "xpack.securitySolution.overview.auditBeatSocketTitle": "ソケット", "xpack.securitySolution.overview.auditBeatUserTitle": "ユーザー", + "xpack.securitySolution.overview.ctiDashboardDangerPanelButton": "モジュールを有効にする", "xpack.securitySolution.overview.ctiDashboardDangerPanelTitle": "表示する脅威インテリジェンスデータがありません", "xpack.securitySolution.overview.ctiDashboardEnableThreatIntel": "別のソースからデータを表示するには、filebeat脅威インテリジェンスモジュールを有効にする必要があります。", "xpack.securitySolution.overview.ctiDashboardInfoPanelBody": "このガイドに従い、ダッシュボードを有効にして、ビジュアライゼーションにソースを表示できるようにしてください。", @@ -23459,6 +23460,7 @@ "xpack.securitySolution.overview.endpointNotice.message": "脅威防御、検出、深いセキュリティデータの可視化を実現し、ホストを保護します。", "xpack.securitySolution.overview.endpointNotice.title": "Endpoint Security", "xpack.securitySolution.overview.endpointNotice.tryButton": "Endpoint Securityを試す", + "xpack.securitySolution.overview.errorFetchingEvents": "イベントの取得エラー", "xpack.securitySolution.overview.eventsTitle": "イベント数", "xpack.securitySolution.overview.filebeatCiscoTitle": "Cisco", "xpack.securitySolution.overview.filebeatNetflowTitle": "Netflow", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index da71c1796066f..01997e32f243e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -23828,6 +23828,7 @@ "xpack.securitySolution.overview.auditBeatProcessTitle": "进程", "xpack.securitySolution.overview.auditBeatSocketTitle": "套接字", "xpack.securitySolution.overview.auditBeatUserTitle": "用户", + "xpack.securitySolution.overview.ctiDashboardDangerPanelButton": "启用模块", "xpack.securitySolution.overview.ctiDashboardDangerPanelTitle": "没有可显示的威胁情报数据", "xpack.securitySolution.overview.ctiDashboardEnableThreatIntel": "您需要启用 filebeat threatintel 模块,以便查看不同源的数据。", "xpack.securitySolution.overview.ctiDashboardInfoPanelBody": "按照此指南启用您的仪表板,以便可以在可视化中查看您的源。", @@ -23850,6 +23851,7 @@ "xpack.securitySolution.overview.endpointNotice.message": "使用威胁防御、检测和深度安全数据可见性功能保护您的主机。", "xpack.securitySolution.overview.endpointNotice.title": "Endpoint Security", "xpack.securitySolution.overview.endpointNotice.tryButton": "试用 Endpoint Security", + "xpack.securitySolution.overview.errorFetchingEvents": "提取事件时出错", "xpack.securitySolution.overview.eventsTitle": "事件计数", "xpack.securitySolution.overview.filebeatCiscoTitle": "Cisco", "xpack.securitySolution.overview.filebeatNetflowTitle": "NetFlow", diff --git a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json index ec5e2aae6e2e2..a2e0c2d2921dc 100644 --- a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json @@ -31,9 +31,6 @@ } }, "type": "file" - }, - "feed": { - "name": "AbuseCH malware" } }, "abusemalware": { @@ -75,4 +72,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json index bc5f6e3db9169..8840cd4bee0dd 100644 --- a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json @@ -796,14 +796,6 @@ "type": "keyword" } } - }, - "feed":{ - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } } } } From 2c4b1ff37130af73093f304bc83c556150cb2ebe Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 3 Dec 2021 15:59:02 +0000 Subject: [PATCH 45/65] fix(NA): @kbn/utils build on windows native environment (#120317) * fix(NA): @kbn/utils build on windows native environment * chore(NA): remove circular dep from @kbn/utils --- packages/kbn-utils/src/path/index.test.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/kbn-utils/src/path/index.test.ts b/packages/kbn-utils/src/path/index.test.ts index 307d47af9ac50..e4c80a0783b5d 100644 --- a/packages/kbn-utils/src/path/index.test.ts +++ b/packages/kbn-utils/src/path/index.test.ts @@ -7,10 +7,17 @@ */ import { accessSync, constants } from 'fs'; -import { createAbsolutePathSerializer } from '@kbn/dev-utils'; import { getConfigPath, getDataPath, getLogsPath, getConfigDirectory } from './'; - -expect.addSnapshotSerializer(createAbsolutePathSerializer()); +import { REPO_ROOT } from '../repo_root'; + +expect.addSnapshotSerializer( + ((rootPath: string = REPO_ROOT, replacement = '') => { + return { + test: (value: any) => typeof value === 'string' && value.startsWith(rootPath), + serialize: (value: string) => value.replace(rootPath, replacement).replace(/\\/g, '/'), + }; + })() +); describe('Default path finder', () => { it('should expose a path to the config directory', () => { From 7ba6e7f68811aed560cf1211e384dc2d57c0c43c Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 3 Dec 2021 16:11:57 +0000 Subject: [PATCH 46/65] [ML] Fixing job selector time range charts (#120343) --- .../public/application/components/job_selector/job_selector.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx b/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx index f67a9df4a4a85..4b0d8cdc55094 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx @@ -10,6 +10,8 @@ import React, { useState, useEffect, useCallback } from 'react'; import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup, EuiFlyout } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import './_index.scss'; + import { Dictionary } from '../../../../common/types/common'; import { useUrlState } from '../../util/url_state'; // @ts-ignore From 63bbc45ec24a7441b4cac4441a75147e10cee6a8 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Fri, 3 Dec 2021 11:16:52 -0500 Subject: [PATCH 47/65] [Security Solution][Endpoint] Remove checks for `superuser` role and instead look at fleet kibana privileges (#120027) * Change endpoint privileges to use fleet authz instead of checking for superuser * split user privileges react context component from hook in order to better support mocking * remove `isPlatinumPlus` from endpoint privileges and refactor to use `useUserPrivileges()` hook instead * add `endpointAuthz` to the Server API route handler context * moved fleet's `createFleetAuthzMock` to `fleet/common` --- x-pack/plugins/fleet/common/index.ts | 1 + x-pack/plugins/fleet/common/mocks.ts | 27 ++++- x-pack/plugins/fleet/server/mocks/index.ts | 30 +----- .../server/routes/setup/handlers.test.ts | 4 +- .../endpoint/service/authz/authz.test.ts | 75 +++++++++++++ .../common/endpoint/service/authz/authz.ts | 43 ++++++++ .../common/endpoint/service/authz/index.ts | 9 ++ .../common/endpoint/service/authz/mocks.ts | 29 +++++ .../common/endpoint/types/authz.ts | 27 +++++ .../common/endpoint/types/index.ts | 2 + .../security_solution/public/app/app.tsx | 2 +- .../user_privileges/__mocks__/index.ts | 18 ++++ .../user_privileges/endpoint/index.ts | 2 +- .../user_privileges/endpoint/mocks.ts | 24 ++--- .../endpoint/use_endpoint_privileges.test.ts | 100 ++++-------------- .../endpoint/use_endpoint_privileges.ts | 79 ++++++-------- .../user_privileges/endpoint/utils.ts | 9 +- .../components/user_privileges/index.ts | 13 +++ ...{index.tsx => user_privileges_context.tsx} | 22 ++-- .../public/common/mock/test_providers.tsx | 2 +- .../components/user_info/index.test.tsx | 2 +- .../search_exceptions.test.tsx | 42 ++++---- .../search_exceptions/search_exceptions.tsx | 6 +- .../host_isolation_exceptions_list.test.tsx | 24 ++++- .../view/host_isolation_exceptions_list.tsx | 4 +- .../policy_trusted_apps_empty_unassigned.tsx | 6 +- .../policy_trusted_apps_layout.test.tsx | 4 +- .../layout/policy_trusted_apps_layout.tsx | 10 +- .../list/policy_trusted_apps_list.test.tsx | 28 ++--- .../list/policy_trusted_apps_list.tsx | 15 ++- .../public/overview/pages/overview.test.tsx | 8 +- .../server/endpoint/mocks.ts | 7 +- .../endpoint/routes/actions/isolation.test.ts | 46 +++++--- .../endpoint/routes/actions/isolation.ts | 20 ++-- .../routes/__mocks__/request_context.ts | 2 + .../server/request_context_factory.ts | 37 ++++++- .../plugins/security_solution/server/types.ts | 2 + 37 files changed, 490 insertions(+), 291 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts create mode 100644 x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts create mode 100644 x-pack/plugins/security_solution/common/endpoint/service/authz/index.ts create mode 100644 x-pack/plugins/security_solution/common/endpoint/service/authz/mocks.ts create mode 100644 x-pack/plugins/security_solution/common/endpoint/types/authz.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/index.ts rename x-pack/plugins/security_solution/public/common/components/user_privileges/{index.tsx => user_privileges_context.tsx} (81%) diff --git a/x-pack/plugins/fleet/common/index.ts b/x-pack/plugins/fleet/common/index.ts index 611e150323855..46a8e2d01fc96 100644 --- a/x-pack/plugins/fleet/common/index.ts +++ b/x-pack/plugins/fleet/common/index.ts @@ -13,3 +13,4 @@ export * from './services'; export * from './types'; export type { FleetAuthz } from './authz'; export { calculateAuthz } from './authz'; +export { createFleetAuthzMock } from './mocks'; diff --git a/x-pack/plugins/fleet/common/mocks.ts b/x-pack/plugins/fleet/common/mocks.ts index eb81ea2d6a0ac..5b71e9b15860e 100644 --- a/x-pack/plugins/fleet/common/mocks.ts +++ b/x-pack/plugins/fleet/common/mocks.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { NewPackagePolicy, PackagePolicy, DeletePackagePoliciesResponse } from './types'; +import type { DeletePackagePoliciesResponse, NewPackagePolicy, PackagePolicy } from './types'; +import type { FleetAuthz } from './authz'; export const createNewPackagePolicyMock = (): NewPackagePolicy => { return { @@ -56,3 +57,27 @@ export const deletePackagePolicyMock = (): DeletePackagePoliciesResponse => { }, ]; }; + +/** + * Creates mock `authz` object + */ +export const createFleetAuthzMock = (): FleetAuthz => { + return { + fleet: { + all: true, + setup: true, + readEnrollmentTokens: true, + }, + integrations: { + readPackageInfo: true, + readInstalledPackages: true, + installPackages: true, + upgradePackages: true, + removePackages: true, + readPackageSettings: true, + writePackageSettings: true, + readIntegrationPolicies: true, + writeIntegrationPolicies: true, + }, + }; +}; diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 90a0addfae490..90c9181b5007a 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -7,11 +7,11 @@ import { of } from 'rxjs'; import { + coreMock, elasticsearchServiceMock, loggingSystemMock, - savedObjectsServiceMock, - coreMock, savedObjectsClientMock, + savedObjectsServiceMock, } from '../../../../../src/core/server/mocks'; import { dataPluginMock } from '../../../../../src/plugins/data/server/mocks'; import { licensingMock } from '../../../../plugins/licensing/server/mocks'; @@ -21,7 +21,7 @@ import type { PackagePolicyServiceInterface } from '../services/package_policy'; import type { AgentPolicyServiceInterface, PackageService } from '../services'; import type { FleetAppContext } from '../plugin'; import { createMockTelemetryEventsSender } from '../telemetry/__mocks__'; -import type { FleetAuthz } from '../../common'; +import { createFleetAuthzMock } from '../../common'; import { agentServiceMock } from '../services/agents/agent_service.mock'; import type { FleetRequestHandlerContext } from '../types'; @@ -145,27 +145,3 @@ export const createMockPackageService = (): PackageService => { ensureInstalledPackage: jest.fn(), }; }; - -/** - * Creates mock `authz` object - */ -export const createFleetAuthzMock = (): FleetAuthz => { - return { - fleet: { - all: true, - setup: true, - readEnrollmentTokens: true, - }, - integrations: { - readPackageInfo: true, - readInstalledPackages: true, - installPackages: true, - upgradePackages: true, - removePackages: true, - readPackageSettings: true, - writePackageSettings: true, - readIntegrationPolicies: true, - writeIntegrationPolicies: true, - }, - }; -}; diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts index d48d80add2435..035659185955d 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts @@ -9,12 +9,14 @@ import { httpServerMock, savedObjectsClientMock } from 'src/core/server/mocks'; import type { PostFleetSetupResponse } from '../../../common'; import { RegistryError } from '../../errors'; -import { createAppContextStartContractMock, xpackMocks, createFleetAuthzMock } from '../../mocks'; +import { createAppContextStartContractMock, xpackMocks } from '../../mocks'; import { agentServiceMock } from '../../services/agents/agent_service.mock'; import { appContextService } from '../../services/app_context'; import { setupFleet } from '../../services/setup'; import type { FleetRequestHandlerContext } from '../../types'; +import { createFleetAuthzMock } from '../../../common'; + import { fleetSetupHandler } from './handlers'; jest.mock('../../services/setup', () => { diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts new file mode 100644 index 0000000000000..588366036932f --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { calculateEndpointAuthz, getEndpointAuthzInitialState } from './authz'; +import { createFleetAuthzMock, FleetAuthz } from '../../../../../fleet/common'; +import { createLicenseServiceMock } from '../../../license/mocks'; +import type { EndpointAuthz } from '../../types/authz'; + +describe('Endpoint Authz service', () => { + let licenseService: ReturnType; + let fleetAuthz: FleetAuthz; + + beforeEach(() => { + licenseService = createLicenseServiceMock(); + fleetAuthz = createFleetAuthzMock(); + }); + + describe('calculateEndpointAuthz()', () => { + describe('and `fleet.all` access is true', () => { + it.each>([ + ['canAccessFleet'], + ['canAccessEndpointManagement'], + ['canIsolateHost'], + ])('should set `%s` to `true`', (authProperty) => { + expect(calculateEndpointAuthz(licenseService, fleetAuthz)[authProperty]).toBe(true); + }); + + it('should set `canIsolateHost` to false if not proper license', () => { + licenseService.isPlatinumPlus.mockReturnValue(false); + + expect(calculateEndpointAuthz(licenseService, fleetAuthz).canIsolateHost).toBe(false); + }); + + it('should set `canUnIsolateHost` to true even if not proper license', () => { + licenseService.isPlatinumPlus.mockReturnValue(false); + + expect(calculateEndpointAuthz(licenseService, fleetAuthz).canUnIsolateHost).toBe(true); + }); + }); + + describe('and `fleet.all` access is false', () => { + beforeEach(() => (fleetAuthz.fleet.all = false)); + + it.each>([ + ['canAccessFleet'], + ['canAccessEndpointManagement'], + ['canIsolateHost'], + ])('should set `%s` to `false`', (authProperty) => { + expect(calculateEndpointAuthz(licenseService, fleetAuthz)[authProperty]).toBe(false); + }); + + it('should set `canUnIsolateHost` to true even if not proper license', () => { + licenseService.isPlatinumPlus.mockReturnValue(false); + + expect(calculateEndpointAuthz(licenseService, fleetAuthz).canUnIsolateHost).toBe(true); + }); + }); + }); + + describe('getEndpointAuthzInitialState()', () => { + it('returns expected initial state', () => { + expect(getEndpointAuthzInitialState()).toEqual({ + canAccessFleet: false, + canAccessEndpointManagement: false, + canIsolateHost: false, + canUnIsolateHost: true, + canCreateArtifactsByPolicy: false, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts new file mode 100644 index 0000000000000..766843311cfdc --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts @@ -0,0 +1,43 @@ +/* + * 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 { LicenseService } from '../../../license'; +import { FleetAuthz } from '../../../../../fleet/common'; +import { EndpointAuthz } from '../../types/authz'; + +/** + * Used by both the server and the UI to generate the Authorization for access to Endpoint related + * functionality + * + * @param licenseService + * @param fleetAuthz + */ +export const calculateEndpointAuthz = ( + licenseService: LicenseService, + fleetAuthz: FleetAuthz +): EndpointAuthz => { + const isPlatinumPlusLicense = licenseService.isPlatinumPlus(); + const hasAllAccessToFleet = fleetAuthz.fleet.all; + + return { + canAccessFleet: hasAllAccessToFleet, + canAccessEndpointManagement: hasAllAccessToFleet, + canCreateArtifactsByPolicy: isPlatinumPlusLicense, + canIsolateHost: isPlatinumPlusLicense && hasAllAccessToFleet, + canUnIsolateHost: true, + }; +}; + +export const getEndpointAuthzInitialState = (): EndpointAuthz => { + return { + canAccessFleet: false, + canAccessEndpointManagement: false, + canCreateArtifactsByPolicy: false, + canIsolateHost: false, + canUnIsolateHost: true, + }; +}; diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/index.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/index.ts new file mode 100644 index 0000000000000..975d28eb9dcbf --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getEndpointAuthzInitialState, calculateEndpointAuthz } from './authz'; +export { getEndpointAuthzInitialStateMock } from './mocks'; diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/mocks.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/mocks.ts new file mode 100644 index 0000000000000..7f1a6f969272b --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/mocks.ts @@ -0,0 +1,29 @@ +/* + * 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 { EndpointAuthz } from '../../types/authz'; +import { getEndpointAuthzInitialState } from './authz'; + +export const getEndpointAuthzInitialStateMock = ( + overrides: Partial = {} +): EndpointAuthz => { + const authz: EndpointAuthz = { + ...( + Object.entries(getEndpointAuthzInitialState()) as Array<[keyof EndpointAuthz, boolean]> + ).reduce((mockPrivileges, [key, value]) => { + // Invert the initial values (from `false` to `true`) so that everything is authorized + mockPrivileges[key] = !value; + + return mockPrivileges; + }, {} as EndpointAuthz), + // this one is currently treated special in that everyone can un-isolate + canUnIsolateHost: true, + ...overrides, + }; + + return authz; +}; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/authz.ts b/x-pack/plugins/security_solution/common/endpoint/types/authz.ts new file mode 100644 index 0000000000000..da0a372db8aa2 --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/types/authz.ts @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * Set of Endpoint Specific privileges that control application authorization. This interface is + * used both on the client and server for consistency + */ +export interface EndpointAuthz { + /** If user has permissions to access Fleet */ + canAccessFleet: boolean; + /** If user has permissions to access Endpoint management (includes check to ensure they also have access to fleet) */ + canAccessEndpointManagement: boolean; + /** if user has permissions to create Artifacts by Policy */ + canCreateArtifactsByPolicy: boolean; + /** If user has permissions to isolate hosts */ + canIsolateHost: boolean; + /** If user has permissions to un-isolate (release) hosts */ + canUnIsolateHost: boolean; +} + +export interface EndpointPrivileges extends EndpointAuthz { + loading: boolean; +} diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index c869c9c780bd9..1fce6f17bdea6 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -1246,3 +1246,5 @@ interface BaseListResponse { * Returned by the server via GET /api/endpoint/metadata */ export type MetadataListResponse = BaseListResponse; + +export type { EndpointPrivileges } from './authz'; diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 78a340d6bbca0..6d5f81b076560 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -25,7 +25,7 @@ import { State } from '../common/store'; import { StartServices } from '../types'; import { PageRouter } from './routes'; import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; -import { UserPrivilegesProvider } from '../common/components/user_privileges'; +import { UserPrivilegesProvider } from '../common/components/user_privileges/user_privileges_context'; interface StartAppComponent { children: React.ReactNode; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/index.ts new file mode 100644 index 0000000000000..dc77a6b9eea8d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { initialUserPrivilegesState, UserPrivilegesState } from '../user_privileges_context'; +import { getEndpointPrivilegesInitialStateMock } from '../endpoint/mocks'; + +export const useUserPrivileges = jest.fn(() => { + const mockedPrivileges: UserPrivilegesState = { + ...initialUserPrivilegesState(), + endpointPrivileges: getEndpointPrivilegesInitialStateMock(), + }; + + return mockedPrivileges; +}); diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts index adea89ce1a051..83443dc20b9b8 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export * from './use_endpoint_privileges'; +export { useEndpointPrivileges } from './use_endpoint_privileges'; export { getEndpointPrivilegesInitialState } from './utils'; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts index 2851c92816cea..2348fdf017c86 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts @@ -5,24 +5,16 @@ * 2.0. */ -import type { EndpointPrivileges } from './use_endpoint_privileges'; -import { getEndpointPrivilegesInitialState } from './utils'; +import { EndpointPrivileges } from '../../../../../common/endpoint/types'; +import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint/service/authz/mocks'; -export const getEndpointPrivilegesInitialStateMock = ( - overrides: Partial = {} -): EndpointPrivileges => { - // Get the initial state and set all permissions to `true` (enabled) for testing +export const getEndpointPrivilegesInitialStateMock = ({ + loading = false, + ...overrides +}: Partial = {}): EndpointPrivileges => { const endpointPrivilegesMock: EndpointPrivileges = { - ...( - Object.entries(getEndpointPrivilegesInitialState()) as Array< - [keyof EndpointPrivileges, boolean] - > - ).reduce((mockPrivileges, [key, value]) => { - mockPrivileges[key] = !value; - - return mockPrivileges; - }, {} as EndpointPrivileges), - ...overrides, + ...getEndpointAuthzInitialStateMock(overrides), + loading, }; return endpointPrivilegesMock; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts index d4ba29a4ef950..4daef6cca45bd 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts @@ -6,14 +6,14 @@ */ import { act, renderHook, RenderHookResult, RenderResult } from '@testing-library/react-hooks'; -import { useHttp, useCurrentUser } from '../../../lib/kibana'; -import { EndpointPrivileges, useEndpointPrivileges } from './use_endpoint_privileges'; +import { useCurrentUser, useKibana } from '../../../lib/kibana'; +import { useEndpointPrivileges } from './use_endpoint_privileges'; import { securityMock } from '../../../../../../security/public/mocks'; -import { appRoutesService } from '../../../../../../fleet/common'; import { AuthenticatedUser } from '../../../../../../security/common'; import { licenseService } from '../../../hooks/use_license'; -import { fleetGetCheckPermissionsHttpMock } from '../../../../management/pages/mocks'; import { getEndpointPrivilegesInitialStateMock } from './mocks'; +import { EndpointPrivileges } from '../../../../../common/endpoint/types'; +import { getEndpointPrivilegesInitialState } from './utils'; jest.mock('../../../lib/kibana'); jest.mock('../../../hooks/use_license', () => { @@ -32,10 +32,9 @@ const licenseServiceMock = licenseService as jest.Mocked; describe('When using useEndpointPrivileges hook', () => { let authenticatedUser: AuthenticatedUser; - let fleetApiMock: ReturnType; let result: RenderResult; let unmount: ReturnType['unmount']; - let waitForNextUpdate: ReturnType['waitForNextUpdate']; + let releaseFleetAuthz: () => void; let render: () => RenderHookResult; beforeEach(() => { @@ -45,14 +44,19 @@ describe('When using useEndpointPrivileges hook', () => { (useCurrentUser as jest.Mock).mockReturnValue(authenticatedUser); - fleetApiMock = fleetGetCheckPermissionsHttpMock( - useHttp() as Parameters[0] - ); licenseServiceMock.isPlatinumPlus.mockReturnValue(true); + // Add a daly to fleet service that provides authz information + const fleetAuthz = useKibana().services.fleet!.authz; + + // Add a delay to the fleet Authz promise to test out the `loading` property + useKibana().services.fleet!.authz = new Promise((resolve) => { + releaseFleetAuthz = () => resolve(fleetAuthz); + }); + render = () => { const hookRenderResponse = renderHook(() => useEndpointPrivileges()); - ({ result, unmount, waitForNextUpdate } = hookRenderResponse); + ({ result, unmount } = hookRenderResponse); return hookRenderResponse; }; }); @@ -62,88 +66,22 @@ describe('When using useEndpointPrivileges hook', () => { }); it('should return `loading: true` while retrieving privileges', async () => { - // Add a daly to the API response that we can control from the test - let releaseApiResponse: () => void; - fleetApiMock.responseProvider.checkPermissions.mockDelay.mockReturnValue( - new Promise((resolve) => { - releaseApiResponse = () => resolve(); - }) - ); (useCurrentUser as jest.Mock).mockReturnValue(null); const { rerender } = render(); - expect(result.current).toEqual( - getEndpointPrivilegesInitialStateMock({ - canAccessEndpointManagement: false, - canAccessFleet: false, - loading: true, - }) - ); + expect(result.current).toEqual(getEndpointPrivilegesInitialState()); // Make user service available (useCurrentUser as jest.Mock).mockReturnValue(authenticatedUser); rerender(); - expect(result.current).toEqual( - getEndpointPrivilegesInitialStateMock({ - canAccessEndpointManagement: false, - canAccessFleet: false, - loading: true, - }) - ); + expect(result.current).toEqual(getEndpointPrivilegesInitialState()); // Release the API response await act(async () => { - fleetApiMock.waitForApi(); - releaseApiResponse!(); + releaseFleetAuthz(); + await useKibana().services.fleet!.authz; }); - expect(result.current).toEqual(getEndpointPrivilegesInitialStateMock()); - }); - - it('should call Fleet permissions api to determine user privilege to fleet', async () => { - render(); - await waitForNextUpdate(); - await fleetApiMock.waitForApi(); - expect(useHttp().get as jest.Mock).toHaveBeenCalledWith( - appRoutesService.getCheckPermissionsPath() - ); - }); - it('should set privileges to false if user does not have superuser role', async () => { - authenticatedUser.roles = []; - render(); - await waitForNextUpdate(); - await fleetApiMock.waitForApi(); - expect(result.current).toEqual( - getEndpointPrivilegesInitialStateMock({ - canAccessEndpointManagement: false, - }) - ); - }); - - it('should set privileges to false if fleet api check returns failure', async () => { - fleetApiMock.responseProvider.checkPermissions.mockReturnValue({ - error: 'MISSING_SECURITY', - success: false, - }); - - render(); - await waitForNextUpdate(); - await fleetApiMock.waitForApi(); - expect(result.current).toEqual( - getEndpointPrivilegesInitialStateMock({ - canAccessEndpointManagement: false, - canAccessFleet: false, - }) - ); + expect(result.current).toEqual(getEndpointPrivilegesInitialStateMock()); }); - - it.each([['canIsolateHost'], ['canCreateArtifactsByPolicy']])( - 'should set %s to false if license is not PlatinumPlus', - async (privilege) => { - licenseServiceMock.isPlatinumPlus.mockReturnValue(false); - render(); - await waitForNextUpdate(); - expect(result.current).toEqual(expect.objectContaining({ [privilege]: false })); - } - ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts index 448cb215941de..6fa0c51f500da 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts @@ -6,24 +6,14 @@ */ import { useEffect, useMemo, useRef, useState } from 'react'; -import { useCurrentUser, useHttp } from '../../../lib/kibana'; -import { appRoutesService, CheckPermissionsResponse } from '../../../../../../fleet/common'; +import { useCurrentUser, useKibana } from '../../../lib/kibana'; import { useLicense } from '../../../hooks/use_license'; -import { Immutable } from '../../../../../common/endpoint/types'; - -export interface EndpointPrivileges { - loading: boolean; - /** If user has permissions to access Fleet */ - canAccessFleet: boolean; - /** If user has permissions to access Endpoint management (includes check to ensure they also have access to fleet) */ - canAccessEndpointManagement: boolean; - /** if user has permissions to create Artifacts by Policy */ - canCreateArtifactsByPolicy: boolean; - /** If user has permissions to use the Host isolation feature */ - canIsolateHost: boolean; - /** @deprecated do not use. instead, use one of the other privileges defined */ - isPlatinumPlus: boolean; -} +import { EndpointPrivileges, Immutable } from '../../../../../common/endpoint/types'; +import { + calculateEndpointAuthz, + getEndpointAuthzInitialState, +} from '../../../../../common/endpoint/service/authz'; +import { FleetAuthz } from '../../../../../../fleet/common'; /** * Retrieve the endpoint privileges for the current user. @@ -32,23 +22,39 @@ export interface EndpointPrivileges { * to keep API calls to a minimum. */ export const useEndpointPrivileges = (): Immutable => { - const http = useHttp(); const user = useCurrentUser(); + const fleetServices = useKibana().services.fleet; const isMounted = useRef(true); - const isPlatinumPlusLicense = useLicense().isPlatinumPlus(); - const [canAccessFleet, setCanAccessFleet] = useState(false); + const licenseService = useLicense(); const [fleetCheckDone, setFleetCheckDone] = useState(false); + const [fleetAuthz, setFleetAuthz] = useState(null); + + const privileges = useMemo(() => { + const privilegeList: EndpointPrivileges = Object.freeze({ + loading: !fleetCheckDone || !user, + ...(fleetAuthz + ? calculateEndpointAuthz(licenseService, fleetAuthz) + : getEndpointAuthzInitialState()), + }); + + return privilegeList; + }, [fleetCheckDone, user, fleetAuthz, licenseService]); // Check if user can access fleet useEffect(() => { + if (!fleetServices) { + setFleetCheckDone(true); + return; + } + + setFleetCheckDone(false); + (async () => { try { - const fleetPermissionsResponse = await http.get( - appRoutesService.getCheckPermissionsPath() - ); + const fleetAuthzForCurrentUser = await fleetServices.authz; if (isMounted.current) { - setCanAccessFleet(fleetPermissionsResponse.success); + setFleetAuthz(fleetAuthzForCurrentUser); } } finally { if (isMounted.current) { @@ -56,30 +62,7 @@ export const useEndpointPrivileges = (): Immutable => { } } })(); - }, [http]); - - // Check if user has `superuser` role - const isSuperUser = useMemo(() => { - if (user?.roles) { - return user.roles.includes('superuser'); - } - return false; - }, [user?.roles]); - - const privileges = useMemo(() => { - const privilegeList: EndpointPrivileges = Object.freeze({ - loading: !fleetCheckDone || !user, - canAccessFleet, - canAccessEndpointManagement: canAccessFleet && isSuperUser, - canCreateArtifactsByPolicy: isPlatinumPlusLicense, - canIsolateHost: isPlatinumPlusLicense, - // FIXME: Remove usages of the property below - /** @deprecated */ - isPlatinumPlus: isPlatinumPlusLicense, - }); - - return privilegeList; - }, [canAccessFleet, fleetCheckDone, isSuperUser, user, isPlatinumPlusLicense]); + }, [fleetServices]); // Capture if component is unmounted useEffect( diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts index df91314479f18..0c314ba5573c8 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts @@ -5,15 +5,12 @@ * 2.0. */ -import { EndpointPrivileges } from './use_endpoint_privileges'; +import { EndpointPrivileges } from '../../../../../common/endpoint/types'; +import { getEndpointAuthzInitialState } from '../../../../../common/endpoint/service/authz'; export const getEndpointPrivilegesInitialState = (): EndpointPrivileges => { return { loading: true, - canAccessFleet: false, - canAccessEndpointManagement: false, - canIsolateHost: false, - canCreateArtifactsByPolicy: false, - isPlatinumPlus: false, + ...getEndpointAuthzInitialState(), }; }; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/index.ts new file mode 100644 index 0000000000000..3a5d942d3b532 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useContext } from 'react'; +import { DeepReadonly } from 'utility-types'; +import { UserPrivilegesContext, UserPrivilegesState } from './user_privileges_context'; + +export const useUserPrivileges = (): DeepReadonly => + useContext(UserPrivilegesContext); diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx b/x-pack/plugins/security_solution/public/common/components/user_privileges/user_privileges_context.tsx similarity index 81% rename from x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx rename to x-pack/plugins/security_solution/public/common/components/user_privileges/user_privileges_context.tsx index 05ccadeaf67ac..5c681e5dbbaec 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/user_privileges_context.tsx @@ -5,16 +5,14 @@ * 2.0. */ -import React, { createContext, useContext, useEffect, useState } from 'react'; -import { DeepReadonly } from 'utility-types'; - -import { Capabilities } from '../../../../../../../src/core/public'; -import { useFetchDetectionEnginePrivileges } from '../../../detections/components/user_privileges/use_fetch_detection_engine_privileges'; +import React, { createContext, useEffect, useState } from 'react'; +import { Capabilities } from '../../../../../../../src/core/types'; +import { SERVER_APP_ID } from '../../../../common/constants'; import { useFetchListPrivileges } from '../../../detections/components/user_privileges/use_fetch_list_privileges'; -import { EndpointPrivileges, useEndpointPrivileges } from './endpoint'; +import { useFetchDetectionEnginePrivileges } from '../../../detections/components/user_privileges/use_fetch_detection_engine_privileges'; +import { getEndpointPrivilegesInitialState, useEndpointPrivileges } from './endpoint'; +import { EndpointPrivileges } from '../../../../common/endpoint/types'; -import { SERVER_APP_ID } from '../../../../common/constants'; -import { getEndpointPrivilegesInitialState } from './endpoint/utils'; export interface UserPrivilegesState { listPrivileges: ReturnType; detectionEnginePrivileges: ReturnType; @@ -28,8 +26,9 @@ export const initialUserPrivilegesState = (): UserPrivilegesState => ({ endpointPrivileges: getEndpointPrivilegesInitialState(), kibanaSecuritySolutionsPrivileges: { crud: false, read: false }, }); - -const UserPrivilegesContext = createContext(initialUserPrivilegesState()); +export const UserPrivilegesContext = createContext( + initialUserPrivilegesState() +); interface UserPrivilegesProviderProps { kibanaCapabilities: Capabilities; @@ -73,6 +72,3 @@ export const UserPrivilegesProvider = ({ ); }; - -export const useUserPrivileges = (): DeepReadonly => - useContext(UserPrivilegesContext); diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index 528592051ccce..9ad5abc1c7ed2 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -25,8 +25,8 @@ import { import { FieldHook } from '../../shared_imports'; import { SUB_PLUGINS_REDUCER } from './utils'; import { createSecuritySolutionStorageMock, localStorageMock } from './mock_local_storage'; -import { UserPrivilegesProvider } from '../components/user_privileges'; import { CASES_FEATURE_ID } from '../../../common/constants'; +import { UserPrivilegesProvider } from '../components/user_privileges/user_privileges_context'; const state: State = mockGlobalState; diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx index 0447130e1bd14..32911a2c8e4ab 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx @@ -13,7 +13,7 @@ import { Capabilities } from 'src/core/public'; import { useKibana } from '../../../common/lib/kibana'; import * as api from '../../containers/detection_engine/alerts/api'; import { TestProviders } from '../../../common/mock/test_providers'; -import { UserPrivilegesProvider } from '../../../common/components/user_privileges'; +import { UserPrivilegesProvider } from '../../../common/components/user_privileges/user_privileges_context'; jest.mock('../../../common/lib/kibana'); jest.mock('../../containers/detection_engine/alerts/api'); diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx index 3b987a7211411..493b41bc0165c 100644 --- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx @@ -8,18 +8,21 @@ import React from 'react'; import { act, fireEvent } from '@testing-library/react'; import { AppContextTestRender, createAppRootMockRenderer } from '../../../common/mock/endpoint'; -import { - EndpointPrivileges, - useEndpointPrivileges, -} from '../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; +import { useUserPrivileges } from '../../../common/components/user_privileges'; import { SearchExceptions, SearchExceptionsProps } from '.'; import { getEndpointPrivilegesInitialStateMock } from '../../../common/components/user_privileges/endpoint/mocks'; -jest.mock('../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); +import { + initialUserPrivilegesState, + UserPrivilegesState, +} from '../../../common/components/user_privileges/user_privileges_context'; +import { EndpointPrivileges } from '../../../../common/endpoint/types'; + +jest.mock('../../../common/components/user_privileges'); let onSearchMock: jest.Mock; -const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock; +const mockUseUserPrivileges = useUserPrivileges as jest.Mock; describe('Search exceptions', () => { let appTestContext: AppContextTestRender; @@ -28,13 +31,16 @@ describe('Search exceptions', () => { props?: Partial ) => ReturnType; - const loadedUserEndpointPrivilegesState = ( + const loadedUserPrivilegesState = ( endpointOverrides: Partial = {} - ): EndpointPrivileges => - getEndpointPrivilegesInitialStateMock({ - isPlatinumPlus: false, - ...endpointOverrides, - }); + ): UserPrivilegesState => { + return { + ...initialUserPrivilegesState(), + endpointPrivileges: getEndpointPrivilegesInitialStateMock({ + ...endpointOverrides, + }), + }; + }; beforeEach(() => { onSearchMock = jest.fn(); @@ -51,11 +57,11 @@ describe('Search exceptions', () => { return renderResult; }; - mockUseEndpointPrivileges.mockReturnValue(loadedUserEndpointPrivilegesState()); + mockUseUserPrivileges.mockReturnValue(loadedUserPrivilegesState()); }); afterAll(() => { - mockUseEndpointPrivileges.mockReset(); + mockUseUserPrivileges.mockReset(); }); it('should have a default value', () => { @@ -102,8 +108,8 @@ describe('Search exceptions', () => { it('should hide policies selector when no license', () => { const generator = new EndpointDocGenerator('policy-list'); const policy = generator.generatePolicyPackagePolicy(); - mockUseEndpointPrivileges.mockReturnValue( - loadedUserEndpointPrivilegesState({ isPlatinumPlus: false }) + mockUseUserPrivileges.mockReturnValue( + loadedUserPrivilegesState({ canCreateArtifactsByPolicy: false }) ); const element = render({ policyList: [policy], hasPolicyFilter: true }); @@ -113,8 +119,8 @@ describe('Search exceptions', () => { it('should display policies selector when right license', () => { const generator = new EndpointDocGenerator('policy-list'); const policy = generator.generatePolicyPackagePolicy(); - mockUseEndpointPrivileges.mockReturnValue( - loadedUserEndpointPrivilegesState({ isPlatinumPlus: true }) + mockUseUserPrivileges.mockReturnValue( + loadedUserPrivilegesState({ canCreateArtifactsByPolicy: true }) ); const element = render({ policyList: [policy], hasPolicyFilter: true }); diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx index 569916ac20315..5489f7a394c99 100644 --- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx @@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/e import { i18n } from '@kbn/i18n'; import { PolicySelectionItem, PoliciesSelector } from '../policies_selector'; import { ImmutableArray, PolicyData } from '../../../../common/endpoint/types'; -import { useEndpointPrivileges } from '../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; +import { useUserPrivileges } from '../../../common/components/user_privileges'; export interface SearchExceptionsProps { defaultValue?: string; @@ -34,7 +34,7 @@ export const SearchExceptions = memo( defaultExcludedPolicies, hideRefreshButton = false, }) => { - const { isPlatinumPlus } = useEndpointPrivileges(); + const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges; const [query, setQuery] = useState(defaultValue); const [includedPolicies, setIncludedPolicies] = useState(defaultIncludedPolicies || ''); const [excludedPolicies, setExcludedPolicies] = useState(defaultExcludedPolicies || ''); @@ -92,7 +92,7 @@ export const SearchExceptions = memo( data-test-subj="searchField" /> - {isPlatinumPlus && hasPolicyFilter && policyList ? ( + {canCreateArtifactsByPolicy && hasPolicyFilter && policyList ? ( { let history: AppContextTestRender['history']; let mockedContext: AppContextTestRender; - const useEndpointPrivilegesMock = useEndpointPrivileges as jest.Mock; + const useUserPrivilegesMock = _useUserPrivileges as jest.Mock; + + const setEndpointPrivileges = (overrides: Partial = {}) => { + const newPrivileges = _useUserPrivileges(); + + useUserPrivilegesMock.mockReturnValue({ + ...newPrivileges, + endpointPrivileges: { + ...newPrivileges.endpointPrivileges, + ...overrides, + }, + }); + }; + const waitForApiCall = () => { return waitFor(() => expect(getHostIsolationExceptionItemsMock).toHaveBeenCalled()); }; @@ -162,7 +176,7 @@ describe('When on the host isolation exceptions page', () => { describe('has canIsolateHost privileges', () => { beforeEach(async () => { - useEndpointPrivilegesMock.mockReturnValue({ canIsolateHost: true }); + setEndpointPrivileges({ canIsolateHost: true }); getHostIsolationExceptionItemsMock.mockImplementation(getFoundExceptionListItemSchemaMock); }); @@ -185,7 +199,7 @@ describe('When on the host isolation exceptions page', () => { describe('does not have canIsolateHost privileges', () => { beforeEach(() => { - useEndpointPrivilegesMock.mockReturnValue({ canIsolateHost: false }); + setEndpointPrivileges({ canIsolateHost: false }); }); it('should not show the create flyout if the user navigates to the create url', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx index a9da5c6d135a3..816aef5ca2dce 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx @@ -31,11 +31,11 @@ import { EDIT_HOST_ISOLATION_EXCEPTION_LABEL, } from './components/translations'; import { getEndpointListPath } from '../../../common/routing'; -import { useEndpointPrivileges } from '../../../../common/components/user_privileges/endpoint'; import { MANAGEMENT_DEFAULT_PAGE_SIZE, MANAGEMENT_PAGE_SIZE_OPTIONS, } from '../../../common/constants'; +import { useUserPrivileges } from '../../../../common/components/user_privileges'; type HostIsolationExceptionPaginatedContent = PaginatedContentProps< Immutable, @@ -44,7 +44,7 @@ type HostIsolationExceptionPaginatedContent = PaginatedContentProps< export const HostIsolationExceptionsList = () => { const history = useHistory(); - const privileges = useEndpointPrivileges(); + const privileges = useUserPrivileges().endpointPrivileges; const location = useHostIsolationExceptionsSelector(getCurrentLocation); const navigateCallback = useHostIsolationExceptionsNavigateCallback(); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx index 3252c5a27d85d..3a7308fef75f1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx @@ -10,7 +10,7 @@ import { EuiEmptyPrompt, EuiButton, EuiPageTemplate, EuiLink } from '@elastic/eu import { FormattedMessage } from '@kbn/i18n-react'; import { usePolicyDetailsNavigateCallback } from '../../policy_hooks'; import { useGetLinkTo } from './use_policy_trusted_apps_empty_hooks'; -import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; +import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; interface CommonProps { policyId: string; @@ -18,7 +18,7 @@ interface CommonProps { } export const PolicyTrustedAppsEmptyUnassigned = memo(({ policyId, policyName }) => { - const { isPlatinumPlus } = useEndpointPrivileges(); + const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges; const navigateCallback = usePolicyDetailsNavigateCallback(); const { onClickHandler, toRouteUrl } = useGetLinkTo(policyId, policyName); const onClickPrimaryButtonHandler = useCallback( @@ -49,7 +49,7 @@ export const PolicyTrustedAppsEmptyUnassigned = memo(({ policyId, p /> } actions={[ - ...(isPlatinumPlus + ...(canCreateArtifactsByPolicy ? [ { it('should hide assign button on empty state with unassigned policies when downgraded to a gold or below license', async () => { mockUseEndpointPrivileges.mockReturnValue( getEndpointPrivilegesInitialStateMock({ - isPlatinumPlus: false, + canCreateArtifactsByPolicy: false, }) ); const component = render(); @@ -184,7 +184,7 @@ describe('Policy trusted apps layout', () => { it('should hide the `Assign trusted applications` button when there is data and the license is downgraded to gold or below', async () => { mockUseEndpointPrivileges.mockReturnValue( getEndpointPrivilegesInitialStateMock({ - isPlatinumPlus: false, + canCreateArtifactsByPolicy: false, }) ); const component = render(); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx index f39b080e56e30..3cf8e60c5e168 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx @@ -32,10 +32,10 @@ import { import { usePolicyDetailsNavigateCallback, usePolicyDetailsSelector } from '../../policy_hooks'; import { PolicyTrustedAppsFlyout } from '../flyout'; import { PolicyTrustedAppsList } from '../list/policy_trusted_apps_list'; -import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { useAppUrl } from '../../../../../../common/lib/kibana'; import { APP_UI_ID } from '../../../../../../../common/constants'; import { getTrustedAppsListPath } from '../../../../../common/routing'; +import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; export const PolicyTrustedAppsLayout = React.memo(() => { const { getAppUrl } = useAppUrl(); @@ -44,7 +44,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => { const isDoesTrustedAppExistsLoading = usePolicyDetailsSelector(doesTrustedAppExistsLoading); const policyItem = usePolicyDetailsSelector(policyDetails); const navigateCallback = usePolicyDetailsNavigateCallback(); - const { isPlatinumPlus } = useEndpointPrivileges(); + const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges; const totalAssignedCount = usePolicyDetailsSelector(getTotalPolicyTrustedAppsListPagination); const hasTrustedApps = usePolicyDetailsSelector(getHasTrustedApps); const isLoadedHasTrustedApps = usePolicyDetailsSelector(getIsLoadedHasTrustedApps); @@ -138,7 +138,9 @@ export const PolicyTrustedAppsLayout = React.memo(() => { - {isPlatinumPlus && assignTrustedAppButton} + + {canCreateArtifactsByPolicy && assignTrustedAppButton} + @@ -169,7 +171,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => { )} - {isPlatinumPlus && showListFlyout ? : null} + {canCreateArtifactsByPolicy && showListFlyout ? : null} ) : null; }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx index 7410dd20d9286..32568ec2b48ee 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx @@ -16,14 +16,12 @@ import { policyDetailsPageAllApiHttpMocks } from '../../../test_utils'; import { isFailedResourceState, isLoadedResourceState } from '../../../../../state'; import { fireEvent, within, act, waitFor } from '@testing-library/react'; import { APP_UI_ID } from '../../../../../../../common/constants'; -import { - EndpointPrivileges, - useEndpointPrivileges, -} from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; +import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks'; +import { EndpointPrivileges } from '../../../../../../../common/endpoint/types'; -jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); -const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock; +jest.mock('../../../../../../common/components/user_privileges'); +const mockUseUserPrivileges = useUserPrivileges as jest.Mock; describe('when rendering the PolicyTrustedAppsList', () => { // The index (zero based) of the card created by the generator that is policy specific @@ -78,11 +76,14 @@ describe('when rendering the PolicyTrustedAppsList', () => { }; afterAll(() => { - mockUseEndpointPrivileges.mockReset(); + mockUseUserPrivileges.mockReset(); }); beforeEach(() => { appTestContext = createAppRootMockRenderer(); - mockUseEndpointPrivileges.mockReturnValue(loadedUserEndpointPrivilegesState()); + mockUseUserPrivileges.mockReturnValue({ + ...mockUseUserPrivileges(), + endpointPrivileges: loadedUserEndpointPrivilegesState(), + }); mockedApis = policyDetailsPageAllApiHttpMocks(appTestContext.coreStart.http); appTestContext.setExperimentalFlag({ trustedAppsByPolicyEnabled: true }); @@ -317,11 +318,12 @@ describe('when rendering the PolicyTrustedAppsList', () => { }); it('does not show remove option in actions menu if license is downgraded to gold or below', async () => { - mockUseEndpointPrivileges.mockReturnValue( - loadedUserEndpointPrivilegesState({ - isPlatinumPlus: false, - }) - ); + mockUseUserPrivileges.mockReturnValue({ + ...mockUseUserPrivileges(), + endpointPrivileges: loadedUserEndpointPrivilegesState({ + canCreateArtifactsByPolicy: false, + }), + }); await render(); await toggleCardActionMenu(POLICY_SPECIFIC_CARD_INDEX); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx index 3453bc529b272..fa4d4e40b3e52 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx @@ -38,7 +38,7 @@ import { ContextMenuItemNavByRouterProps } from '../../../../../components/conte import { ArtifactEntryCollapsibleCardProps } from '../../../../../components/artifact_entry_card'; import { useTestIdGenerator } from '../../../../../components/hooks/use_test_id_generator'; import { RemoveTrustedAppFromPolicyModal } from './remove_trusted_app_from_policy_modal'; -import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; +import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; const DATA_TEST_SUBJ = 'policyTrustedAppsGrid'; @@ -52,7 +52,7 @@ export const PolicyTrustedAppsList = memo( const toasts = useToasts(); const history = useHistory(); const { getAppUrl } = useAppUrl(); - const { isPlatinumPlus } = useEndpointPrivileges(); + const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges; const policyId = usePolicyDetailsSelector(policyIdFromParams); const isLoading = usePolicyDetailsSelector(isPolicyTrustedAppListLoading); const defaultFilter = usePolicyDetailsSelector(getCurrentPolicyArtifactsFilter); @@ -158,7 +158,7 @@ export const PolicyTrustedAppsList = memo( ]; const thisTrustedAppCardProps: ArtifactCardGridCardComponentProps = { expanded: Boolean(isCardExpanded[trustedApp.id]), - actions: isPlatinumPlus + actions: canCreateArtifactsByPolicy ? [ ...fullDetailsAction, { @@ -194,7 +194,14 @@ export const PolicyTrustedAppsList = memo( } return newCardProps; - }, [allPoliciesById, getAppUrl, getTestId, isCardExpanded, trustedAppItems, isPlatinumPlus]); + }, [ + allPoliciesById, + getAppUrl, + getTestId, + isCardExpanded, + trustedAppItems, + canCreateArtifactsByPolicy, + ]); const provideCardProps = useCallback['cardComponentProps']>( (item) => { diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx index 2539490be16fb..33fd1918dad59 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx @@ -17,10 +17,7 @@ import { UseMessagesStorage, } from '../../common/containers/local_storage/use_messages_storage'; import { Overview } from './index'; -import { - initialUserPrivilegesState, - useUserPrivileges, -} from '../../common/components/user_privileges'; +import { useUserPrivileges } from '../../common/components/user_privileges'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useFetchIndex } from '../../common/containers/source'; import { useIsThreatIntelModuleEnabled } from '../containers/overview_cti_links/use_is_threat_intel_module_enabled'; @@ -30,9 +27,10 @@ import { mockCtiLinksResponse, } from '../components/overview_cti_links/mock'; import { useCtiDashboardLinks } from '../containers/overview_cti_links'; -import { EndpointPrivileges } from '../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; import { useHostsRiskScore } from '../containers/overview_risky_host_links/use_hosts_risk_score'; +import { initialUserPrivilegesState } from '../../common/components/user_privileges/user_privileges_context'; +import { EndpointPrivileges } from '../../../common/endpoint/types'; jest.mock('../../common/lib/kibana'); jest.mock('../../common/containers/source'); diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index 9b9d72805425a..dce08e2522beb 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -17,9 +17,8 @@ import { createMockAgentPolicyService, createMockAgentService, createArtifactsClientMock, - createFleetAuthzMock, } from '../../../fleet/server/mocks'; -import { createMockConfig } from '../lib/detection_engine/routes/__mocks__'; +import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__'; import { EndpointAppContextService, EndpointAppContextServiceSetupContract, @@ -40,6 +39,7 @@ import { parseExperimentalConfigValue } from '../../common/experimental_features import { createCasesClientMock } from '../../../cases/server/client/mocks'; import { requestContextFactoryMock } from '../request_context_factory.mock'; import { EndpointMetadataService } from './services/metadata'; +import { createFleetAuthzMock } from '../../../fleet/common'; /** * Creates a mocked EndpointAppContext. @@ -183,8 +183,7 @@ export function createRouteHandlerContext( dataClient: jest.Mocked, savedObjectsClient: jest.Mocked ) { - const context = - xpackMocks.createRequestHandlerContext() as unknown as jest.Mocked; + const context = requestContextMock.create() as jest.Mocked; context.core.elasticsearch.client = dataClient; context.core.savedObjects.client = savedObjectsClient; return context; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts index 29a4e5ce0b299..bd72c5a4044ee 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts @@ -48,6 +48,7 @@ import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data' import { legacyMetadataSearchResponseMock } from '../metadata/support/test_support'; import { AGENT_ACTIONS_INDEX, ElasticsearchAssetType } from '../../../../../fleet/common'; import { CasesClientMock } from '../../../../../cases/server/client/mocks'; +import { EndpointAuthz } from '../../../../common/endpoint/types/authz'; interface CallRouteInterface { body?: HostIsolationRequestBody; @@ -55,6 +56,7 @@ interface CallRouteInterface { searchResponse?: HostMetadata; mockUser?: any; license?: License; + authz?: Partial; } const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } }); @@ -182,7 +184,7 @@ describe('Host Isolation', () => { // it returns the requestContext mock used in the call, to assert internal calls (e.g. the indexed document) callRoute = async ( routePrefix: string, - { body, idxResponse, searchResponse, mockUser, license }: CallRouteInterface, + { body, idxResponse, searchResponse, mockUser, license, authz = {} }: CallRouteInterface, indexExists?: { endpointDsExists: boolean } ): Promise> => { const asUser = mockUser ? mockUser : superUser; @@ -191,6 +193,12 @@ describe('Host Isolation', () => { ); const ctx = createRouteHandlerContext(mockScopedClient, mockSavedObjectClient); + + ctx.securitySolution.endpointAuthz = { + ...ctx.securitySolution.endpointAuthz, + ...authz, + }; + // mock _index_template ctx.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate = jest .fn() @@ -206,6 +214,7 @@ describe('Host Isolation', () => { statusCode: 404, }); }); + const withIdxResp = idxResponse ? idxResponse : { statusCode: 201 }; const mockIndexResponse = jest.fn().mockImplementation(() => Promise.resolve(withIdxResp)); const mockSearchResponse = jest @@ -213,19 +222,25 @@ describe('Host Isolation', () => { .mockImplementation(() => Promise.resolve({ body: legacyMetadataSearchResponseMock(searchResponse) }) ); + if (indexExists) { ctx.core.elasticsearch.client.asInternalUser.index = mockIndexResponse; } + ctx.core.elasticsearch.client.asCurrentUser.index = mockIndexResponse; ctx.core.elasticsearch.client.asCurrentUser.search = mockSearchResponse; + const withLicense = license ? license : Platinum; licenseEmitter.next(withLicense); + const mockRequest = httpServerMock.createKibanaRequest({ body }); const [, routeHandler]: [ RouteConfig, RequestHandler ] = routerMock.post.mock.calls.find(([{ path }]) => path.startsWith(routePrefix))!; + await routeHandler(ctx, mockRequest, mockResponse); + return ctx as unknown as jest.Mocked; }; }); @@ -424,14 +439,17 @@ describe('Host Isolation', () => { }); expect(mockResponse.ok).toBeCalled(); }); - it('prohibits license levels less than platinum from isolating hosts', async () => { - licenseEmitter.next(Gold); + + it('prohibits isolating hosts if no authz for it', async () => { await callRoute(ISOLATE_HOST_ROUTE, { body: { endpoint_ids: ['XYZ'] }, + authz: { canIsolateHost: false }, license: Gold, }); + expect(mockResponse.forbidden).toBeCalled(); }); + it('allows any license level to unisolate', async () => { licenseEmitter.next(Gold); await callRoute(UNISOLATE_HOST_ROUTE, { @@ -442,37 +460,33 @@ describe('Host Isolation', () => { }); }); - describe('User Level', () => { - it('allows superuser to perform isolation', async () => { - const superU = { username: 'foo', roles: ['superuser'] }; + describe('User Authorization Level', () => { + it('allows user to perform isolation when canIsolateHost is true', async () => { await callRoute(ISOLATE_HOST_ROUTE, { body: { endpoint_ids: ['XYZ'] }, - mockUser: superU, }); expect(mockResponse.ok).toBeCalled(); }); - it('allows superuser to perform unisolation', async () => { - const superU = { username: 'foo', roles: ['superuser'] }; + + it('allows user to perform unisolation when canUnIsolateHost is true', async () => { await callRoute(UNISOLATE_HOST_ROUTE, { body: { endpoint_ids: ['XYZ'] }, - mockUser: superU, }); expect(mockResponse.ok).toBeCalled(); }); - it('prohibits non-admin user from performing isolation', async () => { - const superU = { username: 'foo', roles: ['user'] }; + it('prohibits user from performing isolation if canIsolateHost is false', async () => { await callRoute(ISOLATE_HOST_ROUTE, { body: { endpoint_ids: ['XYZ'] }, - mockUser: superU, + authz: { canIsolateHost: false }, }); expect(mockResponse.forbidden).toBeCalled(); }); - it('prohibits non-admin user from performing unisolation', async () => { - const superU = { username: 'foo', roles: ['user'] }; + + it('prohibits user from performing un-isolation if canUnIsolateHost is false', async () => { await callRoute(UNISOLATE_HOST_ROUTE, { body: { endpoint_ids: ['XYZ'] }, - mockUser: superU, + authz: { canUnIsolateHost: false }, }); expect(mockResponse.forbidden).toBeCalled(); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts index 02f0cb4867646..51f88730eb6fd 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts @@ -33,7 +33,6 @@ import { import { getMetadataForEndpoints } from '../../services'; import { EndpointAppContext } from '../../types'; import { APP_ID } from '../../../../common/constants'; -import { userCanIsolate } from '../../../../common/endpoint/actions'; import { doLogsEndpointActionDsExists } from '../../utils'; /** @@ -100,25 +99,20 @@ export const isolationRequestHandler = function ( SecuritySolutionRequestHandlerContext > { return async (context, req, res) => { - // only allow admin users - const user = endpointContext.service.security?.authc.getCurrentUser(req); - if (!userCanIsolate(user?.roles)) { - return res.forbidden({ - body: { - message: 'You do not have permission to perform this action', - }, - }); - } + const { canIsolateHost, canUnIsolateHost } = context.securitySolution.endpointAuthz; - // isolation requires plat+ - if (isolate && !endpointContext.service.getLicenseService()?.isPlatinumPlus()) { + // Ensure user has authorization to use this api + if ((!canIsolateHost && isolate) || (!canUnIsolateHost && !isolate)) { return res.forbidden({ body: { - message: 'Your license level does not allow for this action', + message: + 'You do not have permission to perform this action or license level does not allow for this action', }, }); } + const user = endpointContext.service.security?.authc.getCurrentUser(req); + // fetch the Agent IDs to send the commands to const endpointIDs = [...new Set(req.body.endpoint_ids)]; // dedupe const endpointData = await getMetadataForEndpoints(endpointIDs, context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index 86bba69699195..8abe054daeaf5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -30,6 +30,7 @@ import type { SecuritySolutionApiRequestHandlerContext, SecuritySolutionRequestHandlerContext, } from '../../../../types'; +import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint/service/authz'; const createMockClients = () => { const core = coreMock.createRequestHandlerContext(); @@ -93,6 +94,7 @@ const createSecuritySolutionRequestContextMock = ( return { core, + endpointAuthz: getEndpointAuthzInitialStateMock(), getConfig: jest.fn(() => clients.config), getFrameworkRequest: jest.fn(() => { return { diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index f6c1d6b44eca6..d4adf55004389 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -17,7 +17,18 @@ import { SecuritySolutionPluginCoreSetupDependencies, SecuritySolutionPluginSetupDependencies, } from './plugin_contract'; -import { SecuritySolutionApiRequestHandlerContext } from './types'; +import { + SecuritySolutionApiRequestHandlerContext, + SecuritySolutionRequestHandlerContext, +} from './types'; +import { Immutable } from '../common/endpoint/types'; +import { EndpointAuthz } from '../common/endpoint/types/authz'; +import { + calculateEndpointAuthz, + getEndpointAuthzInitialState, +} from '../common/endpoint/service/authz'; +import { licenseService } from './lib/license'; +import { FleetAuthz } from '../../fleet/common'; export interface IRequestContextFactory { create( @@ -41,7 +52,7 @@ export class RequestContextFactory implements IRequestContextFactory { } public async create( - context: RequestHandlerContext, + context: Omit, request: KibanaRequest ): Promise { const { options, appClientFactory } = this; @@ -55,9 +66,31 @@ export class RequestContextFactory implements IRequestContextFactory { config, }); + let endpointAuthz: Immutable; + let fleetAuthz: FleetAuthz; + + // If Fleet is enabled, then get its Authz + if (startPlugins.fleet) { + fleetAuthz = context.fleet?.authz ?? (await startPlugins.fleet?.authz.fromRequest(request)); + } + return { core: context.core, + get endpointAuthz(): Immutable { + // Lazy getter of endpoint Authz. No point in defining it if it is never used. + if (!endpointAuthz) { + // If no fleet (fleet plugin is optional in the configuration), then just turn off all permissions + if (!startPlugins.fleet) { + endpointAuthz = getEndpointAuthzInitialState(); + } else { + endpointAuthz = calculateEndpointAuthz(licenseService, fleetAuthz); + } + } + + return endpointAuthz; + }, + getConfig: () => config, getFrameworkRequest: () => frameworkRequest, diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index 82616aa36d27e..75686d7834070 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -17,10 +17,12 @@ import { AppClient } from './client'; import { ConfigType } from './config'; import { IRuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/types'; import { FrameworkRequest } from './lib/framework'; +import { EndpointAuthz } from '../common/endpoint/types/authz'; export { AppClient }; export interface SecuritySolutionApiRequestHandlerContext extends RequestHandlerContext { + endpointAuthz: EndpointAuthz; getConfig: () => ConfigType; getFrameworkRequest: () => FrameworkRequest; getAppClient: () => AppClient; From 7fb9dee206e045520a1f271f48f498b010340639 Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Fri, 3 Dec 2021 16:24:16 +0000 Subject: [PATCH 48/65] Rename error rate to failed transactions rate (#120255) * Rename error rate to failed transactions rate * Fix conflict --- ..._error_rate.ts => get_failed_transaction_rate.ts} | 12 ++++++++---- .../service_map/get_service_map_service_node_info.ts | 4 ++-- .../plugins/apm/server/routes/transactions/route.ts | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) rename x-pack/plugins/apm/server/lib/transaction_groups/{get_error_rate.ts => get_failed_transaction_rate.ts} (94%) diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_failed_transaction_rate.ts similarity index 94% rename from x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts rename to x-pack/plugins/apm/server/lib/transaction_groups/get_failed_transaction_rate.ts index e1dde61bfc3ff..b4f2c4b4bee11 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_failed_transaction_rate.ts @@ -32,7 +32,7 @@ import { getFailedTransactionRateTimeSeries, } from '../helpers/transaction_error_rate'; -export async function getErrorRate({ +export async function getFailedTransactionRate({ environment, kuery, serviceName, @@ -122,7 +122,7 @@ export async function getErrorRate({ return { timeseries, average }; } -export async function getErrorRatePeriods({ +export async function getFailedTransactionRatePeriods({ environment, kuery, serviceName, @@ -157,11 +157,15 @@ export async function getErrorRatePeriods({ searchAggregatedTransactions, }; - const currentPeriodPromise = getErrorRate({ ...commonProps, start, end }); + const currentPeriodPromise = getFailedTransactionRate({ + ...commonProps, + start, + end, + }); const previousPeriodPromise = comparisonStart && comparisonEnd - ? getErrorRate({ + ? getFailedTransactionRate({ ...commonProps, start: comparisonStart, end: comparisonEnd, diff --git a/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts index ad2ab74098c22..545fb4dbc4606 100644 --- a/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts +++ b/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts @@ -29,7 +29,7 @@ import { getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../lib/helpers/transactions'; -import { getErrorRate } from '../../lib/transaction_groups/get_error_rate'; +import { getFailedTransactionRate } from '../../lib/transaction_groups/get_failed_transaction_rate'; import { withApmSpan } from '../../utils/with_apm_span'; import { percentCgroupMemoryUsedScript, @@ -123,7 +123,7 @@ async function getFailedTransactionsRateStats({ numBuckets, }: TaskParameters): Promise { return withApmSpan('get_error_rate_for_service_map_node', async () => { - const { average, timeseries } = await getErrorRate({ + const { average, timeseries } = await getFailedTransactionRate({ environment, setup, serviceName, diff --git a/x-pack/plugins/apm/server/routes/transactions/route.ts b/x-pack/plugins/apm/server/routes/transactions/route.ts index fb73fe1555965..b9db2762bce93 100644 --- a/x-pack/plugins/apm/server/routes/transactions/route.ts +++ b/x-pack/plugins/apm/server/routes/transactions/route.ts @@ -19,7 +19,7 @@ import { getServiceTransactionGroupDetailedStatisticsPeriods } from '../services import { getTransactionBreakdown } from './breakdown'; import { getTransactionTraceSamples } from './trace_samples'; import { getLatencyPeriods } from './get_latency_charts'; -import { getErrorRatePeriods } from '../../lib/transaction_groups/get_error_rate'; +import { getFailedTransactionRatePeriods } from '../../lib/transaction_groups/get_failed_transaction_rate'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { createApmServerRouteRepository } from '../apm_routes/create_apm_server_route_repository'; import { @@ -349,7 +349,7 @@ const transactionChartsErrorRateRoute = createApmServerRoute({ end, }); - return getErrorRatePeriods({ + return getFailedTransactionRatePeriods({ environment, kuery, serviceName, From 80660f168676604791870f91fe8eaf691ed75808 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 3 Dec 2021 08:27:41 -0800 Subject: [PATCH 49/65] [Fleet] Renable skipped test for limited packages (#120293) * Renable skipped test for limited packages * Try with newer endpoint package version. Test pass locally... --- .../fleet_api_integration/apis/package_policy/create.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/create.ts b/x-pack/test/fleet_api_integration/apis/package_policy/create.ts index d568e7224fd20..1815ab91b5316 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/create.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/create.ts @@ -199,8 +199,7 @@ export default function (providerContext: FtrProviderContext) { .expect(400); }); - // https://github.com/elastic/kibana/issues/118257 - it.skip('should not allow multiple limited packages on the same agent policy', async function () { + it('should not allow multiple limited packages on the same agent policy', async function () { await supertest .post(`/api/fleet/package_policies`) .set('kbn-xsrf', 'xxxx') @@ -215,7 +214,7 @@ export default function (providerContext: FtrProviderContext) { package: { name: 'endpoint', title: 'Endpoint', - version: '0.13.0', + version: '1.3.0-dev.0', }, }) .expect(200); @@ -233,7 +232,7 @@ export default function (providerContext: FtrProviderContext) { package: { name: 'endpoint', title: 'Endpoint', - version: '0.13.0', + version: '1.3.0-dev.0', }, }) .expect(400); From c7a06cdcbf844cdfecdbf9856c891915e124f3e3 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Fri, 3 Dec 2021 11:58:56 -0500 Subject: [PATCH 50/65] [Fleet] Wire Fleet setup status to core Kibana status API (#120020) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Wire Fleet setup status to core Kibana status API * Remove fake error from testing 🙃 * Apply suggestion for PR review Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com> * Add error message to meta upon Fleet setup failure * Mark fleet as available if setup fails - for now * Fix failing API key tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com> --- x-pack/plugins/fleet/server/plugin.ts | 37 ++++++++++++++++++- .../functional/apps/api_keys/home_page.ts | 3 ++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 4b45cf645201c..1e421fefce835 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -6,6 +6,7 @@ */ import type { Observable } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import type { CoreSetup, CoreStart, @@ -16,13 +17,18 @@ import type { SavedObjectsServiceStart, HttpServiceSetup, KibanaRequest, + ServiceStatus, ElasticsearchClient, } from 'kibana/server'; import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import type { TelemetryPluginSetup, TelemetryPluginStart } from 'src/plugins/telemetry/server'; -import { DEFAULT_APP_CATEGORIES, SavedObjectsClient } from '../../../../src/core/server'; +import { + DEFAULT_APP_CATEGORIES, + SavedObjectsClient, + ServiceStatusLevels, +} from '../../../../src/core/server'; import type { PluginStart as DataPluginStart } from '../../../../src/plugins/data/server'; import type { LicensingPluginSetup, ILicense } from '../../licensing/server'; import type { @@ -182,6 +188,7 @@ export class FleetPlugin private securitySetup?: SecurityPluginSetup; private encryptedSavedObjectsSetup?: EncryptedSavedObjectsPluginSetup; private readonly telemetryEventsSender: TelemetryEventsSender; + private readonly fleetStatus$: BehaviorSubject; private agentService?: AgentService; @@ -193,6 +200,11 @@ export class FleetPlugin this.logger = this.initializerContext.logger.get(); this.configInitialValue = this.initializerContext.config.get(); this.telemetryEventsSender = new TelemetryEventsSender(this.logger.get('telemetry_events')); + + this.fleetStatus$ = new BehaviorSubject({ + level: ServiceStatusLevels.unavailable, + summary: 'Fleet is unavailable', + }); } public setup(core: CoreSetup, deps: FleetSetupDeps) { @@ -203,6 +215,8 @@ export class FleetPlugin this.securitySetup = deps.security; const config = this.configInitialValue; + core.status.set(this.fleetStatus$.asObservable()); + registerSavedObjects(core.savedObjects, deps.encryptedSavedObjects); registerEncryptedSavedObjects(deps.encryptedSavedObjects); @@ -357,13 +371,33 @@ export class FleetPlugin const fleetSetupPromise = (async () => { try { + this.fleetStatus$.next({ + level: ServiceStatusLevels.degraded, + summary: 'Fleet is setting up', + }); + await setupFleet( new SavedObjectsClient(core.savedObjects.createInternalRepository()), core.elasticsearch.client.asInternalUser ); + + this.fleetStatus$.next({ + level: ServiceStatusLevels.available, + summary: 'Fleet is available', + }); } catch (error) { logger.warn('Fleet setup failed'); logger.warn(error); + + this.fleetStatus$.next({ + // As long as Fleet has a dependency on EPR, we can't reliably set Kibana status to `unavailable` here. + // See https://github.com/elastic/kibana/issues/120237 + level: ServiceStatusLevels.available, + summary: 'Fleet setup failed', + meta: { + error: error.message, + }, + }); } })(); @@ -400,6 +434,7 @@ export class FleetPlugin appContextService.stop(); licenseService.stop(); this.telemetryEventsSender.stop(); + this.fleetStatus$.complete(); } private setupAgentService(internalEsClient: ElasticsearchClient): AgentService { diff --git a/x-pack/test/functional/apps/api_keys/home_page.ts b/x-pack/test/functional/apps/api_keys/home_page.ts index 5907247527585..c2dbcc1046f54 100644 --- a/x-pack/test/functional/apps/api_keys/home_page.ts +++ b/x-pack/test/functional/apps/api_keys/home_page.ts @@ -42,6 +42,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await security.testUser.setRoles(['kibana_admin']); await security.testUser.setRoles(['test_api_keys']); await pageObjects.common.navigateToApp('apiKeys'); + + // Delete any API keys created outside of these tests + await pageObjects.apiKeys.bulkDeleteApiKeys(); }); afterEach(async () => { From ffbe19cf4658ad64f274bd913a43465162da70a5 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 3 Dec 2021 19:26:03 +0200 Subject: [PATCH 51/65] [Canvas] Added KibanaThemeProvider to expression_reveal_image. (#120094) * Added kibanaThemeProvider to expression_reveal_image. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../expression_reveal_image/kibana.json | 2 +- .../reveal_image_renderer.stories.tsx | 4 +- .../public/expression_renderers/index.ts | 6 +- .../reveal_image_renderer.tsx | 55 +++++++++++-------- .../expression_reveal_image/public/index.ts | 5 +- .../expression_reveal_image/public/plugin.ts | 4 +- .../canvas_plugin_src/renderers/external.ts | 10 +--- .../shareable_runtime/supported_renderers.js | 4 +- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/plugins/expression_reveal_image/kibana.json b/src/plugins/expression_reveal_image/kibana.json index dad7fdfe2bc5f..5fb13ce31247b 100755 --- a/src/plugins/expression_reveal_image/kibana.json +++ b/src/plugins/expression_reveal_image/kibana.json @@ -11,5 +11,5 @@ "ui": true, "requiredPlugins": ["expressions", "presentationUtil"], "optionalPlugins": [], - "requiredBundles": [] + "requiredBundles": ["kibanaReact"] } diff --git a/src/plugins/expression_reveal_image/public/expression_renderers/__stories__/reveal_image_renderer.stories.tsx b/src/plugins/expression_reveal_image/public/expression_renderers/__stories__/reveal_image_renderer.stories.tsx index 863d8d1000f38..22dd2ef4156df 100644 --- a/src/plugins/expression_reveal_image/public/expression_renderers/__stories__/reveal_image_renderer.stories.tsx +++ b/src/plugins/expression_reveal_image/public/expression_renderers/__stories__/reveal_image_renderer.stories.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; -import { revealImageRenderer } from '../'; +import { getRevealImageRenderer } from '../'; import { getElasticOutline, getElasticLogo } from '../../../../presentation_util/public'; import { Render, waitFor } from '../../../../presentation_util/public/__stories__'; import { Origin } from '../../../common/types/expression_functions'; @@ -26,7 +26,7 @@ const Renderer = ({ origin: Origin.LEFT, percent: 0.45, }; - return ; + return ; }; storiesOf('renderers/revealImage', module).add( diff --git a/src/plugins/expression_reveal_image/public/expression_renderers/index.ts b/src/plugins/expression_reveal_image/public/expression_renderers/index.ts index 433a81884f157..959a630b08b51 100644 --- a/src/plugins/expression_reveal_image/public/expression_renderers/index.ts +++ b/src/plugins/expression_reveal_image/public/expression_renderers/index.ts @@ -6,8 +6,4 @@ * Side Public License, v 1. */ -import { revealImageRenderer } from './reveal_image_renderer'; - -export const renderers = [revealImageRenderer]; - -export { revealImageRenderer }; +export { revealImageRendererFactory, getRevealImageRenderer } from './reveal_image_renderer'; diff --git a/src/plugins/expression_reveal_image/public/expression_renderers/reveal_image_renderer.tsx b/src/plugins/expression_reveal_image/public/expression_renderers/reveal_image_renderer.tsx index d4dec3a8a5825..6bdd014296419 100644 --- a/src/plugins/expression_reveal_image/public/expression_renderers/reveal_image_renderer.tsx +++ b/src/plugins/expression_reveal_image/public/expression_renderers/reveal_image_renderer.tsx @@ -7,10 +7,14 @@ */ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; +import { Observable } from 'rxjs'; +import { CoreTheme } from 'kibana/public'; import { I18nProvider } from '@kbn/i18n-react'; import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { i18n } from '@kbn/i18n'; -import { withSuspense } from '../../../presentation_util/public'; +import { CoreSetup } from '../../../../core/public'; +import { KibanaThemeProvider } from '../../../kibana_react/public'; +import { withSuspense, defaultTheme$ } from '../../../presentation_util/public'; import { RevealImageRendererConfig } from '../../common/types'; export const strings = { @@ -27,25 +31,32 @@ export const strings = { const LazyRevealImageComponent = lazy(() => import('../components/reveal_image_component')); const RevealImageComponent = withSuspense(LazyRevealImageComponent, null); -export const revealImageRenderer = (): ExpressionRenderDefinition => ({ - name: 'revealImage', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render: ( - domNode: HTMLElement, - config: RevealImageRendererConfig, - handlers: IInterpreterRenderHandlers - ) => { - handlers.onDestroy(() => { - unmountComponentAtNode(domNode); - }); +export const getRevealImageRenderer = + (theme$: Observable = defaultTheme$) => + (): ExpressionRenderDefinition => ({ + name: 'revealImage', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render: ( + domNode: HTMLElement, + config: RevealImageRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); - render( - - - , - domNode - ); - }, -}); + render( + + + + + , + domNode + ); + }, + }); + +export const revealImageRendererFactory = (core: CoreSetup) => + getRevealImageRenderer(core.theme.theme$); diff --git a/src/plugins/expression_reveal_image/public/index.ts b/src/plugins/expression_reveal_image/public/index.ts index 66512a1126b06..736e062475e6a 100755 --- a/src/plugins/expression_reveal_image/public/index.ts +++ b/src/plugins/expression_reveal_image/public/index.ts @@ -6,9 +6,6 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/110893 -/* eslint-disable @kbn/eslint/no_export_all */ - import { ExpressionRevealImagePlugin } from './plugin'; export type { ExpressionRevealImagePluginSetup, ExpressionRevealImagePluginStart } from './plugin'; @@ -17,4 +14,4 @@ export function plugin() { return new ExpressionRevealImagePlugin(); } -export * from './expression_renderers'; +export { revealImageRendererFactory, getRevealImageRenderer } from './expression_renderers'; diff --git a/src/plugins/expression_reveal_image/public/plugin.ts b/src/plugins/expression_reveal_image/public/plugin.ts index c5e1b5c8d916f..17bff3f33e8ac 100755 --- a/src/plugins/expression_reveal_image/public/plugin.ts +++ b/src/plugins/expression_reveal_image/public/plugin.ts @@ -8,7 +8,7 @@ import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; -import { revealImageRenderer } from './expression_renderers'; +import { revealImageRendererFactory } from './expression_renderers'; import { revealImageFunction } from '../common/expression_functions'; interface SetupDeps { @@ -33,7 +33,7 @@ export class ExpressionRevealImagePlugin { public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionRevealImagePluginSetup { expressions.registerFunction(revealImageFunction); - expressions.registerRenderer(revealImageRenderer); + expressions.registerRenderer(revealImageRendererFactory(core)); } public start(core: CoreStart): ExpressionRevealImagePluginStart {} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts index 9e2a51065eb6c..569669032cb0b 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -11,23 +11,19 @@ import { errorRendererFactory, debugRendererFactory, } from '../../../../../src/plugins/expression_error/public'; +import { revealImageRendererFactory } from '../../../../../src/plugins/expression_reveal_image/public'; import { repeatImageRendererFactory } from '../../../../../src/plugins/expression_repeat_image/public'; -import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public'; import { shapeRenderer, progressRenderer, } from '../../../../../src/plugins/expression_shape/public'; -export const renderFunctions = [ - imageRenderer, - revealImageRenderer, - shapeRenderer, - progressRenderer, -]; +export const renderFunctions = [imageRenderer, shapeRenderer, progressRenderer]; export const renderFunctionFactories = [ debugRendererFactory, errorRendererFactory, + revealImageRendererFactory, repeatImageRendererFactory, metricRendererFactory, ]; diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index 84150a6a9e82e..01b8cc98ba5ec 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -15,8 +15,8 @@ import { getErrorRenderer, getDebugRenderer, } from '../../../../src/plugins/expression_error/public'; +import { getRevealImageRenderer } from '../../../../src/plugins/expression_reveal_image/public'; import { getRepeatImageRenderer } from '../../../../src/plugins/expression_repeat_image/public'; -import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public'; import { shapeRenderer as shape, progressRenderer as progress, @@ -31,6 +31,7 @@ const renderFunctionsFactories = [ getTableRenderer, getErrorRenderer, getDebugRenderer, + getRevealImageRenderer, getRepeatImageRenderer, getMetricRenderer, ]; @@ -42,7 +43,6 @@ const renderFunctionsFactories = [ */ export const renderFunctions = [ image, - revealImage, pie, plot, progress, From 72765c9de4247161e2672abae4e4bfc0282cb5aa Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 3 Dec 2021 11:33:28 -0700 Subject: [PATCH 52/65] [maps] rename layer types provide more distinguishable names that better reflect layer (#118617) * [maps] rename layer types provide more distinguishable names that better refect layer * fix migration rename * revert extends change for EmsVectorTileLayer * tsling * fix regression and jest test * extend from Abstract instead of RasterTile * better fix * fix tests * more test fixes * update path for newly merged code Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__mocks__/regions_layer.mock.ts | 4 +- .../VisitorBreakdownMap/useLayerList.ts | 4 +- .../choropleth_map.tsx | 2 +- x-pack/plugins/maps/common/constants.ts | 8 +- .../layer_descriptor_types.ts | 2 +- .../migrations/add_field_meta_options.js | 5 +- .../migrations/add_field_meta_options.test.js | 6 +- .../migrations/add_type_to_termjoin.test.ts | 4 +- .../ems_raster_tile_to_ems_vector_tile.js | 8 +- .../common/migrations/join_agg_key.test.ts | 3 +- .../maps/common/migrations/join_agg_key.ts | 3 +- .../migrate_symbol_style_descriptor.js | 5 +- .../migrate_symbol_style_descriptor.test.js | 8 +- .../migrations/rename_layer_types.test.ts | 83 +++++++++++++++++++ .../common/migrations/rename_layer_types.ts | 49 +++++++++++ .../public/actions/data_request_actions.ts | 2 +- .../create_basemap_layer_descriptor.test.ts | 4 +- .../layers/create_basemap_layer_descriptor.ts | 8 +- .../ems_vector_tile_layer.test.ts | 53 ++++++++++++ .../ems_vector_tile_layer.tsx} | 43 ++++++++-- .../raster_tile_layer.test.ts} | 12 ++- .../raster_tile_layer.ts} | 18 ++-- .../geojson_vector_layer.tsx | 2 +- .../mvt_vector_layer/mvt_vector_layer.tsx | 2 +- .../layers/vector_layer/vector_layer.tsx | 2 +- .../vector_tile_layer.test.ts | 75 ----------------- .../create_layer_descriptor.test.ts | 4 +- .../security/create_layer_descriptors.test.ts | 18 ++-- .../ems_base_map_layer_wizard.tsx | 6 +- .../update_source_editor.test.tsx | 2 +- .../update_source_editor.tsx | 8 +- .../es_search_source/util/scaling_form.tsx | 4 +- .../kibana_base_map_layer_wizard.tsx | 4 +- .../sources/wms_source/wms_layer_wizard.tsx | 4 +- .../sources/xyz_tms_source/layer_wizard.tsx | 4 +- .../edit_layer_panel.test.tsx | 2 +- .../connected_components/mb_map/utils.ts | 2 +- x-pack/plugins/maps/public/locators.test.ts | 4 +- .../public/selectors/map_selectors.test.ts | 4 +- .../maps/public/selectors/map_selectors.ts | 18 ++-- .../visualize_geo_field_action.ts | 2 +- .../maps/server/embeddable_migrations.ts | 13 +++ .../maps_telemetry/maps_telemetry.test.js | 2 +- .../server/maps_telemetry/maps_telemetry.ts | 2 +- .../sample_map_saved_objects.json | 8 +- .../maps/server/maps_telemetry/util.ts | 2 +- .../saved_objects/saved_object_migrations.js | 14 ++++ .../application/explorer/anomalies_map.tsx | 2 +- .../api_integration/apis/maps/migrations.js | 4 +- 49 files changed, 359 insertions(+), 189 deletions(-) create mode 100644 x-pack/plugins/maps/common/migrations/rename_layer_types.test.ts create mode 100644 x-pack/plugins/maps/common/migrations/rename_layer_types.ts create mode 100644 x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.test.ts rename x-pack/plugins/maps/public/classes/layers/{vector_tile_layer/vector_tile_layer.tsx => ems_vector_tile_layer/ems_vector_tile_layer.tsx} (93%) rename x-pack/plugins/maps/public/classes/layers/{tile_layer/tile_layer.test.ts => raster_tile_layer/raster_tile_layer.test.ts} (88%) rename x-pack/plugins/maps/public/classes/layers/{tile_layer/tile_layer.ts => raster_tile_layer/raster_tile_layer.ts} (92%) delete mode 100644 x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.test.ts diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__mocks__/regions_layer.mock.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__mocks__/regions_layer.mock.ts index 52bd024d8116b..81b0a71e8d943 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__mocks__/regions_layer.mock.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__mocks__/regions_layer.mock.ts @@ -79,7 +79,7 @@ export const mockLayerList = [ maxZoom: 24, alpha: 0.75, visible: true, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', }, { joins: [ @@ -148,6 +148,6 @@ export const mockLayerList = [ maxZoom: 24, alpha: 0.75, visible: true, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', }, ]; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useLayerList.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useLayerList.ts index 7f81e9d105186..f573a2641b864 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useLayerList.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useLayerList.ts @@ -155,7 +155,7 @@ export function useLayerList() { maxZoom: 24, alpha: 0.75, visible: true, - type: LAYER_TYPE.VECTOR, + type: LAYER_TYPE.GEOJSON_VECTOR, }; ES_TERM_SOURCE_REGION.whereQuery = getWhereQuery(serviceName!); @@ -179,7 +179,7 @@ export function useLayerList() { maxZoom: 24, alpha: 0.75, visible: true, - type: LAYER_TYPE.VECTOR, + type: LAYER_TYPE.GEOJSON_VECTOR, }; return [ diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx index 507bfc36d47f0..bf767c7e65207 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx @@ -87,7 +87,7 @@ export const getChoroplethTopValuesLayer = ( }, isTimeAware: true, }, - type: LAYER_TYPE.VECTOR, + type: LAYER_TYPE.GEOJSON_VECTOR, }; }; diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 0f2ce2c917738..43cca5f0c6a07 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -49,12 +49,12 @@ export function getEditPath(id: string | undefined) { } export enum LAYER_TYPE { - TILE = 'TILE', - VECTOR = 'VECTOR', - VECTOR_TILE = 'VECTOR_TILE', // for static display of mvt vector tiles with a mapbox stylesheet. Does not support any ad-hoc configurations. Used for consuming EMS vector tiles. + RASTER_TILE = 'RASTER_TILE', + GEOJSON_VECTOR = 'GEOJSON_VECTOR', + EMS_VECTOR_TILE = 'EMS_VECTOR_TILE', HEATMAP = 'HEATMAP', BLENDED_VECTOR = 'BLENDED_VECTOR', - TILED_VECTOR = 'TILED_VECTOR', // similar to a regular vector-layer, but it consumes the data as .mvt tilea iso GeoJson. It supports similar ad-hoc configurations like a regular vector layer (E.g. using IVectorStyle), although there is some loss of functionality e.g. does not support term joining + MVT_VECTOR = 'MVT_VECTOR', } export enum SOURCE_TYPES { diff --git a/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts index 4d687969308bb..044678b918fde 100644 --- a/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts @@ -66,7 +66,7 @@ export type LayerDescriptor = { }; export type VectorLayerDescriptor = LayerDescriptor & { - type: LAYER_TYPE.VECTOR | LAYER_TYPE.TILED_VECTOR | LAYER_TYPE.BLENDED_VECTOR; + type: LAYER_TYPE.GEOJSON_VECTOR | LAYER_TYPE.MVT_VECTOR | LAYER_TYPE.BLENDED_VECTOR; joins?: JoinDescriptor[]; style: VectorStyleDescriptor; }; diff --git a/x-pack/plugins/maps/common/migrations/add_field_meta_options.js b/x-pack/plugins/maps/common/migrations/add_field_meta_options.js index 33a98c7dbf33c..736fd30bfbcbb 100644 --- a/x-pack/plugins/maps/common/migrations/add_field_meta_options.js +++ b/x-pack/plugins/maps/common/migrations/add_field_meta_options.js @@ -6,11 +6,12 @@ */ import _ from 'lodash'; -import { LAYER_TYPE, STYLE_TYPE } from '../constants'; +import { STYLE_TYPE } from '../constants'; function isVectorLayer(layerDescriptor) { const layerType = _.get(layerDescriptor, 'type'); - return layerType === LAYER_TYPE.VECTOR; + // can not use LAYER_TYPE because LAYER_TYPE.VECTOR does not exist >8.1 + return layerType === 'VECTOR'; } export function addFieldMetaOptions({ attributes }) { diff --git a/x-pack/plugins/maps/common/migrations/add_field_meta_options.test.js b/x-pack/plugins/maps/common/migrations/add_field_meta_options.test.js index 31300707d147d..60587e56bdbae 100644 --- a/x-pack/plugins/maps/common/migrations/add_field_meta_options.test.js +++ b/x-pack/plugins/maps/common/migrations/add_field_meta_options.test.js @@ -41,7 +41,7 @@ describe('addFieldMetaOptions', () => { test('Should ignore static style properties', () => { const layerListJSON = JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', style: { type: 'VECTOR', properties: { @@ -68,7 +68,7 @@ describe('addFieldMetaOptions', () => { test('Should add field meta options to dynamic style properties', () => { const layerListJSON = JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', style: { type: 'VECTOR', properties: { @@ -94,7 +94,7 @@ describe('addFieldMetaOptions', () => { title: 'my map', layerListJSON: JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', style: { type: 'VECTOR', properties: { diff --git a/x-pack/plugins/maps/common/migrations/add_type_to_termjoin.test.ts b/x-pack/plugins/maps/common/migrations/add_type_to_termjoin.test.ts index d795e63bf81d1..5229380ac55f4 100644 --- a/x-pack/plugins/maps/common/migrations/add_type_to_termjoin.test.ts +++ b/x-pack/plugins/maps/common/migrations/add_type_to_termjoin.test.ts @@ -6,14 +6,14 @@ */ import { addTypeToTermJoin } from './add_type_to_termjoin'; -import { LAYER_TYPE, SOURCE_TYPES } from '../constants'; +import { SOURCE_TYPES } from '../constants'; import { LayerDescriptor } from '../descriptor_types'; describe('addTypeToTermJoin', () => { test('Should handle missing type attribute', () => { const layerListJSON = JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', joins: [ { right: {}, diff --git a/x-pack/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js b/x-pack/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js index 2945b9efed958..4cf5407d79691 100644 --- a/x-pack/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js +++ b/x-pack/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js @@ -6,7 +6,7 @@ */ import _ from 'lodash'; -import { SOURCE_TYPES, LAYER_TYPE } from '../constants'; +import { SOURCE_TYPES } from '../constants'; function isEmsTileSource(layerDescriptor) { const sourceType = _.get(layerDescriptor, 'sourceDescriptor.type'); @@ -15,7 +15,8 @@ function isEmsTileSource(layerDescriptor) { function isTileLayer(layerDescriptor) { const layerType = _.get(layerDescriptor, 'type'); - return layerType === LAYER_TYPE.TILE; + // can not use LAYER_TYPE because LAYER_TYPE.TILE does not exist >8.1 + return layerType === 'TILE'; } export function emsRasterTileToEmsVectorTile({ attributes }) { @@ -33,7 +34,8 @@ export function emsRasterTileToEmsVectorTile({ attributes }) { layerList.forEach((layer) => { if (isTileLayer(layer) && isEmsTileSource(layer)) { // Just need to switch layer type to migrate TILE layer to VECTOR_TILE layer - layer.type = LAYER_TYPE.VECTOR_TILE; + // can not use LAYER_TYPE because LAYER_TYPE.VECTOR_TILE does not exist >8.1 + layer.type = 'VECTOR_TILE'; } }); diff --git a/x-pack/plugins/maps/common/migrations/join_agg_key.test.ts b/x-pack/plugins/maps/common/migrations/join_agg_key.test.ts index 96d60e2f92385..3926c017a9502 100644 --- a/x-pack/plugins/maps/common/migrations/join_agg_key.test.ts +++ b/x-pack/plugins/maps/common/migrations/join_agg_key.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { LAYER_TYPE } from '../constants'; import { migrateJoinAggKey } from './join_agg_key'; describe('migrateJoinAggKey', () => { @@ -65,7 +64,7 @@ describe('migrateJoinAggKey', () => { test('Should migrate vector styles from legacy join agg key to new join agg key', () => { const layerListJSON = JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', joins, style: { properties: { diff --git a/x-pack/plugins/maps/common/migrations/join_agg_key.ts b/x-pack/plugins/maps/common/migrations/join_agg_key.ts index 726855783be63..ae102f2ed540f 100644 --- a/x-pack/plugins/maps/common/migrations/join_agg_key.ts +++ b/x-pack/plugins/maps/common/migrations/join_agg_key.ts @@ -71,7 +71,8 @@ export function migrateJoinAggKey({ layerList.forEach((layerDescriptor: LayerDescriptor) => { if ( - layerDescriptor.type === LAYER_TYPE.VECTOR || + // can not use LAYER_TYPE because LAYER_TYPE.VECTOR does not exist >8.1 + layerDescriptor.type === 'VECTOR' || layerDescriptor.type === LAYER_TYPE.BLENDED_VECTOR ) { const vectorLayerDescriptor = layerDescriptor as VectorLayerDescriptor; diff --git a/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js b/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js index 6dab8595663ed..4aa0b74148438 100644 --- a/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js +++ b/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js @@ -6,11 +6,12 @@ */ import _ from 'lodash'; -import { DEFAULT_ICON, LAYER_TYPE, STYLE_TYPE, SYMBOLIZE_AS_TYPES } from '../constants'; +import { DEFAULT_ICON, STYLE_TYPE, SYMBOLIZE_AS_TYPES } from '../constants'; function isVectorLayer(layerDescriptor) { const layerType = _.get(layerDescriptor, 'type'); - return layerType === LAYER_TYPE.VECTOR; + // can not use LAYER_TYPE because LAYER_TYPE.VECTOR does not exist >8.1 + return layerType === 'VECTOR'; } export function migrateSymbolStyleDescriptor({ attributes }) { diff --git a/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js b/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js index 89f3c05e621ec..9932fe1625c9d 100644 --- a/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js +++ b/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js @@ -41,7 +41,7 @@ describe('migrateSymbolStyleDescriptor', () => { test('Should migrate "symbol" style descriptor', () => { const layerListJSON = JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', style: { properties: { fillColor: { @@ -66,7 +66,7 @@ describe('migrateSymbolStyleDescriptor', () => { title: 'my map', layerListJSON: JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', style: { properties: { fillColor: { @@ -90,7 +90,7 @@ describe('migrateSymbolStyleDescriptor', () => { test('Should migrate style descriptor without "symbol"', () => { const layerListJSON = JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', style: { properties: { fillColor: { @@ -109,7 +109,7 @@ describe('migrateSymbolStyleDescriptor', () => { title: 'my map', layerListJSON: JSON.stringify([ { - type: LAYER_TYPE.VECTOR, + type: 'VECTOR', style: { properties: { fillColor: { diff --git a/x-pack/plugins/maps/common/migrations/rename_layer_types.test.ts b/x-pack/plugins/maps/common/migrations/rename_layer_types.test.ts new file mode 100644 index 0000000000000..2504a2adb31aa --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/rename_layer_types.test.ts @@ -0,0 +1,83 @@ +/* + * 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 { renameLayerTypes } from './rename_layer_types'; + +describe('renameLayerTypes', () => { + test('Should handle missing layerListJSON attribute', () => { + const attributes = { + title: 'my map', + }; + expect(renameLayerTypes({ attributes })).toEqual({ + title: 'my map', + }); + }); + + test('Should rename TILED_VECTOR to MVT_VECTOR', () => { + const layerListJSON = JSON.stringify([ + { + type: 'TILED_VECTOR', + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(renameLayerTypes({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"type":"MVT_VECTOR"}]', + }); + }); + + test('Should rename VECTOR_TILE to EMS_VECTOR_TILE', () => { + const layerListJSON = JSON.stringify([ + { + type: 'VECTOR_TILE', + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(renameLayerTypes({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"type":"EMS_VECTOR_TILE"}]', + }); + }); + + test('Should rename VECTOR to GEOJSON_VECTOR', () => { + const layerListJSON = JSON.stringify([ + { + type: 'VECTOR', + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(renameLayerTypes({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"type":"GEOJSON_VECTOR"}]', + }); + }); + + test('Should rename TILE to RASTER_TILE', () => { + const layerListJSON = JSON.stringify([ + { + type: 'TILE', + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(renameLayerTypes({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"type":"RASTER_TILE"}]', + }); + }); +}); diff --git a/x-pack/plugins/maps/common/migrations/rename_layer_types.ts b/x-pack/plugins/maps/common/migrations/rename_layer_types.ts new file mode 100644 index 0000000000000..6ba924ff32268 --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/rename_layer_types.ts @@ -0,0 +1,49 @@ +/* + * 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 { LAYER_TYPE } from '../constants'; +import { LayerDescriptor } from '../descriptor_types'; +import { MapSavedObjectAttributes } from '../map_saved_object_type'; + +// LAYER_TYPE constants renamed in 8.1 to provide more distinguishable names that better refect layer. +// TILED_VECTOR replaced with MVT_VECTOR +// VECTOR_TILE replaced with EMS_VECTOR_TILE +// VECTOR replaced with GEOJSON_VECTOR +// TILE replaced with RASTER_TILE +export function renameLayerTypes({ + attributes, +}: { + attributes: MapSavedObjectAttributes; +}): MapSavedObjectAttributes { + if (!attributes || !attributes.layerListJSON) { + return attributes; + } + + let layerList: LayerDescriptor[] = []; + try { + layerList = JSON.parse(attributes.layerListJSON); + } catch (e) { + throw new Error('Unable to parse attribute layerListJSON'); + } + + layerList.forEach((layerDescriptor: LayerDescriptor) => { + if (layerDescriptor.type === 'TILED_VECTOR') { + layerDescriptor.type = LAYER_TYPE.MVT_VECTOR; + } else if (layerDescriptor.type === 'VECTOR_TILE') { + layerDescriptor.type = LAYER_TYPE.EMS_VECTOR_TILE; + } else if (layerDescriptor.type === 'VECTOR') { + layerDescriptor.type = LAYER_TYPE.GEOJSON_VECTOR; + } else if (layerDescriptor.type === 'TILE') { + layerDescriptor.type = LAYER_TYPE.RASTER_TILE; + } + }); + + return { + ...attributes, + layerListJSON: JSON.stringify(layerList), + }; +} diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts index c1a6d05cc0577..730135424a4dd 100644 --- a/x-pack/plugins/maps/public/actions/data_request_actions.ts +++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts @@ -287,7 +287,7 @@ function endDataLoad( const eventHandlers = getEventHandlers(getState()); if (eventHandlers && eventHandlers.onDataLoadEnd) { const resultMeta: ResultMeta = {}; - if (layer && layer.getType() === LAYER_TYPE.VECTOR) { + if (layer && layer.getType() === LAYER_TYPE.GEOJSON_VECTOR) { const featuresWithoutCentroids = features.filter((feature) => { return feature.properties ? !feature.properties[KBN_IS_CENTROID_FEATURE] : true; }); diff --git a/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts b/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts index cf3d870538004..eded70a75e4ac 100644 --- a/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts @@ -53,7 +53,7 @@ describe('kibana.yml configured with map.tilemap.url', () => { type: 'KIBANA_TILEMAP', }, style: { type: 'TILE' }, - type: 'TILE', + type: 'RASTER_TILE', visible: true, }); }); @@ -89,7 +89,7 @@ describe('EMS is enabled', () => { type: 'EMS_TMS', }, style: { type: 'TILE' }, - type: 'VECTOR_TILE', + type: 'EMS_VECTOR_TILE', visible: true, }); }); diff --git a/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.ts index d285d3a36f66a..e104261f90847 100644 --- a/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.ts @@ -11,14 +11,14 @@ import { getKibanaTileMap } from '../../util'; import { getEMSSettings } from '../../kibana_services'; // @ts-expect-error import { KibanaTilemapSource } from '../sources/kibana_tilemap_source'; -import { TileLayer } from './tile_layer/tile_layer'; -import { VectorTileLayer } from './vector_tile_layer/vector_tile_layer'; +import { RasterTileLayer } from './raster_tile_layer/raster_tile_layer'; +import { EmsVectorTileLayer } from './ems_vector_tile_layer/ems_vector_tile_layer'; import { EMSTMSSource } from '../sources/ems_tms_source'; export function createBasemapLayerDescriptor(): LayerDescriptor | null { const tilemapSourceFromKibana = getKibanaTileMap(); if (_.get(tilemapSourceFromKibana, 'url')) { - const layerDescriptor = TileLayer.createDescriptor({ + const layerDescriptor = RasterTileLayer.createDescriptor({ sourceDescriptor: KibanaTilemapSource.createDescriptor(), }); return layerDescriptor; @@ -26,7 +26,7 @@ export function createBasemapLayerDescriptor(): LayerDescriptor | null { const isEmsEnabled = getEMSSettings()!.isEMSEnabled(); if (isEmsEnabled) { - const layerDescriptor = VectorTileLayer.createDescriptor({ + const layerDescriptor = EmsVectorTileLayer.createDescriptor({ sourceDescriptor: EMSTMSSource.createDescriptor({ isAutoSelect: true }), }); return layerDescriptor; diff --git a/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.test.ts b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.test.ts new file mode 100644 index 0000000000000..8b27bacff8ecb --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.test.ts @@ -0,0 +1,53 @@ +/* + * 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 { SOURCE_TYPES } from '../../../../common/constants'; +import { DataFilters, XYZTMSSourceDescriptor } from '../../../../common/descriptor_types'; +import { ILayer } from '../layer'; +import { EmsVectorTileLayer } from './ems_vector_tile_layer'; +import { DataRequestContext } from '../../../actions'; +import { EMSTMSSource } from '../../sources/ems_tms_source'; + +describe('EmsVectorTileLayer', () => { + it('should correctly inject tileLayerId in meta', async () => { + const layer: ILayer = new EmsVectorTileLayer({ + source: { + getTileLayerId: () => { + return 'myTileLayerId'; + }, + getVectorStyleSheetAndSpriteMeta: () => { + throw new Error('network error'); + }, + } as unknown as EMSTMSSource, + layerDescriptor: { + id: 'layerid', + sourceDescriptor: { + type: SOURCE_TYPES.EMS_XYZ, + urlTemplate: 'https://example.com/{x}/{y}/{z}.png', + id: 'mockSourceId', + } as XYZTMSSourceDescriptor, + }, + }); + + let actualMeta; + let actualErrorMessage; + const mockContext = { + startLoading: (requestId: string, token: string, meta: unknown) => { + actualMeta = meta; + }, + onLoadError: (requestId: string, token: string, message: string) => { + actualErrorMessage = message; + }, + dataFilters: { foo: 'bar' } as unknown as DataFilters, + } as unknown as DataRequestContext; + + await layer.syncData(mockContext); + + expect(actualMeta).toStrictEqual({ tileLayerId: 'myTileLayerId' }); + expect(actualErrorMessage).toStrictEqual('network error'); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.tsx b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx similarity index 93% rename from x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.tsx rename to x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx index f6c4e3fd057cf..8f7471f255a5d 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx @@ -7,7 +7,7 @@ import type { Map as MbMap, Layer as MbLayer, Style as MbStyle } from '@kbn/mapbox-gl'; import _ from 'lodash'; -import { TileLayer } from '../tile_layer/tile_layer'; +import { AbstractLayer } from '../layer'; import { SOURCE_DATA_REQUEST_ID, LAYER_TYPE, LAYER_STYLE_TYPE } from '../../../../common/constants'; import { LayerDescriptor } from '../../../../common/descriptor_types'; import { DataRequest } from '../../util/data_request'; @@ -18,6 +18,7 @@ import { } from '../../../connected_components/mb_map/utils'; import { DataRequestContext } from '../../../actions'; import { EMSTMSSource } from '../../sources/ems_tms_source'; +import { TileStyle } from '../../styles/tile/tile_style'; interface SourceRequestMeta { tileLayerId: string; @@ -46,22 +47,44 @@ interface SourceRequestData { }; } -// TODO - rename to EmsVectorTileLayer -export class VectorTileLayer extends TileLayer { - static type = LAYER_TYPE.VECTOR_TILE; - +export class EmsVectorTileLayer extends AbstractLayer { static createDescriptor(options: Partial) { const tileLayerDescriptor = super.createDescriptor(options); - tileLayerDescriptor.type = VectorTileLayer.type; + tileLayerDescriptor.type = LAYER_TYPE.EMS_VECTOR_TILE; tileLayerDescriptor.alpha = _.get(options, 'alpha', 1); tileLayerDescriptor.style = { type: LAYER_STYLE_TYPE.TILE }; return tileLayerDescriptor; } + private readonly _style: TileStyle; + + constructor({ + source, + layerDescriptor, + }: { + source: EMSTMSSource; + layerDescriptor: LayerDescriptor; + }) { + super({ source, layerDescriptor }); + this._style = new TileStyle(); + } + getSource(): EMSTMSSource { return super.getSource() as EMSTMSSource; } + getStyleForEditing() { + return this._style; + } + + getStyle() { + return this._style; + } + + getCurrentStyle() { + return this._style; + } + _canSkipSync({ prevDataRequest, nextMeta, @@ -349,4 +372,12 @@ export class VectorTileLayer extends TileLayer { async getLicensedFeatures() { return this._source.getLicensedFeatures(); } + + getLayerTypeIconName() { + return 'grid'; + } + + isBasemap(order: number) { + return order === 0; + } } diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.test.ts b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.test.ts similarity index 88% rename from x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.test.ts rename to x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.test.ts index 43cc69687ffb7..66c5b8da0591c 100644 --- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ITileLayerArguments, TileLayer } from './tile_layer'; +import { RasterTileLayer } from './raster_tile_layer'; import { SOURCE_TYPES } from '../../../../common/constants'; import { XYZTMSSourceDescriptor } from '../../../../common/descriptor_types'; import { AbstractSource } from '../../sources/source'; @@ -34,22 +34,20 @@ class MockTileSource extends AbstractSource implements ITMSSource { } } -describe('TileLayer', () => { +describe('RasterTileLayer', () => { it('should use display-label from source', async () => { const source = new MockTileSource(sourceDescriptor); - const args: ITileLayerArguments = { + const layer: ILayer = new RasterTileLayer({ source, layerDescriptor: { id: 'layerid', sourceDescriptor }, - }; - - const layer: ILayer = new TileLayer(args); + }); expect(await source.getDisplayName()).toEqual(await layer.getDisplayName()); }); it('should override with custom display-label if present', async () => { const source = new MockTileSource(sourceDescriptor); - const layer: ILayer = new TileLayer({ + const layer: ILayer = new RasterTileLayer({ source, layerDescriptor: { id: 'layerid', sourceDescriptor, label: 'custom' }, }); diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.ts b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts similarity index 92% rename from x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.ts rename to x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts index 009b447402f9e..40a7276bc10d0 100644 --- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts @@ -14,16 +14,10 @@ import { TileStyle } from '../../styles/tile/tile_style'; import { ITMSSource } from '../../sources/tms_source'; import { DataRequestContext } from '../../../actions'; -export interface ITileLayerArguments { - source: ITMSSource; - layerDescriptor: LayerDescriptor; -} - -// TODO - rename to RasterTileLayer -export class TileLayer extends AbstractLayer { +export class RasterTileLayer extends AbstractLayer { static createDescriptor(options: Partial) { const tileLayerDescriptor = super.createDescriptor(options); - tileLayerDescriptor.type = LAYER_TYPE.TILE; + tileLayerDescriptor.type = LAYER_TYPE.RASTER_TILE; tileLayerDescriptor.alpha = _.get(options, 'alpha', 1); tileLayerDescriptor.style = { type: LAYER_STYLE_TYPE.TILE }; return tileLayerDescriptor; @@ -31,7 +25,13 @@ export class TileLayer extends AbstractLayer { private readonly _style: TileStyle; - constructor({ source, layerDescriptor }: ITileLayerArguments) { + constructor({ + source, + layerDescriptor, + }: { + source: ITMSSource; + layerDescriptor: LayerDescriptor; + }) { super({ source, layerDescriptor }); this._style = new TileStyle(); } 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 3152ac27189b3..e7fa87435cf09 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 @@ -51,7 +51,7 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer { mapColors?: string[] ): VectorLayerDescriptor { const layerDescriptor = super.createDescriptor(options) as VectorLayerDescriptor; - layerDescriptor.type = LAYER_TYPE.VECTOR; + layerDescriptor.type = LAYER_TYPE.GEOJSON_VECTOR; if (!options.style) { const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors ? mapColors : []); 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 e266c729f26fa..5ac95c9a91f64 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 @@ -42,7 +42,7 @@ export class MvtVectorLayer extends AbstractVectorLayer { mapColors?: string[] ): VectorLayerDescriptor { const layerDescriptor = super.createDescriptor(descriptor, mapColors); - layerDescriptor.type = LAYER_TYPE.TILED_VECTOR; + layerDescriptor.type = LAYER_TYPE.MVT_VECTOR; if (!layerDescriptor.style) { const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors ? mapColors : []); 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 71a960fc1919b..0384517bf9834 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 @@ -113,7 +113,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { mapColors?: string[] ): VectorLayerDescriptor { const layerDescriptor = super.createDescriptor(options) as VectorLayerDescriptor; - layerDescriptor.type = LAYER_TYPE.VECTOR; + layerDescriptor.type = LAYER_TYPE.GEOJSON_VECTOR; if (!options.style) { const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors ? mapColors : []); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.test.ts b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.test.ts deleted file mode 100644 index d094e53c59a92..0000000000000 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ITileLayerArguments } from '../tile_layer/tile_layer'; -import { SOURCE_TYPES } from '../../../../common/constants'; -import { DataFilters, XYZTMSSourceDescriptor } from '../../../../common/descriptor_types'; -import { AbstractSource } from '../../sources/source'; -import { ITMSSource } from '../../sources/tms_source'; -import { ILayer } from '../layer'; -import { VectorTileLayer } from './vector_tile_layer'; -import { DataRequestContext } from '../../../actions'; - -const sourceDescriptor: XYZTMSSourceDescriptor = { - type: SOURCE_TYPES.EMS_XYZ, - urlTemplate: 'https://example.com/{x}/{y}/{z}.png', - id: 'mockSourceId', -}; - -class MockTileSource extends AbstractSource implements ITMSSource { - readonly _descriptor: XYZTMSSourceDescriptor; - constructor(descriptor: XYZTMSSourceDescriptor) { - super(descriptor, {}); - this._descriptor = descriptor; - } - - async getDisplayName(): Promise { - return this._descriptor.urlTemplate; - } - - async getUrlTemplate(): Promise { - return 'template/{x}/{y}/{z}.png'; - } - - getTileLayerId() { - return this._descriptor.id; - } - - getVectorStyleSheetAndSpriteMeta() { - throw new Error('network error'); - } -} - -describe('VectorTileLayer', () => { - it('should correctly inject tileLayerId in meta', async () => { - const source = new MockTileSource(sourceDescriptor); - - const args: ITileLayerArguments = { - source, - layerDescriptor: { id: 'layerid', sourceDescriptor }, - }; - - const layer: ILayer = new VectorTileLayer(args); - - let actualMeta; - let actualErrorMessage; - const mockContext = { - startLoading: (requestId: string, token: string, meta: unknown) => { - actualMeta = meta; - }, - onLoadError: (requestId: string, token: string, message: string) => { - actualErrorMessage = message; - }, - dataFilters: { foo: 'bar' } as unknown as DataFilters, - } as unknown as DataRequestContext; - - await layer.syncData(mockContext); - - expect(actualMeta).toStrictEqual({ tileLayerId: 'mockSourceId' }); - expect(actualErrorMessage).toStrictEqual('network error'); - }); -}); diff --git a/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/observability/create_layer_descriptor.test.ts b/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/observability/create_layer_descriptor.test.ts index 7c762ac3409f8..9e843c98cb649 100644 --- a/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/observability/create_layer_descriptor.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/observability/create_layer_descriptor.test.ts @@ -165,7 +165,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }); }); @@ -345,7 +345,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }); }); diff --git a/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/security/create_layer_descriptors.test.ts b/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/security/create_layer_descriptors.test.ts index b5d4edc8cb43b..b7e07e6f8e274 100644 --- a/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/security/create_layer_descriptors.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/security/create_layer_descriptors.test.ts @@ -138,7 +138,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, { @@ -248,7 +248,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, { @@ -365,7 +365,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, ]); @@ -480,7 +480,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, { @@ -590,7 +590,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, { @@ -707,7 +707,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, ]); @@ -822,7 +822,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, { @@ -932,7 +932,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, { @@ -1049,7 +1049,7 @@ describe('createLayerDescriptor', () => { }, type: 'VECTOR', }, - type: 'VECTOR', + type: 'GEOJSON_VECTOR', visible: true, }, ]); diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_base_map_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_base_map_layer_wizard.tsx index 9138b199fd578..26afa65b9527c 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_base_map_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_base_map_layer_wizard.tsx @@ -8,10 +8,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { LayerWizard, RenderWizardArguments } from '../../layers'; -// @ts-ignore import { EMSTMSSource, getSourceTitle } from './ems_tms_source'; -// @ts-ignore -import { VectorTileLayer } from '../../layers/vector_tile_layer/vector_tile_layer'; +import { EmsVectorTileLayer } from '../../layers/ems_vector_tile_layer/ems_vector_tile_layer'; import { EmsTmsSourceConfig } from './tile_service_select'; import { CreateSourceEditor } from './create_source_editor'; import { getEMSSettings } from '../../../kibana_services'; @@ -45,7 +43,7 @@ export const emsBaseMapLayerWizardConfig: LayerWizard = { icon: WorldMapLayerIcon, renderWizard: ({ previewLayers }: RenderWizardArguments) => { const onSourceConfigChange = (sourceConfig: EmsTmsSourceConfig) => { - const layerDescriptor = VectorTileLayer.createDescriptor({ + const layerDescriptor = EmsVectorTileLayer.createDescriptor({ sourceDescriptor: EMSTMSSource.createDescriptor(sourceConfig), }); previewLayers([layerDescriptor]); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.test.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.test.tsx index 32658447acf2b..3ddb804cac213 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.test.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.test.tsx @@ -18,7 +18,7 @@ jest.mock('uuid/v4', () => { }); const defaultProps = { - currentLayerType: LAYER_TYPE.VECTOR, + currentLayerType: LAYER_TYPE.GEOJSON_VECTOR, indexPatternId: 'foobar', onChange: async () => {}, metrics: [], diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx index ba10479a2bd2c..fb748cdc63aff 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx @@ -82,11 +82,13 @@ export class UpdateSourceEditor extends Component { _onResolutionChange = async (resolution: GRID_RESOLUTION, metrics: AggDescriptor[]) => { let newLayerType; if ( - this.props.currentLayerType === LAYER_TYPE.VECTOR || - this.props.currentLayerType === LAYER_TYPE.TILED_VECTOR + this.props.currentLayerType === LAYER_TYPE.GEOJSON_VECTOR || + this.props.currentLayerType === LAYER_TYPE.MVT_VECTOR ) { newLayerType = - resolution === GRID_RESOLUTION.SUPER_FINE ? LAYER_TYPE.TILED_VECTOR : LAYER_TYPE.VECTOR; + resolution === GRID_RESOLUTION.SUPER_FINE + ? LAYER_TYPE.MVT_VECTOR + : LAYER_TYPE.GEOJSON_VECTOR; } await this.props.onChange( diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/util/scaling_form.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/util/scaling_form.tsx index ddf727842780d..fcf915618a0b7 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/util/scaling_form.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/util/scaling_form.tsx @@ -86,9 +86,9 @@ export class ScalingForm extends Component { if (optionId === SCALING_TYPES.CLUSTERS) { layerType = LAYER_TYPE.BLENDED_VECTOR; } else if (optionId === SCALING_TYPES.MVT) { - layerType = LAYER_TYPE.TILED_VECTOR; + layerType = LAYER_TYPE.MVT_VECTOR; } else { - layerType = LAYER_TYPE.VECTOR; + layerType = LAYER_TYPE.GEOJSON_VECTOR; } this.props.onChange({ propName: 'scalingType', value: optionId, newLayerType: layerType }); diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx index 70f56f8f4e6d2..ec69989a8313d 100644 --- a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx @@ -12,7 +12,7 @@ import { LayerWizard, RenderWizardArguments } from '../../layers'; import { CreateSourceEditor } from './create_source_editor'; // @ts-ignore import { KibanaTilemapSource, sourceTitle } from './kibana_tilemap_source'; -import { TileLayer } from '../../layers/tile_layer/tile_layer'; +import { RasterTileLayer } from '../../layers/raster_tile_layer/raster_tile_layer'; import { getKibanaTileMap } from '../../../util'; import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants'; @@ -29,7 +29,7 @@ export const kibanaBasemapLayerWizardConfig: LayerWizard = { icon: 'logoKibana', renderWizard: ({ previewLayers }: RenderWizardArguments) => { const onSourceConfigChange = () => { - const layerDescriptor = TileLayer.createDescriptor({ + const layerDescriptor = RasterTileLayer.createDescriptor({ sourceDescriptor: KibanaTilemapSource.createDescriptor(), }); previewLayers([layerDescriptor]); diff --git a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_layer_wizard.tsx index adbb23b921d4b..19f31d481f58e 100644 --- a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_layer_wizard.tsx @@ -12,7 +12,7 @@ import { WMSCreateSourceEditor } from './wms_create_source_editor'; // @ts-ignore import { sourceTitle, WMSSource } from './wms_source'; import { LayerWizard, RenderWizardArguments } from '../../layers'; -import { TileLayer } from '../../layers/tile_layer/tile_layer'; +import { RasterTileLayer } from '../../layers/raster_tile_layer/raster_tile_layer'; import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants'; import { WebMapServiceLayerIcon } from '../../layers/wizards/icons/web_map_service_layer_icon'; @@ -29,7 +29,7 @@ export const wmsLayerWizardConfig: LayerWizard = { return; } - const layerDescriptor = TileLayer.createDescriptor({ + const layerDescriptor = RasterTileLayer.createDescriptor({ sourceDescriptor: WMSSource.createDescriptor(sourceConfig), }); previewLayers([layerDescriptor]); diff --git a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/layer_wizard.tsx index 95a2b104c7a1b..82aab592a1344 100644 --- a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/layer_wizard.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { XYZTMSEditor, XYZTMSSourceConfig } from './xyz_tms_editor'; import { XYZTMSSource, sourceTitle } from './xyz_tms_source'; import { LayerWizard, RenderWizardArguments } from '../../layers'; -import { TileLayer } from '../../layers/tile_layer/tile_layer'; +import { RasterTileLayer } from '../../layers/raster_tile_layer/raster_tile_layer'; import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants'; import { WorldMapLayerIcon } from '../../layers/wizards/icons/world_map_layer_icon'; @@ -32,7 +32,7 @@ export const tmsLayerWizardConfig: LayerWizard = { return; } - const layerDescriptor = TileLayer.createDescriptor({ + const layerDescriptor = RasterTileLayer.createDescriptor({ sourceDescriptor: XYZTMSSource.createDescriptor(sourceConfig), }); previewLayers([layerDescriptor]); diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/edit_layer_panel.test.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/edit_layer_panel.test.tsx index 82795b8bd9317..ebe0cc6a4178d 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/edit_layer_panel.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/edit_layer_panel.test.tsx @@ -57,7 +57,7 @@ const mockLayer = { return '1'; }, getType: () => { - return LAYER_TYPE.VECTOR; + return LAYER_TYPE.GEOJSON_VECTOR; }, getDisplayName: () => { return 'layer 1'; 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 17994bf799487..f5de99d04c01c 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 @@ -11,7 +11,7 @@ import { TileMetaFeature } from '../../../common/descriptor_types'; import { RGBAImage } from './image_utils'; import { isGlDrawLayer } from './sort_layers'; import { ILayer } from '../../classes/layers/layer'; -import { EmsSpriteSheet } from '../../classes/layers/vector_tile_layer/vector_tile_layer'; +import { EmsSpriteSheet } from '../../classes/layers/ems_vector_tile_layer/ems_vector_tile_layer'; import { ES_MVT_META_LAYER_NAME } from '../../classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer'; export function removeOrphanedSourcesAndLayers( diff --git a/x-pack/plugins/maps/public/locators.test.ts b/x-pack/plugins/maps/public/locators.test.ts index bfcd34c4ae710..aabae1a26c1df 100644 --- a/x-pack/plugins/maps/public/locators.test.ts +++ b/x-pack/plugins/maps/public/locators.test.ts @@ -52,7 +52,7 @@ describe('visualize url generator', () => { { id: LAYER_ID, visible: true, - type: LAYER_TYPE.VECTOR, + type: LAYER_TYPE.GEOJSON_VECTOR, sourceDescriptor: { id: LAYER_ID, type: SOURCE_TYPES.ES_SEARCH, @@ -70,7 +70,7 @@ describe('visualize url generator', () => { expect(location).toMatchObject({ app: 'maps', - path: `/map#/?_g=()&_a=()&initialLayers=(id%3A'13823000-99b9-11ea-9eb6-d9e8adceb647'%2CsourceDescriptor%3A(geoField%3Atest%2Cid%3A'13823000-99b9-11ea-9eb6-d9e8adceb647'%2CindexPatternId%3A'90943e30-9a47-11e8-b64d-95841ca0b247'%2Clabel%3A'Sample%20Data'%2CscalingType%3ALIMIT%2CtooltipProperties%3A!()%2Ctype%3AES_SEARCH)%2Ctype%3AVECTOR%2Cvisible%3A!t)`, + path: `/map#/?_g=()&_a=()&initialLayers=(id%3A'13823000-99b9-11ea-9eb6-d9e8adceb647'%2CsourceDescriptor%3A(geoField%3Atest%2Cid%3A'13823000-99b9-11ea-9eb6-d9e8adceb647'%2CindexPatternId%3A'90943e30-9a47-11e8-b64d-95841ca0b247'%2Clabel%3A'Sample%20Data'%2CscalingType%3ALIMIT%2CtooltipProperties%3A!()%2Ctype%3AES_SEARCH)%2Ctype%3AGEOJSON_VECTOR%2Cvisible%3A!t)`, state: {}, }); }); diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts index 4f336d9a8ad27..6b903214f8f97 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts @@ -8,7 +8,7 @@ import { LAYER_STYLE_TYPE, LAYER_TYPE, SOURCE_TYPES } from '../../common/constants'; jest.mock('../classes/layers/heatmap_layer', () => {}); -jest.mock('../classes/layers/vector_tile_layer/vector_tile_layer', () => {}); +jest.mock('../classes/layers/ems_vector_tile_layer/ems_vector_tile_layer', () => {}); jest.mock('../classes/joins/inner_join', () => {}); jest.mock('../kibana_services', () => ({ getTimeFilter: () => ({ @@ -232,7 +232,7 @@ describe('getQueryableUniqueIndexPatternIds', () => { indexPatternId: string; }) { return { - type: LAYER_TYPE.VECTOR, + type: LAYER_TYPE.GEOJSON_VECTOR, style: { type: LAYER_STYLE_TYPE.VECTOR, }, diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index f58525ea6f974..5f308387e9d8b 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -10,9 +10,8 @@ import { FeatureCollection } from 'geojson'; import _ from 'lodash'; import { Adapters } from 'src/plugins/inspector/public'; import type { Query } from 'src/plugins/data/common'; -import { TileLayer } from '../classes/layers/tile_layer/tile_layer'; -// @ts-ignore -import { VectorTileLayer } from '../classes/layers/vector_tile_layer/vector_tile_layer'; +import { RasterTileLayer } from '../classes/layers/raster_tile_layer/raster_tile_layer'; +import { EmsVectorTileLayer } from '../classes/layers/ems_vector_tile_layer/ems_vector_tile_layer'; import { BlendedVectorLayer, IVectorLayer, @@ -59,6 +58,7 @@ import { ISource } from '../classes/sources/source'; import { ITMSSource } from '../classes/sources/tms_source'; import { IVectorSource } from '../classes/sources/vector_source'; import { ESGeoGridSource } from '../classes/sources/es_geo_grid_source'; +import { EMSTMSSource } from '../classes/sources/ems_tms_source'; import { ILayer } from '../classes/layers/layer'; import { getIsReadOnly } from './ui_selectors'; @@ -70,9 +70,9 @@ export function createLayerInstance( const source: ISource = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters); switch (layerDescriptor.type) { - case LAYER_TYPE.TILE: - return new TileLayer({ layerDescriptor, source: source as ITMSSource }); - case LAYER_TYPE.VECTOR: + case LAYER_TYPE.RASTER_TILE: + return new RasterTileLayer({ layerDescriptor, source: source as ITMSSource }); + case LAYER_TYPE.GEOJSON_VECTOR: const joins: InnerJoin[] = []; const vectorLayerDescriptor = layerDescriptor as VectorLayerDescriptor; if (vectorLayerDescriptor.joins) { @@ -87,8 +87,8 @@ export function createLayerInstance( joins, chartsPaletteServiceGetColor, }); - case LAYER_TYPE.VECTOR_TILE: - return new VectorTileLayer({ layerDescriptor, source: source as ITMSSource }); + case LAYER_TYPE.EMS_VECTOR_TILE: + return new EmsVectorTileLayer({ layerDescriptor, source: source as EMSTMSSource }); case LAYER_TYPE.HEATMAP: return new HeatmapLayer({ layerDescriptor: layerDescriptor as HeatmapLayerDescriptor, @@ -100,7 +100,7 @@ export function createLayerInstance( source: source as IVectorSource, chartsPaletteServiceGetColor, }); - case LAYER_TYPE.TILED_VECTOR: + case LAYER_TYPE.MVT_VECTOR: return new MvtVectorLayer({ layerDescriptor: layerDescriptor as VectorLayerDescriptor, source: source as IVectorSource, diff --git a/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts b/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts index 1cbb3e92134b8..1da2ca14bd16f 100644 --- a/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts +++ b/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts @@ -61,7 +61,7 @@ const getMapsLink = async (context: VisualizeFieldContext) => { { id: uuid(), visible: true, - type: supportsClustering ? LAYER_TYPE.BLENDED_VECTOR : LAYER_TYPE.VECTOR, + type: supportsClustering ? LAYER_TYPE.BLENDED_VECTOR : LAYER_TYPE.GEOJSON_VECTOR, sourceDescriptor: { id: uuid(), type: SOURCE_TYPES.ES_SEARCH, diff --git a/x-pack/plugins/maps/server/embeddable_migrations.ts b/x-pack/plugins/maps/server/embeddable_migrations.ts index 962f5c4fb0d7a..f5356e5eb29a5 100644 --- a/x-pack/plugins/maps/server/embeddable_migrations.ts +++ b/x-pack/plugins/maps/server/embeddable_migrations.ts @@ -9,6 +9,7 @@ import type { SerializableRecord } from '@kbn/utility-types'; import { MapSavedObjectAttributes } from '../common/map_saved_object_type'; import { moveAttribution } from '../common/migrations/move_attribution'; import { setEmsTmsDefaultModes } from '../common/migrations/set_ems_tms_default_modes'; +import { renameLayerTypes } from '../common/migrations/rename_layer_types'; /* * Embeddables such as Maps, Lens, and Visualize can be embedded by value or by reference on a dashboard. @@ -42,4 +43,16 @@ export const embeddableMigrations = { return state; } }, + '8.1.0': (state: SerializableRecord) => { + try { + return { + ...state, + attributes: renameLayerTypes(state as { attributes: MapSavedObjectAttributes }), + } as SerializableRecord; + } catch (e) { + // Do not fail migration for invalid layerListJSON + // Maps application can display invalid layerListJSON error when saved object is viewed + return state; + } + }, }; diff --git a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.test.js b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.test.js index a415d181900d7..796d641f3eff7 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.test.js +++ b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.test.js @@ -202,7 +202,7 @@ describe('buildMapsSavedObjectsTelemetry', () => { max: 1, min: 1, }, - VECTOR: { + GEOJSON_VECTOR: { avg: 1.2, max: 2, min: 1, diff --git a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts index 93a9a118d23bc..3b257fb812bf9 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts @@ -160,7 +160,7 @@ async function isGeoShapeAggLayer(layer: LayerDescriptor): Promise { } if ( - layer.type !== LAYER_TYPE.VECTOR && + layer.type !== LAYER_TYPE.GEOJSON_VECTOR && layer.type !== LAYER_TYPE.BLENDED_VECTOR && layer.type !== LAYER_TYPE.HEATMAP ) { diff --git a/x-pack/plugins/maps/server/maps_telemetry/test_resources/sample_map_saved_objects.json b/x-pack/plugins/maps/server/maps_telemetry/test_resources/sample_map_saved_objects.json index 3adaaaf091e08..e9427a996ad1e 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/test_resources/sample_map_saved_objects.json +++ b/x-pack/plugins/maps/server/maps_telemetry/test_resources/sample_map_saved_objects.json @@ -6,7 +6,7 @@ "title": "Italy Map", "description": "", "mapStateJSON": "{\"zoom\":4.82,\"center\":{\"lon\":11.41545,\"lat\":42.0865},\"timeFilters\":{\"from\":\"now-15w\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":false,\"interval\":0},\"query\":{\"language\":\"lucene\",\"query\":\"\"}}", - "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"id\":\"csq5v\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.65,\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"italy_provinces\"},\"id\":\"0oye8\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#0c1f70\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\"},{\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"id\":\"053fe296-f5ae-4cb0-9e73-a5752cb9ba74\",\"indexPatternId\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"geoField\":\"DestLocation\",\"requestType\":\"point\",\"resolution\":\"COARSE\"},\"id\":\"1gx22\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Greens\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}}}},\"type\":\"VECTOR\"}]", + "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"id\":\"csq5v\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.65,\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"italy_provinces\"},\"id\":\"0oye8\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#0c1f70\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"GEOJSON_VECTOR\"},{\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"id\":\"053fe296-f5ae-4cb0-9e73-a5752cb9ba74\",\"indexPatternId\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"geoField\":\"DestLocation\",\"requestType\":\"point\",\"resolution\":\"COARSE\"},\"id\":\"1gx22\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Greens\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}}}},\"type\":\"GEOJSON_VECTOR\"}]", "uiStateJSON": "{}" }, "references": [ @@ -21,7 +21,7 @@ "title": "France Map", "description": "", "mapStateJSON": "{\"zoom\":3.43,\"center\":{\"lon\":-16.30411,\"lat\":42.88411},\"timeFilters\":{\"from\":\"now-15w\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":false,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"lucene\"}}", - "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"id\":\"csq5v\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.65,\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"france_departments\"},\"id\":\"65xbw\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.25,\"visible\":true,\"joins\":[{\"leftField\":\"iso_3166_2\",\"right\":{\"id\":\"6a263f96-7a96-4f5a-a00e-c89178c1d017\"}}],\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#19c1e6\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\"},{\"sourceDescriptor\":{\"id\":\"240125db-e612-4001-b853-50107e55d984\",\"type\":\"ES_SEARCH\",\"scalingType\":\"LIMIT\",\"indexPatternId\":\"ff959d40-b880-11e8-a6d9-e546fe2bba5f\",\"geoField\":\"geoip.location\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[]},\"id\":\"mdae9\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#1ce619\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\"}]", + "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"id\":\"csq5v\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.65,\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"france_departments\"},\"id\":\"65xbw\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.25,\"visible\":true,\"joins\":[{\"leftField\":\"iso_3166_2\",\"right\":{\"id\":\"6a263f96-7a96-4f5a-a00e-c89178c1d017\"}}],\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#19c1e6\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"GEOJSON_VECTOR\"},{\"sourceDescriptor\":{\"id\":\"240125db-e612-4001-b853-50107e55d984\",\"type\":\"ES_SEARCH\",\"scalingType\":\"LIMIT\",\"indexPatternId\":\"ff959d40-b880-11e8-a6d9-e546fe2bba5f\",\"geoField\":\"geoip.location\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[]},\"id\":\"mdae9\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#1ce619\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"GEOJSON_VECTOR\"}]", "uiStateJSON": "{}" }, "references": [ @@ -36,7 +36,7 @@ "title": "Canada Map", "description": "", "mapStateJSON": "{\"zoom\":2.12,\"center\":{\"lon\":-88.67592,\"lat\":34.23257},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\",\"mode\":\"quick\"},\"refreshConfig\":{\"isPaused\":false,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"lucene\"}}", - "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"id\":\"csq5v\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.65,\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"canada_provinces\"},\"id\":\"kt086\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#60895e\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\"}]", + "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"id\":\"csq5v\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.65,\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"canada_provinces\"},\"id\":\"kt086\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#60895e\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"GEOJSON_VECTOR\"}]", "uiStateJSON": "{}" }, "references": [ @@ -51,7 +51,7 @@ "title": "Single cluster layer with geo_shape field", "description": "", "mapStateJSON": "{\"zoom\":2.12,\"center\":{\"lon\":-88.67592,\"lat\":34.23257},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\",\"mode\":\"quick\"},\"refreshConfig\":{\"isPaused\":false,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"lucene\"}}", - "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"id\":\"51afb7d0-c628-11ea-87d0-0242ac130003\",\"geoField\":\"geometry\",\"metrics\":[{\"type\":\"count\"}],\"requestType\":\"point\",\"resolution\":\"COARSE\",\"indexPatternId\":\"4a7f6010-0aed-11ea-9dd2-95afd7ad44d4\"},\"style\":{\"type\":\"VECTOR\",\"properties\":{\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}},\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"color\":\"Blues\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":true,\"sigma\":3},\"type\":\"ORDINAL\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":0}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"minSize\":7,\"maxSize\":32,\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":true,\"sigma\":3}}},\"iconOrientation\":{\"type\":\"STATIC\",\"options\":{\"orientation\":0}},\"labelText\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"}}},\"labelColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#000000\"}},\"labelSize\":{\"type\":\"STATIC\",\"options\":{\"size\":14}},\"labelBorderColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}}},\"isTimeAware\":true},\"id\":\"8d384d5d-6353-468f-b8f8-8eaa487358c4\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"type\":\"VECTOR\",\"joins\":[]}]", + "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"id\":\"51afb7d0-c628-11ea-87d0-0242ac130003\",\"geoField\":\"geometry\",\"metrics\":[{\"type\":\"count\"}],\"requestType\":\"point\",\"resolution\":\"COARSE\",\"indexPatternId\":\"4a7f6010-0aed-11ea-9dd2-95afd7ad44d4\"},\"style\":{\"type\":\"VECTOR\",\"properties\":{\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}},\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"color\":\"Blues\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":true,\"sigma\":3},\"type\":\"ORDINAL\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":0}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"minSize\":7,\"maxSize\":32,\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":true,\"sigma\":3}}},\"iconOrientation\":{\"type\":\"STATIC\",\"options\":{\"orientation\":0}},\"labelText\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"}}},\"labelColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#000000\"}},\"labelSize\":{\"type\":\"STATIC\",\"options\":{\"size\":14}},\"labelBorderColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}}},\"isTimeAware\":true},\"id\":\"8d384d5d-6353-468f-b8f8-8eaa487358c4\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"type\":\"GEOJSON_VECTOR\",\"joins\":[]}]", "uiStateJSON": "{}" }, "references": [ diff --git a/x-pack/plugins/maps/server/maps_telemetry/util.ts b/x-pack/plugins/maps/server/maps_telemetry/util.ts index 27190c9b82142..defc2cb9aa9b3 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/util.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/util.ts @@ -265,7 +265,7 @@ export function getTermJoinsPerCluster( layerLists: LayerDescriptor[][] ): TELEMETRY_TERM_JOIN_COUNTS_PER_CLUSTER { return getCountsByCluster(layerLists, (layerDescriptor: LayerDescriptor) => { - return layerDescriptor.type === LAYER_TYPE.VECTOR && + return layerDescriptor.type === LAYER_TYPE.GEOJSON_VECTOR && (layerDescriptor as VectorLayerDescriptor)?.joins?.length ? TELEMETRY_TERM_JOIN : null; diff --git a/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js b/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js index 6d23246860423..986878e65eb8b 100644 --- a/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js +++ b/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js @@ -18,6 +18,7 @@ import { setDefaultAutoFitToBounds } from '../../common/migrations/set_default_a import { addTypeToTermJoin } from '../../common/migrations/add_type_to_termjoin'; import { moveAttribution } from '../../common/migrations/move_attribution'; import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes'; +import { renameLayerTypes } from '../../common/migrations/rename_layer_types'; function logMigrationWarning(context, errorMsg, doc) { context.log.warning( @@ -172,6 +173,19 @@ export const savedObjectMigrations = { try { const attributes = setEmsTmsDefaultModes(doc); + return { + ...doc, + attributes, + }; + } catch (e) { + logMigrationWarning(context, e.message, doc); + return doc; + } + }, + '8.1.0': (doc, context) => { + try { + const attributes = renameLayerTypes(doc); + return { ...doc, attributes, diff --git a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx index 0eb6e356e1397..cb3651e4a458a 100644 --- a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx +++ b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx @@ -126,7 +126,7 @@ export const getChoroplethAnomaliesLayer = ( isTimeAware: true, }, visible: false, - type: LAYER_TYPE.VECTOR, + type: LAYER_TYPE.GEOJSON_VECTOR, }; }; diff --git a/x-pack/test/api_integration/apis/maps/migrations.js b/x-pack/test/api_integration/apis/maps/migrations.js index 19f79da940253..26de152f1473e 100644 --- a/x-pack/test/api_integration/apis/maps/migrations.js +++ b/x-pack/test/api_integration/apis/maps/migrations.js @@ -44,7 +44,7 @@ export default function ({ getService }) { type: 'index-pattern', }, ]); - expect(resp.body.migrationVersion).to.eql({ map: '8.0.0' }); // migrtionVersion is derived from both "migrations" and "convertToMultiNamespaceVersion" fields when the object is registered + expect(resp.body.migrationVersion).to.eql({ map: '8.1.0' }); // migrtionVersion is derived from both "migrations" and "convertToMultiNamespaceVersion" fields when the object is registered expect(resp.body.attributes.layerListJSON.includes('indexPatternRefName')).to.be(true); }); @@ -90,7 +90,7 @@ export default function ({ getService }) { } expect(panels.length).to.be(1); expect(panels[0].type).to.be('map'); - expect(panels[0].version).to.be('8.0.0'); + expect(panels[0].version).to.be('8.1.0'); }); }); }); From ded75bdb7ba22c8bb3271594c80a5c88f2332a66 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 3 Dec 2021 18:48:25 +0000 Subject: [PATCH 53/65] chore(NA): splits types from code on @kbn/crypto (#120371) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 1 + packages/BUILD.bazel | 1 + packages/kbn-crypto/BUILD.bazel | 26 ++++++++++++++++++---- packages/kbn-crypto/package.json | 3 +-- packages/kbn-server-http-tools/BUILD.bazel | 2 +- yarn.lock | 4 ++++ 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 1e5a135b723ed..b2299c2391886 100644 --- a/package.json +++ b/package.json @@ -565,6 +565,7 @@ "@types/kbn__apm-utils": "link:bazel-bin/packages/kbn-apm-utils/npm_module_types", "@types/kbn__cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode/npm_module_types", "@types/kbn__config": "link:bazel-bin/packages/kbn-config/npm_module_types", + "@types/kbn__crypto": "link:bazel-bin/packages/kbn-crypto/npm_module_types", "@types/kbn__i18n": "link:bazel-bin/packages/kbn-i18n/npm_module_types", "@types/kbn__i18n-react": "link:bazel-bin/packages/kbn-i18n-react/npm_module_types", "@types/license-checker": "15.0.0", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 8208496f7d800..1cf887290af77 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -84,6 +84,7 @@ filegroup( "//packages/kbn-apm-utils:build_types", "//packages/kbn-cli-dev-mode:build_types", "//packages/kbn-config:build_types", + "//packages/kbn-crypto:build_types", "//packages/kbn-i18n:build_types", "//packages/kbn-i18n-react:build_types", ], diff --git a/packages/kbn-crypto/BUILD.bazel b/packages/kbn-crypto/BUILD.bazel index 0f35aab461078..81ee6d770103c 100644 --- a/packages/kbn-crypto/BUILD.bazel +++ b/packages/kbn-crypto/BUILD.bazel @@ -1,10 +1,11 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") -load("//src/dev/bazel:index.bzl", "jsts_transpiler") +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") PKG_BASE_NAME = "kbn-crypto" PKG_REQUIRE_NAME = "@kbn/crypto" +TYPES_PKG_REQUIRE_NAME = "@types/kbn__crypto" SOURCE_FILES = glob( [ @@ -72,7 +73,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + deps = RUNTIME_DEPS + [":target_node"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) @@ -91,3 +92,20 @@ filegroup( ], visibility = ["//visibility:public"], ) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = TYPES_PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-crypto/package.json b/packages/kbn-crypto/package.json index 8fa6cd3c232fa..96bf21906ed4a 100644 --- a/packages/kbn-crypto/package.json +++ b/packages/kbn-crypto/package.json @@ -3,6 +3,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target_node/index.js", - "types": "./target_types/index.d.ts" + "main": "./target_node/index.js" } diff --git a/packages/kbn-server-http-tools/BUILD.bazel b/packages/kbn-server-http-tools/BUILD.bazel index 609fe6d00f173..b9eae3d022439 100644 --- a/packages/kbn-server-http-tools/BUILD.bazel +++ b/packages/kbn-server-http-tools/BUILD.bazel @@ -38,7 +38,7 @@ RUNTIME_DEPS = [ TYPES_DEPS = [ "//packages/kbn-config-schema", - "//packages/kbn-crypto", + "//packages/kbn-crypto:npm_module_types", "@npm//@hapi/hapi", "@npm//@hapi/hoek", "@npm//joi", diff --git a/yarn.lock b/yarn.lock index fc670fda132ca..c4d2a31724d48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5828,6 +5828,10 @@ version "0.0.0" uid "" +"@types/kbn__crypto@link:bazel-bin/packages/kbn-crypto/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__i18n-react@link:bazel-bin/packages/kbn-i18n-react/npm_module_types": version "0.0.0" uid "" From 3521a928d57adf531ac8331ed1008ac8e5e46ce8 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Fri, 3 Dec 2021 14:56:01 -0500 Subject: [PATCH 54/65] More descriptive audit integration test errors (#120354) --- .../security_api_integration/tests/audit/audit_log.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_api_integration/tests/audit/audit_log.ts b/x-pack/test/security_api_integration/tests/audit/audit_log.ts index fd7db8faadd54..bb4c27976c857 100644 --- a/x-pack/test/security_api_integration/tests/audit/audit_log.ts +++ b/x-pack/test/security_api_integration/tests/audit/audit_log.ts @@ -22,7 +22,14 @@ class FileWrapper { } async readJSON() { const content = await this.read(); - return content.map((l) => JSON.parse(l)); + try { + return content.map((l) => JSON.parse(l)); + } catch (err) { + const contentString = content.join('\n'); + throw new Error( + `Failed to parse audit log JSON, error: "${err.message}", audit.log contents:\n${contentString}` + ); + } } // writing in a file is an async operation. we use this method to make sure logs have been written. async isNotEmpty() { From b5895ec0b441befa5d0a4ada2ac602efd5ffe386 Mon Sep 17 00:00:00 2001 From: ymao1 Date: Fri, 3 Dec 2021 15:05:24 -0500 Subject: [PATCH 55/65] [Alerting] Skip writing alerts when rule execution times out (#114518) * Added cancel() to alerting task runner and writing event log document * Updating rule saved object with timeout execution status * Skip scheduling actions and logging event log for alerts if rule execution is cancelled * Adding config for disabling skipping actions * Fixing types * Adding flag for rule types to opt out of skipping acitons * Passing function into rule executor to determine whether to write AAD * Using task runner uuid to differentiate between task instances * Adding functional test * Fixing types * Updating service function name and adding logic to persistence rule type Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/alerting/server/mocks.ts | 1 + .../server/task_runner/task_runner.ts | 1 + x-pack/plugins/alerting/server/types.ts | 1 + .../server/routes/alerts/test_utils/index.ts | 1 + .../utils/create_lifecycle_executor.test.ts | 27 +++++++++++++++++++ .../server/utils/create_lifecycle_executor.ts | 14 +++++++--- .../utils/create_lifecycle_rule_type.test.ts | 23 +++++++++++++++- .../create_persistence_rule_type_wrapper.ts | 11 +++++++- .../server/utils/rule_executor_test_utils.ts | 3 +++ .../routes/rules/preview_rules_route.ts | 3 +++ .../rule_types/__mocks__/rule_type.ts | 1 + 11 files changed, 81 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/alerting/server/mocks.ts b/x-pack/plugins/alerting/server/mocks.ts index 7fb748a305037..f23dbf05449ad 100644 --- a/x-pack/plugins/alerting/server/mocks.ts +++ b/x-pack/plugins/alerting/server/mocks.ts @@ -74,6 +74,7 @@ const createAlertServicesMock = < .mockReturnValue(alertInstanceFactoryMock), savedObjectsClient: savedObjectsClientMock.create(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), + shouldWriteAlerts: () => true, }; }; export type AlertServicesMock = ReturnType; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index fb7268ef529da..0cf5202787392 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -323,6 +323,7 @@ export class TaskRunner< InstanceContext, WithoutReservedActionGroups >(alertInstances), + shouldWriteAlerts: () => this.shouldLogAndScheduleActionsForAlerts(), }, params, state: alertTypeState as State, diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index c1645936c06e9..343b717dcb1aa 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -75,6 +75,7 @@ export interface AlertServices< alertInstanceFactory: ( id: string ) => PublicAlertInstance; + shouldWriteAlerts: () => boolean; } export interface AlertExecutorOptions< diff --git a/x-pack/plugins/apm/server/routes/alerts/test_utils/index.ts b/x-pack/plugins/apm/server/routes/alerts/test_utils/index.ts index 22649a7010461..a8610bbcc8d37 100644 --- a/x-pack/plugins/apm/server/routes/alerts/test_utils/index.ts +++ b/x-pack/plugins/apm/server/routes/alerts/test_utils/index.ts @@ -45,6 +45,7 @@ export const createRuleTypeMocks = () => { alertInstanceFactory: jest.fn(() => ({ scheduleActions })), alertWithLifecycle: jest.fn(), logger: loggerMock, + shouldWriteAlerts: () => true, }; return { diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts index fcddcab378bc6..2c5fe09d80563 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts @@ -350,6 +350,33 @@ describe('createLifecycleExecutor', () => { }) ); }); + + it('does not write alert documents when rule execution is cancelled and feature flags indicate to skip', async () => { + const logger = loggerMock.create(); + const ruleDataClientMock = createRuleDataClientMock(); + const executor = createLifecycleExecutor( + logger, + ruleDataClientMock + )<{}, TestRuleState, never, never, never>(async (options) => { + expect(options.state).toEqual(initialRuleState); + + const nextRuleState: TestRuleState = { + aRuleStateKey: 'NEXT_RULE_STATE_VALUE', + }; + + return nextRuleState; + }); + + await executor( + createDefaultAlertExecutorOptions({ + params: {}, + state: { wrapped: initialRuleState, trackedAlerts: {} }, + shouldWriteAlerts: false, + }) + ); + + expect(ruleDataClientMock.getWriter).not.toHaveBeenCalled(); + }); }); type TestRuleState = Record & { diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts index 1acbc0c3f43bd..c30b1654a3587 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -140,7 +140,7 @@ export const createLifecycleExecutor = > ): Promise> => { const { - services: { alertInstanceFactory }, + services: { alertInstanceFactory, shouldWriteAlerts }, state: previousState, } = options; @@ -281,7 +281,15 @@ export const createLifecycleExecutor = const newEventsToIndex = makeEventsDataMapFor(newAlertIds); const allEventsToIndex = [...trackedEventsToIndex, ...newEventsToIndex]; - if (allEventsToIndex.length > 0 && ruleDataClient.isWriteEnabled()) { + // Only write alerts if: + // - writing is enabled + // AND + // - rule execution has not been cancelled due to timeout + // OR + // - if execution has been cancelled due to timeout, if feature flags are configured to write alerts anyway + const writeAlerts = ruleDataClient.isWriteEnabled() && shouldWriteAlerts(); + + if (allEventsToIndex.length > 0 && writeAlerts) { logger.debug(`Preparing to index ${allEventsToIndex.length} alerts.`); await ruleDataClient.getWriter().bulk({ @@ -307,6 +315,6 @@ export const createLifecycleExecutor = return { wrapped: nextWrappedState ?? ({} as State), - trackedAlerts: ruleDataClient.isWriteEnabled() ? nextTrackedAlerts : {}, + trackedAlerts: writeAlerts ? nextTrackedAlerts : {}, }; }; diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts index 2284ad5e796ee..f0e2412629bb1 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts @@ -21,7 +21,7 @@ import { createLifecycleRuleTypeFactory } from './create_lifecycle_rule_type_fac type RuleTestHelpers = ReturnType; -function createRule() { +function createRule(shouldWriteAlerts: boolean = true) { const ruleDataClientMock = createRuleDataClientMock(); const factory = createLifecycleRuleTypeFactory({ @@ -110,6 +110,7 @@ function createRule() { alertInstanceFactory, savedObjectsClient: {} as any, scopedClusterClient: {} as any, + shouldWriteAlerts: () => shouldWriteAlerts, }, spaceId: 'spaceId', state, @@ -152,6 +153,26 @@ describe('createLifecycleRuleTypeFactory', () => { }); }); + describe('when rule is cancelled due to timeout and config flags indicate to skip actions', () => { + beforeEach(() => { + helpers = createRule(false); + helpers.ruleDataClientMock.isWriteEnabled.mockReturnValue(true); + }); + + it("doesn't persist anything", async () => { + await helpers.alertWithLifecycle([ + { + id: 'opbeans-java', + fields: { + 'service.name': 'opbeans-java', + }, + }, + ]); + + expect(helpers.ruleDataClientMock.getWriter().bulk).toHaveBeenCalledTimes(0); + }); + }); + describe('when alerts are new', () => { beforeEach(async () => { await helpers.alertWithLifecycle([ diff --git a/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts b/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts index afdcf856a872f..de1193771dd95 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts @@ -25,7 +25,16 @@ export const createPersistenceRuleTypeWrapper: CreatePersistenceRuleTypeWrapper const numAlerts = alerts.length; logger.debug(`Found ${numAlerts} alerts.`); - if (ruleDataClient.isWriteEnabled() && numAlerts) { + // Only write alerts if: + // - writing is enabled + // AND + // - rule execution has not been cancelled due to timeout + // OR + // - if execution has been cancelled due to timeout, if feature flags are configured to write alerts anyway + const writeAlerts = + ruleDataClient.isWriteEnabled() && options.services.shouldWriteAlerts(); + + if (writeAlerts && numAlerts) { const commonRuleFields = getCommonAlertFields(options); const CHUNK_SIZE = 10000; diff --git a/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts b/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts index 95a6761152371..288830f4b3804 100644 --- a/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts +++ b/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts @@ -31,6 +31,7 @@ export const createDefaultAlertExecutorOptions = < createdAt = new Date(), startedAt = new Date(), updatedAt = new Date(), + shouldWriteAlerts = true, }: { alertId?: string; ruleName?: string; @@ -39,6 +40,7 @@ export const createDefaultAlertExecutorOptions = < createdAt?: Date; startedAt?: Date; updatedAt?: Date; + shouldWriteAlerts?: boolean; }): AlertExecutorOptions => ({ alertId, createdBy: 'CREATED_BY', @@ -69,6 +71,7 @@ export const createDefaultAlertExecutorOptions = < .alertInstanceFactory, savedObjectsClient: savedObjectsClientMock.create(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), + shouldWriteAlerts: () => shouldWriteAlerts, }, state, updatedBy: null, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts index 882732544dcbb..3949e71582020 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -122,6 +122,7 @@ export const previewRulesRoute = async ( ruleTypeId: string, ruleTypeName: string, params: TParams, + shouldWriteAlerts: () => boolean, alertInstanceFactory: ( id: string ) => Pick< @@ -157,6 +158,7 @@ export const previewRulesRoute = async ( previousStartedAt, rule, services: { + shouldWriteAlerts, alertInstanceFactory, savedObjectsClient: context.core.savedObjects.client, scopedClusterClient: context.core.elasticsearch.client, @@ -191,6 +193,7 @@ export const previewRulesRoute = async ( signalRuleAlertType.id, signalRuleAlertType.name, previewRuleParams, + () => true, alertInstanceFactoryStub ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index c6f818f04fc5d..aaab81a4c66ff 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -78,6 +78,7 @@ export const createRuleTypeMocks = ( findAlerts: jest.fn(), // TODO: does this stay? alertWithPersistence: jest.fn(), logger: loggerMock, + shouldWriteAlerts: () => true, }; return { From dbde0a75f92303b525d7845735085d08329006a1 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Fri, 3 Dec 2021 15:43:32 -0500 Subject: [PATCH 56/65] Update performance docs to touch on server-side considerations (#120356) * Update performance docs to touch on server-side considerations * update edited date --- dev_docs/key_concepts/performance.mdx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/dev_docs/key_concepts/performance.mdx b/dev_docs/key_concepts/performance.mdx index 0201c7774f854..5d955c789ddeb 100644 --- a/dev_docs/key_concepts/performance.mdx +++ b/dev_docs/key_concepts/performance.mdx @@ -3,11 +3,13 @@ id: kibDevPerformance slug: /kibana-dev-docs/key-concepts/performance title: Performance summary: Performance tips for Kibana development. -date: 2021-09-02 +date: 2021-12-03 tags: ['kibana', 'onboarding', 'dev', 'performance'] --- -## Keep Kibana fast +## Client-side considerations + +### Lazy load code _tl;dr_: Load as much code lazily as possible. Everyone loves snappy applications with a responsive UI and hates spinners. Users deserve the @@ -105,3 +107,15 @@ Many OSS tools allow you to analyze the generated stats file: Webpack authors - [webpack-visualizer](https://chrisbateman.github.io/webpack-visualizer/) - [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) + +## Server-side considerations + +### Don't block the event loop + +[Node.js is single threaded](https://nodejs.dev/learn/introduction-to-nodejs) which means a single CPU-intensive server-side, synchronous operation will block any other functionality waiting to execute on the Kibana server. The affects background tasks, like alerts, and search sessions, as well as search requests and page loads. + +**When writing code that will run on the server, [don't block the event loop](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/)**. Instead consider: + +- Writing async code. For example, leverage [setImmediate](https://nodejs.dev/learn/understanding-setimmediate) inside for loops. +- Executing logic on the client instead. This may not be a good option if you require a lot of data going back and forth between the server and the client, as that can also slow down the user's experience, especially over slower bandwidth internet connections. +- Worker threads are also an option if the code doesn't rely on stateful Kibana services. If you are interested in using worker threads, please reach out to a tech-lead before doing so. We will likely want to implement a worker threads pool to ensure worker threads cooperate appropriately. \ No newline at end of file From d21d721bd1104a3e231baa5775fcc6749423aafa Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Fri, 3 Dec 2021 12:44:50 -0800 Subject: [PATCH 57/65] [presentation] Create Expression Input (#119411) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/presentation_util/common/index.ts | 6 + src/plugins/presentation_util/kibana.json | 13 +- .../expression_input}/autocomplete.ts | 24 +- .../components/expression_input/constants.ts | 28 ++ .../expression_input.stories.tsx | 94 +++++ .../expression_input/expression_input.tsx | 82 +++++ .../components/expression_input/index.tsx | 16 + .../components/expression_input/language.ts | 23 +- .../components/expression_input/providers.ts | 189 ++++++++++ .../components/expression_input/reference.ts | 24 +- .../public/components/index.tsx | 5 + .../public/components/types.ts | 43 +++ src/plugins/presentation_util/public/index.ts | 19 +- src/plugins/presentation_util/public/mocks.ts | 2 + .../presentation_util/public/plugin.ts | 3 + .../public/services/index.ts | 3 + src/plugins/presentation_util/public/types.ts | 2 + .../presentation_util/storybook/decorator.tsx | 20 +- .../presentation_util/storybook/main.ts | 4 +- .../presentation_util/storybook/manager.ts | 5 + .../canvas/common/lib/autocomplete.test.ts | 198 ---------- x-pack/plugins/canvas/common/lib/index.ts | 1 - x-pack/plugins/canvas/public/application.tsx | 3 - .../components/expression/expression.tsx | 19 +- .../public/components/expression/index.tsx | 7 +- .../expression_input.stories.storyshot | 33 +- .../__stories__/expression_input.stories.tsx | 76 ++-- .../expression_input/expression_input.tsx | 344 ++---------------- x-pack/plugins/canvas/public/plugin.tsx | 5 + .../canvas/storybook/storyshots.test.tsx | 5 + .../translations/translations/ja-JP.json | 12 +- .../translations/translations/zh-CN.json | 12 +- 32 files changed, 663 insertions(+), 657 deletions(-) rename {x-pack/plugins/canvas/common/lib => src/plugins/presentation_util/public/components/expression_input}/autocomplete.ts (98%) create mode 100644 src/plugins/presentation_util/public/components/expression_input/constants.ts create mode 100644 src/plugins/presentation_util/public/components/expression_input/expression_input.stories.tsx create mode 100644 src/plugins/presentation_util/public/components/expression_input/expression_input.tsx create mode 100644 src/plugins/presentation_util/public/components/expression_input/index.tsx rename x-pack/plugins/canvas/public/lib/monaco_language_def.ts => src/plugins/presentation_util/public/components/expression_input/language.ts (74%) create mode 100644 src/plugins/presentation_util/public/components/expression_input/providers.ts rename {x-pack/plugins/canvas => src/plugins/presentation_util}/public/components/expression_input/reference.ts (79%) delete mode 100644 x-pack/plugins/canvas/common/lib/autocomplete.test.ts diff --git a/src/plugins/presentation_util/common/index.ts b/src/plugins/presentation_util/common/index.ts index 4510a0aac5a0b..a84a78c823a5f 100644 --- a/src/plugins/presentation_util/common/index.ts +++ b/src/plugins/presentation_util/common/index.ts @@ -12,4 +12,10 @@ export const PLUGIN_ID = 'presentationUtil'; export const PLUGIN_NAME = 'presentationUtil'; +/** + * The unique identifier for the Expressions Language for use in the ExpressionInput + * and CodeEditor components. + */ +export const EXPRESSIONS_LANGUAGE_ID = 'kibana-expressions'; + export * from './labs'; diff --git a/src/plugins/presentation_util/kibana.json b/src/plugins/presentation_util/kibana.json index 210937b335e50..32460a8455152 100644 --- a/src/plugins/presentation_util/kibana.json +++ b/src/plugins/presentation_util/kibana.json @@ -9,7 +9,16 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "extraPublicDirs": ["common/lib"], - "requiredPlugins": ["savedObjects", "data", "dataViews", "embeddable", "kibanaReact"], + "extraPublicDirs": [ + "common/lib" + ], + "requiredPlugins": [ + "savedObjects", + "data", + "dataViews", + "embeddable", + "kibanaReact", + "expressions" + ], "optionalPlugins": [] } diff --git a/x-pack/plugins/canvas/common/lib/autocomplete.ts b/src/plugins/presentation_util/public/components/expression_input/autocomplete.ts similarity index 98% rename from x-pack/plugins/canvas/common/lib/autocomplete.ts rename to src/plugins/presentation_util/public/components/expression_input/autocomplete.ts index 88fb6b052b957..5f0c9cab6215c 100644 --- a/x-pack/plugins/canvas/common/lib/autocomplete.ts +++ b/src/plugins/presentation_util/public/components/expression_input/autocomplete.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import { uniq } from 'lodash'; @@ -15,9 +16,9 @@ import { ExpressionFunction, ExpressionFunctionParameter, getByAlias, -} from '../../../../../src/plugins/expressions/common'; +} from '../../../../expressions/common'; -const MARKER = 'CANVAS_SUGGESTION_MARKER'; +const MARKER = 'EXPRESSIONS_SUGGESTION_MARKER'; interface BaseSuggestion { text: string; @@ -25,11 +26,6 @@ interface BaseSuggestion { end: number; } -export interface FunctionSuggestion extends BaseSuggestion { - type: 'function'; - fnDef: ExpressionFunction; -} - interface ArgSuggestionValue extends Omit { name: string; } @@ -43,8 +39,6 @@ interface ValueSuggestion extends BaseSuggestion { type: 'value'; } -export type AutocompleteSuggestion = FunctionSuggestion | ArgSuggestion | ValueSuggestion; - interface FnArgAtPosition { ast: ExpressionASTWithMeta; fnIndex: number; @@ -57,6 +51,7 @@ interface FnArgAtPosition { // If this function is a sub-expression function, we need the parent function and argument // name to determine the return type of the function parentFn?: string; + // If this function is a sub-expression function, the context could either be local or it // could be the parent's previous function. contextFn?: string | null; @@ -101,6 +96,13 @@ type ExpressionASTWithMeta = ASTMetaInformation< > >; +export interface FunctionSuggestion extends BaseSuggestion { + type: 'function'; + fnDef: ExpressionFunction; +} + +export type AutocompleteSuggestion = FunctionSuggestion | ArgSuggestion | ValueSuggestion; + // Typeguard for checking if ExpressionArg is a new expression function isExpression( maybeExpression: ExpressionArgASTWithMeta diff --git a/src/plugins/presentation_util/public/components/expression_input/constants.ts b/src/plugins/presentation_util/public/components/expression_input/constants.ts new file mode 100644 index 0000000000000..f937d55cbf9bb --- /dev/null +++ b/src/plugins/presentation_util/public/components/expression_input/constants.ts @@ -0,0 +1,28 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CodeEditorProps } from '../../../../kibana_react/public'; + +export const LANGUAGE_CONFIGURATION = { + autoClosingPairs: [ + { + open: '{', + close: '}', + }, + ], +}; + +export const CODE_EDITOR_OPTIONS: CodeEditorProps['options'] = { + scrollBeyondLastLine: false, + quickSuggestions: true, + minimap: { + enabled: false, + }, + wordWrap: 'on', + wrappingIndent: 'indent', +}; diff --git a/src/plugins/presentation_util/public/components/expression_input/expression_input.stories.tsx b/src/plugins/presentation_util/public/components/expression_input/expression_input.stories.tsx new file mode 100644 index 0000000000000..648171959791f --- /dev/null +++ b/src/plugins/presentation_util/public/components/expression_input/expression_input.stories.tsx @@ -0,0 +1,94 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import { Meta } from '@storybook/react'; + +import { ExpressionFunction, ExpressionFunctionParameter, Style } from 'src/plugins/expressions'; +import { ExpressionInput } from '../expression_input'; +import { registerExpressionsLanguage } from './language'; + +const content: ExpressionFunctionParameter<'string'> = { + name: 'content', + required: false, + help: 'A string of text that contains Markdown. To concatenate, pass the `string` function multiple times.', + types: ['string'], + default: '', + aliases: ['_', 'expression'], + multi: true, + resolve: false, + options: [], + accepts: () => true, +}; + +const font: ExpressionFunctionParameter

    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%0A2B?K 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'&rQ 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 z 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 diff --git a/docs/management/connectors/images/servicenow-sir-update-connector.png b/docs/management/connectors/images/servicenow-sir-update-connector.png new file mode 100644 index 0000000000000000000000000000000000000000..5a12ad971f62fb6ed060810726501d2d37889da2 GIT binary patch literal 250246 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