diff --git a/.ci/Dockerfile b/.ci/Dockerfile index bf84d0a78d581..109b9ffab3cc5 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,7 +1,7 @@ # NOTE: This Dockerfile is ONLY used to run certain tasks in CI. It is not used to run Kibana or as a distributable. # If you're looking for the Kibana Docker image distributable, please see: src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts -ARG NODE_VERSION=18.17.1 +ARG NODE_VERSION=18.18.2 FROM node:${NODE_VERSION} AS base diff --git a/.node-version b/.node-version index 4a1f488b6c3b6..87ec8842b158d 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -18.17.1 +18.18.2 diff --git a/.nvmrc b/.nvmrc index 4a1f488b6c3b6..87ec8842b158d 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.17.1 +18.18.2 diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 3415256bedbfd..e614bdff172f9 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -22,13 +22,13 @@ load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "yarn_install # Setup the Node.js toolchain for the architectures we want to support node_repositories( node_repositories = { - "18.17.1-darwin_amd64": ("node-v18.17.1-darwin-x64.tar.gz", "node-v18.17.1-darwin-x64", "b3e083d2715f07ec3f00438401fb58faa1e0bdf3c7bde9f38b75ed17809d92fa"), - "18.17.1-darwin_arm64": ("node-v18.17.1-darwin-arm64.tar.gz", "node-v18.17.1-darwin-arm64", "18ca716ea57522b90473777cb9f878467f77fdf826d37beb15a0889fdd74533e"), - "18.17.1-linux_arm64": ("node-v18.17.1-linux-arm64.tar.xz", "node-v18.17.1-linux-arm64", "2743722f164df953b11663a6c25f9cf35769bf500e712e21a6c20e896177da30"), - "18.17.1-linux_amd64": ("node-v18.17.1-linux-x64.tar.xz", "node-v18.17.1-linux-x64", "4612954fef461bb05ba952112636bd11e51c0a59db94e6c4b26328fee4d4d9ab"), - "18.17.1-windows_amd64": ("node-v18.17.1-win-x64.zip", "node-v18.17.1-win-x64", "afc83f5cf6e8b45a4d3fb842904f604dcd271fefada31ad6654f8302f8da28c9"), + "18.18.2-darwin_amd64": ("node-v18.18.2-darwin-x64.tar.gz", "node-v18.18.2-darwin-x64", "5bb8da908ed590e256a69bf2862238c8a67bc4600119f2f7721ca18a7c810c0f"), + "18.18.2-darwin_arm64": ("node-v18.18.2-darwin-arm64.tar.gz", "node-v18.18.2-darwin-arm64", "9f982cc91b28778dd8638e4f94563b0c2a1da7aba62beb72bd427721035ab553"), + "18.18.2-linux_arm64": ("node-v18.18.2-linux-arm64.tar.xz", "node-v18.18.2-linux-arm64", "8a5a03f6a742159c9aa0ae3a99b368cd938cf62f3a5522a2e5acbe6313710efe"), + "18.18.2-linux_amd64": ("node-v18.18.2-linux-x64.tar.xz", "node-v18.18.2-linux-x64", "f7cf590bc7153f3beaa9e1138d00e50d74df223f0bec61f63e7df65f7315b76a"), + "18.18.2-windows_amd64": ("node-v18.18.2-win-x64.zip", "node-v18.18.2-win-x64", "3bb0e51e579a41a22b3bf6cb2f3e79c03801aa17acbe0ca00fc555d1282e7acd"), }, - node_version = "18.17.1", + node_version = "18.18.2", node_urls = [ "https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/dist/v{version}/{filename}", ], diff --git a/docs/action-type-template.asciidoc b/docs/action-type-template.asciidoc index 8ff643c883a40..9bd61039712b3 100644 --- a/docs/action-type-template.asciidoc +++ b/docs/action-type-template.asciidoc @@ -28,42 +28,9 @@ List of user-facing connector configurations. This should align with the fields Property1:: A short description of this property. Property2:: A short description of this property with format hints. This can be specified in `this specific format`. -[float] -[[preconfigured--configuration]] -=== Create preconfigured connectors - -If you are running {kib} on-prem, you can define connectors by -adding `xpack.actions.preconfigured` settings to your `kibana.yml` file. -For example: - -//// -Example preconfigured format for this connector type -//// - -[source,text] --- -xpack.actions.preconfigured: - my-: - name: preconfigured--connector-type - actionTypeId: . - config: - property1: value1 - property2: value2 - secrets: - property3: value3 --- - //// -List of properties from the ConfigSchema and SecretsSchema for this action type. +Add preconfigured settings for this connector type in alert-action-settings.asciidoc and an example in pre-configured-connectors.asciidoc. //// -Config defines information for the connector type. - -`property1`:: A short description of this property. -`property2`:: A short descriptionn of this property. - -Secrets defines sensitive information for the connector type. - -`property3`:: A short descriptionn of this property. [float] [[-action-configuration]] diff --git a/docs/developer/advanced/upgrading-nodejs.asciidoc b/docs/developer/advanced/upgrading-nodejs.asciidoc index 9587dfbfd14a0..547ef0080cee8 100644 --- a/docs/developer/advanced/upgrading-nodejs.asciidoc +++ b/docs/developer/advanced/upgrading-nodejs.asciidoc @@ -17,7 +17,7 @@ These files must be updated when upgrading Node.js: - {kib-repo}blob/{branch}/WORKSPACE.bazel[`WORKSPACE.bazel`] - The version is specified in the `node_version` property. Besides this property, the list of files under `node_repositories` must be updated along with their respective SHA256 hashes. These can be found on the https://nodejs.org[nodejs.org] website. - Example for Node.js v18.17.1: https://nodejs.org/dist/v18.17.1/SHASUMS256.txt.asc + Example for Node.js v18.18.2: https://nodejs.org/dist/v18.18.2/SHASUMS256.txt.asc See PR {kib-repo}pull/128123[#128123] for an example of how the Node.js version has been upgraded previously. diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index 357d0e5de50ea..a8a6409ec49ba 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -7,9 +7,9 @@ Connectors provide a central place to store connection information for services [cols="2"] |=== -a| <> +a| <> -| Send a request to AWS Bedrock. +| Send a request to {bedrock}. a| <> diff --git a/docs/management/connectors/action-types/bedrock.asciidoc b/docs/management/connectors/action-types/bedrock.asciidoc index afefc5914435f..a01a750ae77aa 100644 --- a/docs/management/connectors/action-types/bedrock.asciidoc +++ b/docs/management/connectors/action-types/bedrock.asciidoc @@ -1,15 +1,15 @@ [[bedrock-action-type]] -== AWS Bedrock connector and action +== {bedrock} connector and action ++++ -AWS Bedrock +{bedrock} ++++ -:frontmatter-description: Add a connector that can send requests to AWS Bedrock. +:frontmatter-description: Add a connector that can send requests to {bedrock}. :frontmatter-tags-products: [kibana] :frontmatter-tags-content-type: [how-to] :frontmatter-tags-user-goals: [configure] -The AWS Bedrock connector uses https://github.com/axios/axios[axios] to send a POST request to AWS Bedrock. The connector uses the <> to send the request. +The {bedrock} connector uses https://github.com/axios/axios[axios] to send a POST request to {bedrock}. The connector uses the <> to send the request. [float] [[define-bedrock-ui]] @@ -19,18 +19,18 @@ You can create connectors in *{stack-manage-app} > {connectors-ui}*. For exampl [role="screenshot"] // TODO: need logo before screenshot -image::management/connectors/images/bedrock-connector.png[AWS Bedrock connector] +image::management/connectors/images/bedrock-connector.png[{bedrock} connector] [float] [[bedrock-connector-configuration]] ==== Connector configuration -AWS Bedrock connectors have the following configuration properties: +{bedrock} connectors have the following configuration properties: Name:: The name of the connector. -API URL:: The AWS Bedrock request URL. -Default model:: The GAI model for AWS Bedrock to use. Current support is for the Anthropic Claude models, defaulting to Claude 2. The model can be set on a per request basis by including a "model" parameter alongside the request body. -Region:: The AWS Bedrock request URL. +API URL:: The {bedrock} request URL. +Default model:: The GAI model for {bedrock} to use. Current support is for the Anthropic Claude models, defaulting to Claude 2. The model can be set on a per request basis by including a "model" parameter alongside the request body. +Region:: The {bedrock} request URL. Access Key:: The AWS access key for authentication. Secret:: The secret for authentication. @@ -43,11 +43,11 @@ as you're creating or editing the connector in {kib}. For example: [role="screenshot"] // TODO: need logo before screenshot -image::management/connectors/images/bedrock-params.png[AWS Bedrock params test] +image::management/connectors/images/bedrock-params.png[{bedrock} params test] -The AWS Bedrock actions have the following configuration properties. +The {bedrock} actions have the following configuration properties. -Body:: A stringified JSON payload sent to the AWS Bedrock Invoke Model API URL. For example: +Body:: A stringified JSON payload sent to the {bedrock} Invoke Model API URL. For example: + [source,text] -- diff --git a/docs/management/connectors/pre-configured-connectors.asciidoc b/docs/management/connectors/pre-configured-connectors.asciidoc index 6e6694e8a839d..3b0a5e3004f83 100644 --- a/docs/management/connectors/pre-configured-connectors.asciidoc +++ b/docs/management/connectors/pre-configured-connectors.asciidoc @@ -1,5 +1,9 @@ [[pre-configured-connectors]] == Preconfigured connectors +:frontmatter-description: Define connectors in the {kib} configuration file. +:frontmatter-tags-products: [kibana] +:frontmatter-tags-content-type: [how-to] +:frontmatter-tags-user-goals: [configure] If you are running {kib} on-prem, you can preconfigure a connector to have all the information it needs prior to startup by adding it to the `kibana.yml` file. @@ -20,6 +24,7 @@ predefined, including the connector name and ID. Add `xpack.actions.preconfigured` settings to your `kibana.yml` file. The settings vary depending on which type of connector you're adding. +Refer to <>. This example shows a valid configuration for a Slack connector and a Webhook connector: @@ -107,6 +112,7 @@ Index names must start with `kibana-alert-history-` to take advantage of the pre [[preconfigured-connector-examples]] === Examples +* <> * <> * <> * <> @@ -128,6 +134,30 @@ Index names must start with `kibana-alert-history-` to take advantage of the pre * <> * <> +[float] +[[preconfigured-bedrock-configuration]] +==== {bedrock} connectors + +The following example creates an <>: + +[source,text] +-- +xpack.actions.preconfigured: + my-bedrock: + name: preconfigured-bedrock-connector-type + actionTypeId: .bedrock + config: + apiUrl: https://bedrock.us-east-1.amazonaws.com <1> + defaultModel: anthropic.claude-v2 <2> + secrets: + accessKey: key-value <3> + secret: secret-value <4> +-- +<1> The {bedrock} request URL. +<2> The default model to use for requests. Current support is for the Anthropic Claude models, defaulting to Claude 2. +<3> The AWS access key for authentication. +<4> The AWS secret for authentication. + [float] [[preconfigured-d3security-configuration]] ==== D3 Security connectors @@ -302,7 +332,7 @@ xpack.actions.preconfigured: secrets: apiKey: superlongapikey <4> -- -<1> The OpenAI request URL +<1> The OpenAI request URL. <2> The OpenAI API provider, either `OpenAI` or `Azure OpenAI`. <3> The default model to use for requests. This setting is optional and applicable only when `apiProvider` is `OpenAI`. <4> The OpenAI or Azure OpenAI API key for authentication. diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 4312d2825a9d4..5130fad9abad4 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -267,6 +267,7 @@ For a <>, specifies the OpenAI API provider A configuration URL that varies by connector: + -- +* For an <>, specifies the {bedrock} request URL. * For a <>, specifies the OpenAI request URL. * For a <>, specifies the {ibm-r} instance URL. * For a <>, specifies the Jira instance URL. @@ -327,7 +328,12 @@ NOTE: If you are using the `xpack.actions.allowedHosts` setting, make sure the h For a <>, specifies a string from the response body of the create case method that corresponds to the external service identifier. `xpack.actions.preconfigured..config.defaultModel`:: -For a <>, specifies the default model to use for requests. It is optional and applicable only when `xpack.actions.preconfigured..config.apiProvider` is `OpenAI`. +The default model to use for requests, which varies by connector: ++ +-- +* For an <>, current support is for the Anthropic Claude models. Defaults to `anthropic.claude-v2`. +* For a <>, it is optional and applicable only when `xpack.actions.preconfigured..config.apiProvider` is `OpenAI`. +-- `xpack.actions.preconfigured..config.executionTimeField`:: For an <>, a field that indicates when the document was indexed. @@ -463,6 +469,9 @@ Sensitive configuration details, such as username, password, and keys, which are + TIP: Sensitive properties, such as passwords, should be stored in the <>. +`xpack.actions.preconfigured..secrets.accessKey`:: +For an <>, specifies the AWS access key for authentication. + `xpack.actions.preconfigured..secrets.apikey`:: An API key secret that varies by connector: + @@ -517,6 +526,9 @@ For a <>, <.secrets.routingKey`:: For a <>, specifies the 32 character PagerDuty Integration Key for an integration on a service, also referred to as the routing key. +`xpack.actions.preconfigured..secrets.secret`:: +For an <>, specifies the AWS secret for authentication. + `xpack.actions.preconfigured..secrets.secretsUrl`:: For an <> with URL authentication, specifies the request URL for the Elastic Alerts trigger in xMatters with the API key included in the URL. It is used only when `xpack.actions.preconfigured..config.usesBasic` is `false`. diff --git a/package.json b/package.json index e1a19a7f8cf8e..f54e359b6c1ff 100644 --- a/package.json +++ b/package.json @@ -74,12 +74,12 @@ "url": "https://github.com/elastic/kibana.git" }, "engines": { - "node": "18.17.1", + "node": "18.18.2", "yarn": "^1.22.19" }, "resolutions": { "**/@hello-pangea/dnd": "16.2.0", - "**/@types/node": "18.17.5", + "**/@types/node": "18.18.5", "**/@typescript-eslint/utils": "5.62.0", "**/chokidar": "^3.5.3", "**/globule/minimatch": "^3.1.2", @@ -1360,7 +1360,7 @@ "@types/multistream": "^4.1.0", "@types/mustache": "^0.8.31", "@types/nock": "^10.0.3", - "@types/node": "18.17.5", + "@types/node": "18.18.5", "@types/node-fetch": "2.6.4", "@types/node-forge": "^1.3.1", "@types/nodemailer": "^6.4.0", diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 0c998d4ebe786..147289b888cd1 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -167,7 +167,6 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { connectorsSharepointOnline: `${ENTERPRISE_SEARCH_DOCS}connectors-sharepoint-online.html`, connectorsSlack: `${ENTERPRISE_SEARCH_DOCS}connectors-slack.html`, connectorsTeams: `${ENTERPRISE_SEARCH_DOCS}connectors-teams.html`, - connectorsWorkplaceSearch: `${ENTERPRISE_SEARCH_DOCS}workplace-search-connectors.html`, connectorsZoom: `${ENTERPRISE_SEARCH_DOCS}connectors-zoom.html`, crawlerExtractionRules: `${ENTERPRISE_SEARCH_DOCS}crawler-extraction-rules.html`, crawlerManaging: `${ENTERPRISE_SEARCH_DOCS}crawler-managing.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 4b38c8ccc8fc9..5d2f64c5d8e9b 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -148,7 +148,6 @@ export interface DocLinks { readonly connectorsSharepointOnline: string; readonly connectorsTeams: string; readonly connectorsSlack: string; - readonly connectorsWorkplaceSearch: string; readonly connectorsZoom: string; readonly crawlerExtractionRules: string; readonly crawlerManaging: string; diff --git a/packages/kbn-es-archiver/src/lib/progress.ts b/packages/kbn-es-archiver/src/lib/progress.ts index 59be205eb1283..c7f556b4b5250 100644 --- a/packages/kbn-es-archiver/src/lib/progress.ts +++ b/packages/kbn-es-archiver/src/lib/progress.ts @@ -13,7 +13,7 @@ const SECOND = 1000; export class Progress { private total?: number; private complete?: number; - private loggingInterval?: NodeJS.Timer; + private loggingInterval?: NodeJS.Timeout; getTotal() { return this.total; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.test.tsx new file mode 100644 index 0000000000000..74be43ab32253 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.test.tsx @@ -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 { setMockValues } from '../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { mount } from 'enzyme'; + +import { AppSearchProductCard } from './app_search_product_card'; +import { EnterpriseSearchProductCard } from './enterprise_search_product_card'; +import { WorkplaceSearchProductCard } from './workplace_search_product_card'; + +describe('EnterpriseSearchProductCard', () => { + beforeEach(() => { + setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } }); + }); + + it('renders both services with access', () => { + const wrapper = mount( + + ); + + expect(wrapper.find(AppSearchProductCard)).toHaveLength(1); + expect(wrapper.find(WorkplaceSearchProductCard)).toHaveLength(1); + }); + it('can render just app search', () => { + const wrapper = mount( + + ); + + expect(wrapper.find(AppSearchProductCard)).toHaveLength(1); + expect(wrapper.find(WorkplaceSearchProductCard)).toHaveLength(0); + }); + it('can render just workplace search', () => { + const wrapper = mount( + + ); + + expect(wrapper.find(AppSearchProductCard)).toHaveLength(0); + expect(wrapper.find(WorkplaceSearchProductCard)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.tsx index e332d4fad9401..d76940d1721b3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.tsx @@ -19,23 +19,45 @@ import { ProductCard } from '../product_card'; import { AppSearchProductCard } from './app_search_product_card'; import { WorkplaceSearchProductCard } from './workplace_search_product_card'; -export const EnterpriseSearchProductCard = () => ( - , - , - ]} - /> -); +export interface EnterpriseSearchProductCardProps { + hasAppSearchAccess: boolean; + hasWorkplaceSearchAccess: boolean; + isWorkplaceSearchAdmin: boolean; +} + +export const EnterpriseSearchProductCard = ({ + hasAppSearchAccess, + hasWorkplaceSearchAccess, + isWorkplaceSearchAdmin, +}: EnterpriseSearchProductCardProps) => { + const rightPanelItems: React.ReactNode[] = []; + if (hasAppSearchAccess) { + rightPanelItems.push(); + } + if (hasWorkplaceSearchAccess) { + rightPanelItems.push( + + ); + } + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.test.tsx index 8a37008c2d695..6d90c13f2b55d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.test.tsx @@ -21,10 +21,15 @@ import { EnterpriseSearchProductCard } from './enterprise_search_product_card'; import { ProductSelector } from '.'; +const props = { + access: { hasAppSearchAccess: true, hasWorkplaceSearchAccess: true }, + isWorkplaceSearchAdmin: true, +}; + describe('ProductSelector', () => { it('renders the overview page, product cards, & setup guide CTAs with no host set', () => { setMockValues({ config: { canDeployEntSearch: true, host: '' } }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(ElasticsearchProductCard)).toHaveLength(1); expect(wrapper.find(EnterpriseSearchProductCard)).toHaveLength(1); @@ -33,14 +38,14 @@ describe('ProductSelector', () => { it('renders the trial callout', () => { setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(TrialCallout)).toHaveLength(1); }); it('does not render connection error callout without an error', () => { setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(ErrorStateCallout)).toHaveLength(0); }); @@ -50,7 +55,7 @@ describe('ProductSelector', () => { config: { canDeployEntSearch: true, host: 'localhost' }, errorConnectingMessage: '502 Bad Gateway', }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(ErrorStateCallout)).toHaveLength(1); }); @@ -61,11 +66,37 @@ describe('ProductSelector', () => { }); it('does not render the Setup CTA when there is a host', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(ElasticsearchProductCard)).toHaveLength(1); expect(wrapper.find(EnterpriseSearchProductCard)).toHaveLength(1); expect(wrapper.find(SetupGuideCta)).toHaveLength(0); }); + + it('does not render EnterpriseSearch card without access', () => { + const wrapper = shallow(); + + expect(wrapper.find(ElasticsearchProductCard)).toHaveLength(1); + expect(wrapper.find(EnterpriseSearchProductCard)).toHaveLength(0); + expect(wrapper.find(SetupGuideCta)).toHaveLength(0); + }); + + it('does render EnterpriseSearch card with access to either service', () => { + const appSearchWrapper = shallow( + + ); + const workplaceSearchWrapper = shallow( + + ); + + expect(appSearchWrapper.find(EnterpriseSearchProductCard)).toHaveLength(1); + expect(workplaceSearchWrapper.find(EnterpriseSearchProductCard)).toHaveLength(1); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx index ccda6f5af54ab..13b84c9c6e40b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx @@ -40,7 +40,19 @@ import { IngestionSelector } from './ingestion_selector'; import './product_selector.scss'; -export const ProductSelector: React.FC = () => { +interface ProductSelectorProps { + access: { + hasAppSearchAccess?: boolean; + hasWorkplaceSearchAccess?: boolean; + }; + isWorkplaceSearchAdmin: boolean; +} + +export const ProductSelector: React.FC = ({ + access, + isWorkplaceSearchAdmin, +}) => { + const { hasAppSearchAccess, hasWorkplaceSearchAccess } = access; const { config } = useValues(KibanaLogic); const { errorConnectingMessage } = useValues(HttpLogic); const { security } = useValues(KibanaLogic); @@ -138,9 +150,15 @@ export const ProductSelector: React.FC = () => { - - - + {(hasAppSearchAccess || hasWorkplaceSearchAccess) && ( + + + + )} {!config.host && config.canDeployEntSearch && ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/workplace_search_product_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/workplace_search_product_card.test.tsx new file mode 100644 index 0000000000000..f3e5ab549dd9f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/workplace_search_product_card.test.tsx @@ -0,0 +1,34 @@ +/* + * 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 { shallow } from 'enzyme'; + +import { WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants'; +import { ProductCard } from '../product_card'; + +import { WorkplaceSearchProductCard } from './workplace_search_product_card'; + +describe('WorkplaceSearchProductCard', () => { + it('renders with url when admin', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(ProductCard)).toHaveLength(1); + expect(wrapper.find(ProductCard).prop('url')).toEqual(WORKPLACE_SEARCH_PLUGIN.URL); + }); + it('renders with non-admin url when not admin', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(ProductCard)).toHaveLength(1); + expect(wrapper.find(ProductCard).prop('url')).toEqual(WORKPLACE_SEARCH_PLUGIN.NON_ADMIN_URL); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/workplace_search_product_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/workplace_search_product_card.tsx index 6139d7a2f2b2c..94d3c9bd96bfc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/workplace_search_product_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/workplace_search_product_card.tsx @@ -15,11 +15,13 @@ import { ProductCard } from '../product_card'; export interface WorkplaceSearchProductCardProps { hasBorder: boolean; hasShadow: boolean; + isWorkplaceSearchAdmin: boolean; } export const WorkplaceSearchProductCard: React.FC = ({ hasBorder = true, hasShadow = true, + isWorkplaceSearchAdmin, }) => ( ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/index.tsx index 438b903bd8399..5b8e595d6e88e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/index.tsx @@ -21,6 +21,8 @@ import { SetupGuide } from './components/setup_guide'; import { ROOT_PATH, SETUP_GUIDE_PATH } from './routes'; export const EnterpriseSearchOverview: React.FC = ({ + access = {}, + workplaceSearch, enterpriseSearchVersion, kibanaVersion, }) => { @@ -29,6 +31,8 @@ export const EnterpriseSearchOverview: React.FC = ({ const incompatibleVersions = !!( config.host && isVersionMismatch(enterpriseSearchVersion, kibanaVersion) ); + const isWorkplaceSearchAdmin = !!workplaceSearch?.account?.isAdmin; + const showView = () => { if (incompatibleVersions) { return ( @@ -39,7 +43,7 @@ export const EnterpriseSearchOverview: React.FC = ({ ); } - return ; + return ; }; return ( 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 6dd47fe869ede..6974f956e2bc9 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 @@ -92,7 +92,6 @@ class DocLinks { public connectorsSlack: string; public connectorsTeams: string; public connectorsZoom: string; - public connectorsWorkplaceSearch: string; public consoleGuide: string; public crawlerExtractionRules: string; public crawlerManaging: string; @@ -260,7 +259,6 @@ class DocLinks { this.connectorsSlack = ''; this.connectorsTeams = ''; this.connectorsZoom = ''; - this.connectorsWorkplaceSearch = ''; this.consoleGuide = ''; this.crawlerExtractionRules = ''; this.crawlerManaging = ''; @@ -430,7 +428,6 @@ class DocLinks { this.connectorsSlack = docLinks.links.enterpriseSearch.connectorsSlack; this.connectorsTeams = docLinks.links.enterpriseSearch.connectorsTeams; this.connectorsZoom = docLinks.links.enterpriseSearch.connectorsZoom; - this.connectorsWorkplaceSearch = docLinks.links.enterpriseSearch.connectorsWorkplaceSearch; this.consoleGuide = docLinks.links.console.guide; this.crawlerExtractionRules = docLinks.links.enterpriseSearch.crawlerExtractionRules; this.crawlerManaging = docLinks.links.enterpriseSearch.crawlerManaging; diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts index deb8c0a313f30..984a88d87e61f 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts @@ -25,7 +25,7 @@ import { buildComponentTemplates, installComponentAndIndexTemplateForDataStream, } from '../template/install'; -import { processFields } from '../../fields/field'; +import { isFields, processFields } from '../../fields/field'; import { generateMappings } from '../template/template'; import { getESAssetMetadata } from '../meta'; import { updateEsAssetReferences } from '../../packages/install'; @@ -162,7 +162,6 @@ const processTransformAssetsPerModule = ( installablePackage, path ); - // Since there can be multiple assets per transform definition // We want to create a unique list of assets/specifications for each transform if (transformsSpecifications.get(transformModuleId) === undefined) { @@ -172,7 +171,8 @@ const processTransformAssetsPerModule = ( const content = safeLoad(getAsset(path).toString('utf-8')); - if (fileName === TRANSFORM_SPECS_TYPES.FIELDS) { + // Handling fields.yml and all other files within 'fields' folder + if (fileName === TRANSFORM_SPECS_TYPES.FIELDS || isFields(path)) { const validFields = processFields(content); const mappings = generateMappings(validFields); const templateName = getTransformAssetNameForInstallation( @@ -198,7 +198,14 @@ const processTransformAssetsPerModule = ( } else { destinationIndexTemplates[indexToModify] = template; } - packageAssets?.set('mappings', mappings); + + // If there's already mappings set previously, append it to new + const previousMappings = + transformsSpecifications.get(transformModuleId)?.get('mappings') ?? {}; + + transformsSpecifications.get(transformModuleId)?.set('mappings', { + properties: { ...previousMappings.properties, ...mappings.properties }, + }); } if (fileName === TRANSFORM_SPECS_TYPES.TRANSFORM) { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transforms.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transforms.test.ts index e0c5a123c2421..6ab6f20669baf 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transforms.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transforms.test.ts @@ -107,7 +107,34 @@ _meta: type: date - name: updated_at type: alias - path: event.ingested`, + path: event.ingested +- external: ecs + name: ecs.version +- external: ecs + name: message`, + BEATS_FIELDS: `- name: input.type + type: keyword + description: Type of Filebeat input. +- name: log.flags + type: keyword + description: Flags for the log file. +- name: log.offset + type: long + description: Offset of the entry in the log file. +- name: log.file.path + type: keyword + description: Path to the log file.`, + AGENT_FIELDS: `- name: instance.name + level: extended + type: keyword + ignore_above: 1024 + description: Instance name of the host machine. +- name: machine.type + level: extended + type: keyword + ignore_above: 1024 + description: Machine type of the host machine. + example: t2.medium`, }; }; const getExpectedData = (transformVersion: string) => { @@ -210,6 +237,8 @@ _meta: ], } as unknown as Installation; (getAsset as jest.MockedFunction) + .mockReturnValueOnce(Buffer.from(sourceData.BEATS_FIELDS, 'utf8')) + .mockReturnValueOnce(Buffer.from(sourceData.AGENT_FIELDS, 'utf8')) .mockReturnValueOnce(Buffer.from(sourceData.FIELDS, 'utf8')) .mockReturnValueOnce(Buffer.from(sourceData.MANIFEST, 'utf8')) .mockReturnValueOnce(Buffer.from(sourceData.TRANSFORM, 'utf8')); @@ -247,6 +276,8 @@ _meta: version: '0.16.0-dev.0', } as unknown as RegistryPackage, paths: [ + 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/fields/beats.yml', + 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/fields/agent.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/fields/fields.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/manifest.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/transform.yml', @@ -300,10 +331,19 @@ _meta: }, mappings: { properties: { - '@timestamp': { - ignore_malformed: false, - type: 'date', + input: { properties: { type: { type: 'keyword', ignore_above: 1024 } } }, + log: { + properties: { + flags: { type: 'keyword', ignore_above: 1024 }, + offset: { type: 'long' }, + file: { properties: { path: { type: 'keyword', ignore_above: 1024 } } }, + }, }, + instance: { properties: { name: { type: 'keyword', ignore_above: 1024 } } }, + machine: { properties: { type: { type: 'keyword', ignore_above: 1024 } } }, + '@timestamp': { ignore_malformed: false, type: 'date' }, + ecs: { properties: { version: { type: 'keyword', ignore_above: 1024 } } }, + message: { type: 'keyword', ignore_above: 1024 }, }, dynamic_templates: [ { @@ -582,6 +622,8 @@ _meta: ignore_malformed: false, type: 'date', }, + ecs: { properties: { version: { type: 'keyword', ignore_above: 1024 } } }, + message: { type: 'keyword', ignore_above: 1024 }, }, dynamic_templates: [ { @@ -852,6 +894,8 @@ _meta: ignore_malformed: false, type: 'date', }, + ecs: { properties: { version: { type: 'keyword', ignore_above: 1024 } } }, + message: { type: 'keyword', ignore_above: 1024 }, }, }, }, diff --git a/x-pack/plugins/fleet/server/services/epm/fields/field.ts b/x-pack/plugins/fleet/server/services/epm/fields/field.ts index 27e566d2e4339..437eed7c64192 100644 --- a/x-pack/plugins/fleet/server/services/epm/fields/field.ts +++ b/x-pack/plugins/fleet/server/services/epm/fields/field.ts @@ -284,7 +284,7 @@ export function processFields(fields: Fields): Fields { return validateFields(dedupedFields, dedupedFields); } -const isFields = (path: string) => { +export const isFields = (path: string) => { return path.includes('/fields/'); }; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts index b6656c0dae354..10e94bb2f718c 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts @@ -102,4 +102,5 @@ export type TestSubjects = | 'filter-option-h' | 'infiniteRetentionPeriod.input' | 'saveButton' + | 'dataRetentionDetail' | 'createIndexSaveButton'; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts index 6629502498c74..3a6add88c2840 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts @@ -278,6 +278,7 @@ export const createDataStreamPayload = (dataStream: Partial): DataSt }, hidden: false, lifecycle: { + enabled: true, data_retention: '7d', }, ...dataStream, diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts index c40823509d640..bbb8d924fcd02 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts @@ -338,6 +338,55 @@ describe('Data Streams tab', () => { }); describe('update data retention', () => { + test('Should show disabled or infinite retention period accordingly in table and flyout', async () => { + const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers; + + const ds1 = createDataStreamPayload({ + name: 'dataStream1', + lifecycle: { + enabled: false, + }, + }); + const ds2 = createDataStreamPayload({ + name: 'dataStream2', + lifecycle: { + enabled: true, + }, + }); + + setLoadDataStreamsResponse([ds1, ds2]); + setLoadDataStreamResponse(ds1.name, ds1); + + testBed = await setup(httpSetup, { + history: createMemoryHistory(), + url: urlServiceMock, + }); + await act(async () => { + testBed.actions.goToDataStreamsList(); + }); + testBed.component.update(); + + const { actions, find, table } = testBed; + const { tableCellsValues } = table.getMetaData('dataStreamTable'); + + expect(tableCellsValues).toEqual([ + ['', 'dataStream1', 'green', '1', 'Disabled', 'Delete'], + ['', 'dataStream2', 'green', '1', '', 'Delete'], + ]); + + await actions.clickNameAt(0); + expect(find('dataRetentionDetail').text()).toBe('Disabled'); + + await act(async () => { + testBed.find('closeDetailsButton').simulate('click'); + }); + testBed.component.update(); + + setLoadDataStreamResponse(ds2.name, ds2); + await actions.clickNameAt(1); + expect(find('dataRetentionDetail').text()).toBe('Keep data indefinitely'); + }); + test('can set data retention period', async () => { const { actions: { clickNameAt, clickEditDataRetentionButton }, diff --git a/x-pack/plugins/index_management/common/types/data_streams.ts b/x-pack/plugins/index_management/common/types/data_streams.ts index 8e2e5c3368ac3..f0bd12d96fde5 100644 --- a/x-pack/plugins/index_management/common/types/data_streams.ts +++ b/x-pack/plugins/index_management/common/types/data_streams.ts @@ -59,7 +59,9 @@ export interface DataStream { _meta?: Metadata; privileges: Privileges; hidden: boolean; - lifecycle?: IndicesDataLifecycleWithRollover; + lifecycle?: IndicesDataLifecycleWithRollover & { + enabled?: boolean; + }; } export interface DataStreamIndex { diff --git a/x-pack/plugins/index_management/public/application/lib/data_streams.test.tsx b/x-pack/plugins/index_management/public/application/lib/data_streams.test.tsx new file mode 100644 index 0000000000000..a15cbaefbd0fa --- /dev/null +++ b/x-pack/plugins/index_management/public/application/lib/data_streams.test.tsx @@ -0,0 +1,38 @@ +/* + * 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 { getLifecycleValue } from './data_streams'; + +describe('Data stream helpers', () => { + describe('getLifecycleValue', () => { + it('Knows when it should be marked as disabled', () => { + expect( + getLifecycleValue({ + enabled: false, + }) + ).toBe('Disabled'); + + expect(getLifecycleValue()).toBe('Disabled'); + }); + + it('knows when it should be marked as infinite', () => { + expect( + getLifecycleValue({ + enabled: true, + }) + ).toBe('Keep data indefinitely'); + }); + + it('knows when it has a defined data retention period', () => { + expect( + getLifecycleValue({ + enabled: true, + data_retention: '2d', + }) + ).toBe('2d'); + }); + }); +}); diff --git a/x-pack/plugins/index_management/public/application/lib/data_streams.tsx b/x-pack/plugins/index_management/public/application/lib/data_streams.tsx index 8cf45050855b0..ad068dde91a22 100644 --- a/x-pack/plugins/index_management/public/application/lib/data_streams.tsx +++ b/x-pack/plugins/index_management/public/application/lib/data_streams.tsx @@ -5,6 +5,10 @@ * 2.0. */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiIcon, EuiToolTip } from '@elastic/eui'; + import { DataStream } from '../../../common'; export const isFleetManaged = (dataStream: DataStream): boolean => { @@ -38,3 +42,37 @@ export const isSelectedDataStreamHidden = ( ?.hidden ); }; + +export const getLifecycleValue = ( + lifecycle?: DataStream['lifecycle'], + inifniteAsIcon?: boolean +) => { + if (!lifecycle?.enabled) { + return i18n.translate('xpack.idxMgmt.dataStreamList.dataRetentionDisabled', { + defaultMessage: 'Disabled', + }); + } else if (!lifecycle?.data_retention) { + const infiniteDataRetention = i18n.translate( + 'xpack.idxMgmt.dataStreamList.dataRetentionInfinite', + { + defaultMessage: 'Keep data indefinitely', + } + ); + + if (inifniteAsIcon) { + return ( + + + + ); + } + + return infiniteDataRetention; + } + + return lifecycle?.data_retention; +}; diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx index d04976d157406..96449e6de5238 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx @@ -30,6 +30,7 @@ import { } from '@elastic/eui'; import { DiscoverLink } from '../../../../lib/discover_link'; +import { getLifecycleValue } from '../../../../lib/data_streams'; import { SectionLoading, reactRouterNavigate } from '../../../../../shared_imports'; import { SectionError, Error, DataHealth } from '../../../../components'; import { useLoadDataStream } from '../../../../services/api'; @@ -147,19 +148,6 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ const getManagementDetails = () => { const managementDetails = []; - if (lifecycle?.data_retention) { - managementDetails.push({ - name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.dataRetentionTitle', { - defaultMessage: 'Data retention', - }), - toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.dataRetentionToolTip', { - defaultMessage: 'The amount of time to retain the data in the data stream.', - }), - content: lifecycle.data_retention, - dataTestSubj: 'dataRetentionDetail', - }); - } - if (ilmPolicyName) { managementDetails.push({ name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyTitle', { @@ -278,6 +266,16 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ ), dataTestSubj: 'indexTemplateDetail', }, + { + name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.dataRetentionTitle', { + defaultMessage: 'Data retention', + }), + toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.dataRetentionToolTip', { + defaultMessage: 'The amount of time to retain the data in the data stream.', + }), + content: getLifecycleValue(lifecycle), + dataTestSubj: 'dataRetentionDetail', + }, ]; const managementDetails = getManagementDetails(); @@ -376,7 +374,7 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ } }} dataStreamName={dataStreamName} - dataRetention={dataStream?.lifecycle?.data_retention as string} + lifecycle={dataStream?.lifecycle} /> )} diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx index 485f1db9c06c9..0bf6aa68347de 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx @@ -19,6 +19,7 @@ import { import { ScopedHistory } from '@kbn/core/public'; import { DataStream } from '../../../../../../common/types'; +import { getLifecycleValue } from '../../../../lib/data_streams'; import { UseRequestResponse, reactRouterNavigate } from '../../../../../shared_imports'; import { getDataStreamDetailsLink, getIndexListUri } from '../../../../services/routing'; import { DataHealth } from '../../../../components'; @@ -34,6 +35,8 @@ interface Props { filters?: string; } +const INFINITE_AS_ICON = true; + export const DataStreamTable: React.FunctionComponent = ({ dataStreams, reload, @@ -144,7 +147,7 @@ export const DataStreamTable: React.FunctionComponent = ({ ), truncateText: true, sortable: true, - render: (lifecycle: DataStream['lifecycle']) => lifecycle?.data_retention, + render: (lifecycle: DataStream['lifecycle']) => getLifecycleValue(lifecycle, INFINITE_AS_ICON), }); columns.push({ diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx index e33aaae7a9073..11e51a83b8e99 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx @@ -35,13 +35,13 @@ import { } from '../../../../../shared_imports'; import { documentationService } from '../../../../services/documentation'; -import { splitSizeAndUnits } from '../../../../../../common'; +import { splitSizeAndUnits, DataStream } from '../../../../../../common'; import { useAppContext } from '../../../../app_context'; import { UnitField } from './unit_field'; import { updateDataRetention } from '../../../../services/api'; interface Props { - dataRetention: string; + lifecycle: DataStream['lifecycle']; dataStreamName: string; onClose: (data?: { hasUpdatedDataRetention: boolean }) => void; } @@ -83,33 +83,6 @@ export const timeUnits = [ } ), }, - { - value: 'ms', - text: i18n.translate( - 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.timeUnits.millisecondsLabel', - { - defaultMessage: 'milliseconds', - } - ), - }, - { - value: 'micros', - text: i18n.translate( - 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.timeUnits.microsecondsLabel', - { - defaultMessage: 'microseconds', - } - ), - }, - { - value: 'nanos', - text: i18n.translate( - 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.timeUnits.nanosecondsLabel', - { - defaultMessage: 'nanoseconds', - } - ), - }, ]; const configurationFormSchema: FormSchema = { @@ -124,7 +97,12 @@ const configurationFormSchema: FormSchema = { formatters: [fieldFormatters.toInt], validations: [ { - validator: ({ value }) => { + validator: ({ value, formData }) => { + // If infiniteRetentionPeriod is set, we dont need to validate the data retention field + if (formData.infiniteRetentionPeriod) { + return undefined; + } + if (!value) { return { message: i18n.translate( @@ -171,11 +149,11 @@ const configurationFormSchema: FormSchema = { }; export const EditDataRetentionModal: React.FunctionComponent = ({ - dataRetention, + lifecycle, dataStreamName, onClose, }) => { - const { size, unit } = splitSizeAndUnits(dataRetention); + const { size, unit } = splitSizeAndUnits(lifecycle?.data_retention as string); const { services: { notificationService }, } = useAppContext(); @@ -184,7 +162,10 @@ export const EditDataRetentionModal: React.FunctionComponent = ({ defaultValue: { dataRetention: size, timeUnit: unit || 'd', - infiniteRetentionPeriod: !dataRetention, + // When data retention is not set and lifecycle is enabled, is the only scenario in + // which data retention will be infinite. If lifecycle isnt set or is not enabled, we + // dont have inifinite data retention. + infiniteRetentionPeriod: lifecycle?.enabled && !lifecycle?.data_retention, }, schema: configurationFormSchema, id: 'editDataRetentionForm', diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/bulk_uploader.ts b/x-pack/plugins/monitoring/server/kibana_monitoring/bulk_uploader.ts index aca7c23f00efc..5228eb488b1f7 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/bulk_uploader.ts +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/bulk_uploader.ts @@ -67,7 +67,7 @@ export class BulkUploader implements IBulkUploader { private kibanaStatusSubscription?: Subscription; private readonly opsMetrics$: Observable; private kibanaStatus: ServiceStatusLevel | null; - private _timer: NodeJS.Timer | null; + private _timer: NodeJS.Timeout | null; private readonly _interval: number; private readonly config: MonitoringConfig; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts index 4a49d85d0ffa9..6b8f314ba8d58 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts @@ -41,172 +41,176 @@ interface ITestRuleBody { } ]; } +// flaky +describe.skip( + 'Alert Event Details - Response Actions Form', + { tags: ['@ess', '@serverless'] }, + () => { + let multiQueryPackId: string; + let multiQueryPackName: string; + let ruleId: string; + let ruleName: string; + let packId: string; + let packName: string; + const packData = packFixture(); + const multiQueryPackData = multiQueryPackFixture(); -describe('Alert Event Details - Response Actions Form', { tags: ['@ess', '@serverless'] }, () => { - let multiQueryPackId: string; - let multiQueryPackName: string; - let ruleId: string; - let ruleName: string; - let packId: string; - let packName: string; - const packData = packFixture(); - const multiQueryPackData = multiQueryPackFixture(); - - beforeEach(() => { - loadPack(packData).then((data) => { - packId = data.saved_object_id; - packName = data.name; - }); - loadPack(multiQueryPackData).then((data) => { - multiQueryPackId = data.saved_object_id; - multiQueryPackName = data.name; + beforeEach(() => { + loadPack(packData).then((data) => { + packId = data.saved_object_id; + packName = data.name; + }); + loadPack(multiQueryPackData).then((data) => { + multiQueryPackId = data.saved_object_id; + multiQueryPackName = data.name; + }); + loadRule().then((data) => { + ruleId = data.id; + ruleName = data.name; + }); }); - loadRule().then((data) => { - ruleId = data.id; - ruleName = data.name; + afterEach(() => { + cleanupPack(packId); + cleanupPack(multiQueryPackId); + cleanupRule(ruleId); }); - }); - afterEach(() => { - cleanupPack(packId); - cleanupPack(multiQueryPackId); - cleanupRule(ruleId); - }); - it('adds response actions with osquery with proper validation and form values', () => { - cy.visit('/app/security/rules'); - clickRuleName(ruleName); - cy.getBySel('editRuleSettingsLink').click(); - cy.getBySel('globalLoadingIndicator').should('not.exist'); - closeDateTabIfVisible(); - cy.getBySel('edit-rule-actions-tab').click(); - cy.contains('Response actions are run on each rule execution.'); - cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains('Query is a required field'); - inputQuery('select * from uptime1'); - }); - cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('Run a set of queries in a pack').click(); - }); - cy.getBySel('response-actions-error') - .within(() => { + it('adds response actions with osquery with proper validation and form values', () => { + cy.visit('/app/security/rules'); + clickRuleName(ruleName); + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + closeDateTabIfVisible(); + cy.getBySel('edit-rule-actions-tab').click(); + cy.contains('Response actions are run on each rule execution.'); + cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('Query is a required field'); + inputQuery('select * from uptime1'); + }); + cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains('Run a set of queries in a pack').click(); + }); + cy.getBySel('response-actions-error') + .within(() => { + cy.contains('Pack is a required field'); + }) + .should('exist'); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { cy.contains('Pack is a required field'); - }) - .should('exist'); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('Pack is a required field'); - cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); - }); + cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); + }); - cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); + cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_2).within(() => { - cy.contains('Query is a required field'); - inputQuery('select * from uptime'); - cy.contains('Advanced').click(); - typeInECSFieldInput('message{downArrow}{enter}'); - cy.getBySel('osqueryColumnValueSelect').type('days{downArrow}{enter}'); - cy.wait(1000); // wait for the validation to trigger - cypress is way faster than users ;) - }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_2).within(() => { + cy.contains('Query is a required field'); + inputQuery('select * from uptime'); + cy.contains('Advanced').click(); + typeInECSFieldInput('message{downArrow}{enter}'); + cy.getBySel('osqueryColumnValueSelect').type('days{downArrow}{enter}'); + cy.wait(1000); // wait for the validation to trigger - cypress is way faster than users ;) + }); - cy.getBySel('ruleEditSubmitButton').click(); - cy.contains(`${ruleName} was saved`).should('exist'); - closeToastIfVisible(); + cy.getBySel('ruleEditSubmitButton').click(); + cy.contains(`${ruleName} was saved`).should('exist'); + closeToastIfVisible(); - cy.getBySel('editRuleSettingsLink').click(); - cy.getBySel('globalLoadingIndicator').should('not.exist'); - cy.getBySel('edit-rule-actions-tab').click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains('select * from uptime1'); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_2).within(() => { - cy.contains('select * from uptime'); - cy.contains('Log message optimized for viewing in a log viewer'); - cy.contains('Days of uptime'); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains(packName); - cy.getBySel('comboBoxInput').type('{backspace}{enter}'); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains('select * from uptime1'); - cy.getBySel('remove-response-action').click(); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains('Search for a pack to run'); - cy.contains('Pack is a required field'); - cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('select * from uptime'); - cy.contains('Log message optimized for viewing in a log viewer'); - cy.contains('Days of uptime'); - }); - cy.intercept('PUT', '/api/detection_engine/rules').as('saveRuleChangesOne'); - cy.getBySel('ruleEditSubmitButton').click(); + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('select * from uptime1'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_2).within(() => { + cy.contains('select * from uptime'); + cy.contains('Log message optimized for viewing in a log viewer'); + cy.contains('Days of uptime'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains(packName); + cy.getBySel('comboBoxInput').type('{backspace}{enter}'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('select * from uptime1'); + cy.getBySel('remove-response-action').click(); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('Search for a pack to run'); + cy.contains('Pack is a required field'); + cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains('select * from uptime'); + cy.contains('Log message optimized for viewing in a log viewer'); + cy.contains('Days of uptime'); + }); + cy.intercept('PUT', '/api/detection_engine/rules').as('saveRuleChangesOne'); + cy.getBySel('ruleEditSubmitButton').click(); - cy.wait('@saveRuleChangesOne'); - cy.get<{ request: { url: string; body: ITestRuleBody } }>('@saveRuleChangesOne').should( - ({ request }) => { - const oneQuery = [ - { - interval: 3600, - query: 'select * from uptime;', - id: Object.keys(packData.queries)[0], - }, - ]; - expect(request.body.response_actions[0].params.queries).to.deep.equal(oneQuery); - } - ); + cy.wait('@saveRuleChangesOne'); + cy.get<{ request: { url: string; body: ITestRuleBody } }>('@saveRuleChangesOne').should( + ({ request }) => { + const oneQuery = [ + { + interval: 3600, + query: 'select * from uptime;', + id: Object.keys(packData.queries)[0], + }, + ]; + expect(request.body.response_actions[0].params.queries).to.deep.equal(oneQuery); + } + ); - cy.contains(`${ruleName} was saved`).should('exist'); - closeToastIfVisible(); + cy.contains(`${ruleName} was saved`).should('exist'); + closeToastIfVisible(); - cy.getBySel('editRuleSettingsLink').click(); - cy.getBySel('globalLoadingIndicator').should('not.exist'); - cy.getBySel('edit-rule-actions-tab').click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains(packName); - cy.getBySel('comboBoxInput').type(`${multiQueryPackName}{downArrow}{enter}`); - checkActionItemsInResults({ - cases: false, - lens: false, - discover: false, - timeline: false, + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains(packName); + cy.getBySel('comboBoxInput').type(`${multiQueryPackName}{downArrow}{enter}`); + checkActionItemsInResults({ + cases: false, + lens: false, + discover: false, + timeline: false, + }); }); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('select * from uptime'); - cy.contains('Log message optimized for viewing in a log viewer'); - cy.contains('Days of uptime'); - }); - cy.intercept('PUT', '/api/detection_engine/rules').as('saveRuleChangesTwo'); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains('select * from uptime'); + cy.contains('Log message optimized for viewing in a log viewer'); + cy.contains('Days of uptime'); + }); + cy.intercept('PUT', '/api/detection_engine/rules').as('saveRuleChangesTwo'); - cy.contains('Save changes').click(); - cy.wait('@saveRuleChangesTwo'); - cy.get<{ request: { url: string; body: ITestRuleBody } }>('@saveRuleChangesTwo').should( - ({ request }) => { - const threeQueries = [ - { - interval: 3600, - query: 'SELECT * FROM memory_info;', - platform: 'linux', - id: Object.keys(multiQueryPackData.queries)[0], - }, - { - interval: 3600, - query: 'SELECT * FROM system_info;', - id: Object.keys(multiQueryPackData.queries)[1], - }, - { - interval: 10, - query: 'select opera_extensions.* from users join opera_extensions using (uid);', - id: Object.keys(multiQueryPackData.queries)[2], - }, - ]; - expect(request.body.response_actions[0].params.queries).to.deep.equal(threeQueries); - } - ); - }); -}); + cy.contains('Save changes').click(); + cy.wait('@saveRuleChangesTwo'); + cy.get<{ request: { url: string; body: ITestRuleBody } }>('@saveRuleChangesTwo').should( + ({ request }) => { + const threeQueries = [ + { + interval: 3600, + query: 'SELECT * FROM memory_info;', + platform: 'linux', + id: Object.keys(multiQueryPackData.queries)[0], + }, + { + interval: 3600, + query: 'SELECT * FROM system_info;', + id: Object.keys(multiQueryPackData.queries)[1], + }, + { + interval: 10, + query: 'select opera_extensions.* from users join opera_extensions using (uid);', + id: Object.keys(multiQueryPackData.queries)[2], + }, + ]; + expect(request.body.response_actions[0].params.queries).to.deep.equal(threeQueries); + } + ); + }); + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_tick_tick.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_tick_tick.ts index 14b3aba461c52..0a11b572ea9c3 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_tick_tick.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_tick_tick.ts @@ -10,7 +10,7 @@ import { useEffect, useState, useCallback } from 'react'; export function useTickTick(interval?: number) { const [nextTick, setNextTick] = useState(Date.now()); - const [tickTick] = useState(() => + const [tickTick] = useState(() => setInterval(() => { setNextTick(Date.now()); }, interval ?? 5 * 1000) diff --git a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts index a76df2127a1fb..e0373a405cde7 100644 --- a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts +++ b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts @@ -31,6 +31,10 @@ export default function ({ getService }: FtrProviderContext) { }, }, }, + lifecycle: { + // @ts-expect-error @elastic/elasticsearch enabled prop is not typed yet + enabled: true, + }, }, data_stream: {}, }, @@ -85,8 +89,7 @@ export default function ({ getService }: FtrProviderContext) { expect(typeof storageSizeBytes).to.be('number'); }; - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/168021 - describe.skip('Data streams', function () { + describe('Data streams', function () { describe('Get', () => { const testDataStreamName = 'test-data-stream'; diff --git a/x-pack/test/functional/apps/index_management/data_streams_tab/data_streams_tab.ts b/x-pack/test/functional/apps/index_management/data_streams_tab/data_streams_tab.ts index c801c40c7e067..eea731575f8f3 100644 --- a/x-pack/test/functional/apps/index_management/data_streams_tab/data_streams_tab.ts +++ b/x-pack/test/functional/apps/index_management/data_streams_tab/data_streams_tab.ts @@ -12,8 +12,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'indexManagement', 'header']); const toasts = getService('toasts'); const log = getService('log'); - const dataStreams = getService('dataStreams'); const browser = getService('browser'); + const es = getService('es'); const security = getService('security'); const testSubjects = getService('testSubjects'); @@ -23,15 +23,32 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { before(async () => { await log.debug('Creating required data stream'); try { - await dataStreams.createDataStream( - TEST_DS_NAME, - { - '@timestamp': { - type: 'date', + await es.indices.putIndexTemplate({ + name: `${TEST_DS_NAME}_index_template`, + index_patterns: [TEST_DS_NAME], + data_stream: {}, + _meta: { + description: `Template for ${TEST_DS_NAME} testing index`, + }, + template: { + settings: { mode: undefined }, + mappings: { + properties: { + '@timestamp': { + type: 'date', + }, + }, + }, + lifecycle: { + // @ts-expect-error @elastic/elasticsearch enabled prop is not typed yet + enabled: true, }, }, - false - ); + }); + + await es.indices.createDataStream({ + name: TEST_DS_NAME, + }); } catch (e) { log.debug('[Setup error] Error creating test data stream'); throw e; @@ -49,7 +66,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await log.debug('Cleaning up created data stream'); try { - await dataStreams.deleteDataStream(TEST_DS_NAME); + await es.indices.deleteDataStream({ name: TEST_DS_NAME }); + await es.indices.deleteIndexTemplate({ + name: `${TEST_DS_NAME}_index_template`, + }); } catch (e) { log.debug('[Teardown error] Error deleting test data stream'); throw e; diff --git a/yarn.lock b/yarn.lock index 1ea5b0185a8bf..6ab021f44c6ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9464,10 +9464,10 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@18.17.5", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.9.0", "@types/node@^10.1.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^18.11.18", "@types/node@^18.17.5": - version "18.17.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.5.tgz#c58b12bca8c2a437b38c15270615627e96dd0bc5" - integrity sha512-xNbS75FxH6P4UXTPUJp/zNPq6/xsfdJKussCWNOnz4aULWIRwMgP1LgaB5RiBnMX1DPCYenuqGZfnIAx5mbFLA== +"@types/node@*", "@types/node@18.18.5", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.9.0", "@types/node@^10.1.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^18.11.18", "@types/node@^18.17.5": + version "18.18.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.5.tgz#afc0fd975df946d6e1add5bbf98264225b212244" + integrity sha512-4slmbtwV59ZxitY4ixUZdy1uRLf9eSIvBWPQxNjhHYWEtn0FryfKpyS2cvADYXTayWdKEIsJengncrVvkI4I6A== "@types/nodemailer@^6.4.0": version "6.4.0"