diff --git a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml index 48d0b61d7902a..7538da0cfbd40 100644 --- a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml +++ b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml @@ -63,7 +63,7 @@ steps: queue: n2-4-spot depends_on: build timeout_in_minutes: 60 - parallelism: 2 + parallelism: 4 retry: automatic: - exit_status: '*' diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index 25b22ff8e6a95..815e4d9adb5e2 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -85,7 +85,7 @@ steps: queue: n2-4-spot depends_on: build timeout_in_minutes: 60 - parallelism: 2 + parallelism: 4 retry: automatic: - exit_status: '*' diff --git a/.buildkite/pipelines/quality-gates/pipeline.tests-qa.yaml b/.buildkite/pipelines/quality-gates/pipeline.tests-qa.yaml index e0a892326eb18..29c7ad4ee8491 100644 --- a/.buildkite/pipelines/quality-gates/pipeline.tests-qa.yaml +++ b/.buildkite/pipelines/quality-gates/pipeline.tests-qa.yaml @@ -12,14 +12,15 @@ steps: EC_ENV: qa EC_REGION: aws-eu-west-1 message: "${BUILDKITE_MESSAGE} (triggered by pipeline.tests-qa.yaml)" - - - label: ":pipeline::female-detective::seedling: Trigger Security Solution quality gate script" - trigger: security-serverless-quality-gate # https://buildkite.com/elastic/security-serverless-quality-gate - soft_fail: true # Remove this when tests are fixed - build: - env: - ENVIRONMENT: ${ENVIRONMENT} - message: "${BUILDKITE_MESSAGE} (triggered by pipeline.tests-qa.yaml)" + + # TODO: Uncomment this code when the integration is ready. + # - label: ":pipeline::female-detective::seedling: Trigger Security Solution quality gate script" + # trigger: security-serverless-quality-gate # https://buildkite.com/elastic/security-serverless-quality-gate + # soft_fail: true # Remove this when tests are fixed + # build: + # env: + # ENVIRONMENT: ${ENVIRONMENT} + # message: "${BUILDKITE_MESSAGE} (triggered by pipeline.tests-qa.yaml)" - label: ":pipeline::ship::seedling: Trigger Fleet serverless smoke tests for ${ENVIRONMENT}" trigger: fleet-smoke-tests # https://buildkite.com/elastic/fleet-smoke-tests diff --git a/.eslintrc.js b/.eslintrc.js index 7b18904722c26..67cb263bcf493 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -924,6 +924,8 @@ module.exports = { ], rules: { '@kbn/telemetry/event_generating_elements_should_be_instrumented': 'error', + '@kbn/i18n/strings_should_be_translated_with_i18n': 'warn', + '@kbn/i18n/strings_should_be_translated_with_formatted_message': 'warn', }, }, { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ccb65e643d219..044ce6273292c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -360,6 +360,7 @@ src/plugins/es_ui_shared @elastic/platform-deployment-management packages/kbn-eslint-config @elastic/kibana-operations packages/kbn-eslint-plugin-disable @elastic/kibana-operations packages/kbn-eslint-plugin-eslint @elastic/kibana-operations +packages/kbn-eslint-plugin-i18n @elastic/actionable-observability packages/kbn-eslint-plugin-imports @elastic/kibana-operations packages/kbn-eslint-plugin-telemetry @elastic/actionable-observability x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin @elastic/kibana-security diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index cd180cb3829fa..ed011cd90063a 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -27,10 +27,21 @@ xpack.apm.serverlessOnboarding: true # Fleet specific configuration xpack.fleet.internal.registry.capabilities: ['apm', 'observability'] +xpack.fleet.internal.registry.kibanaVersionCheckEnabled: false xpack.fleet.internal.registry.spec.max: '3.0' -# Disabled until packages implement the new spec https://github.com/elastic/kibana/issues/166742 -# xpack.fleet.internal.registry.kibanaVersionCheckEnabled: false -# xpack.fleet.internal.registry.spec.min: '3.0' +# Temporary until all packages implement new spec https://github.com/elastic/kibana/issues/166742 +xpack.fleet.internal.registry.spec.min: '1.0' +xpack.fleet.internal.registry.excludePackages: [ + # Security integrations + 'endpoint', + 'beaconing', + 'osquery_manager', + # Removed in 8.11 integrations + 'cisco', + 'microsoft', + 'symantec', + 'cyberark', + ] ## Required for force installation of APM Package xpack.fleet.packages: diff --git a/config/serverless.security.yml b/config/serverless.security.yml index 1d3e83c76202a..d5edac4507c5d 100644 --- a/config/serverless.security.yml +++ b/config/serverless.security.yml @@ -37,9 +37,19 @@ telemetry.labels.serverless: security # Fleet specific configuration xpack.fleet.internal.registry.capabilities: ['security'] xpack.fleet.internal.registry.spec.max: '3.0' -# Disabled until packages implement the new spec https://github.com/elastic/kibana/issues/166742 -# xpack.fleet.internal.registry.kibanaVersionCheckEnabled: false -# xpack.fleet.internal.registry.spec.min: '3.0' +xpack.fleet.internal.registry.kibanaVersionCheckEnabled: false +# Temporary until all packages implement new spec https://github.com/elastic/kibana/issues/166742 +xpack.fleet.internal.registry.spec.min: '1.0' +xpack.fleet.internal.registry.excludePackages: [ + # Oblt integrations + 'apm', + 'synthetics', + # Removed in 8.11 integrations + 'cisco', + 'microsoft', + 'symantec', + 'cyberark', + ] xpack.ml.ad.enabled: true xpack.ml.dfa.enabled: true 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/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/cases/add-connectors.asciidoc b/docs/management/cases/add-connectors.asciidoc index c193a42b8fb29..e5fc4e3791082 100644 --- a/docs/management/cases/add-connectors.asciidoc +++ b/docs/management/cases/add-connectors.asciidoc @@ -1,7 +1,10 @@ [[add-case-connectors]] == Add connectors -preview::[] +:frontmatter-description: Configure connectors to push case details to external incident management systems. +:frontmatter-tags-products: [kibana] +:frontmatter-tags-content-type: [how-to] +:frontmatter-tags-user-goals: [configure] You can add connectors to cases to push information to these external incident management systems: @@ -24,7 +27,7 @@ You can create connectors in *{stack-manage-app} > {connectors-ui}*, as described in <>. Alternatively, you can create them in *{stack-manage-app} > Cases*: -. Click *Edit external connection*. +. Click *Settings*. + -- [role="screenshot"] @@ -50,7 +53,7 @@ configuration details. You can create additional connectors, update existing connectors, change the default connector, and change case closure options. -. Go to *{stack-manage-app} > Cases*, click *Edit external connection*. +. Go to *{stack-manage-app} > Cases* and click *Settings*. . To change whether cases are automatically closed after they are sent to an external system, update the case closure options. diff --git a/docs/management/cases/cases.asciidoc b/docs/management/cases/cases.asciidoc index 7d32ddc527930..02d5b5f859a5f 100644 --- a/docs/management/cases/cases.asciidoc +++ b/docs/management/cases/cases.asciidoc @@ -1,7 +1,9 @@ [[cases]] == Cases - -preview::[] +:frontmatter-description: Open and track issues in {kib} cases. +:frontmatter-tags-products: [kibana] +:frontmatter-tags-content-type: [overview] +:frontmatter-tags-user-goals: [analyze] Cases are used to open and track issues directly in {kib}. You can add assignees and tags to your cases, set their severity and status, and add alerts, diff --git a/docs/management/cases/images/cases-connectors.png b/docs/management/cases/images/cases-connectors.png index deec94f09871a..be7cc6aee4994 100644 Binary files a/docs/management/cases/images/cases-connectors.png and b/docs/management/cases/images/cases-connectors.png differ diff --git a/docs/management/cases/images/cases-files.png b/docs/management/cases/images/cases-files.png index 5b4c613593e66..03636626911ad 100644 Binary files a/docs/management/cases/images/cases-files.png and b/docs/management/cases/images/cases-files.png differ diff --git a/docs/management/cases/images/cases-visualization.png b/docs/management/cases/images/cases-visualization.png index 4f401857027cb..59567f67683c6 100644 Binary files a/docs/management/cases/images/cases-visualization.png and b/docs/management/cases/images/cases-visualization.png differ diff --git a/docs/management/cases/images/cases.png b/docs/management/cases/images/cases.png index ab9f01b0e3cd0..b85ab9165c961 100644 Binary files a/docs/management/cases/images/cases.png and b/docs/management/cases/images/cases.png differ diff --git a/docs/management/cases/manage-cases.asciidoc b/docs/management/cases/manage-cases.asciidoc index 1ebe9643d55c2..bccfd315ca445 100644 --- a/docs/management/cases/manage-cases.asciidoc +++ b/docs/management/cases/manage-cases.asciidoc @@ -6,8 +6,6 @@ :frontmatter-tags-content-type: [how-to] :frontmatter-tags-user-goals: [analyze] -preview::[] - [[open-case]] === Open a new case diff --git a/docs/management/cases/setup-cases.asciidoc b/docs/management/cases/setup-cases.asciidoc index cf3dad4318c5f..5e04d3a136605 100644 --- a/docs/management/cases/setup-cases.asciidoc +++ b/docs/management/cases/setup-cases.asciidoc @@ -1,7 +1,10 @@ [[setup-cases]] == Configure access to cases -preview::[] +:frontmatter-description: Learn about the {kib} feature privileges required to access cases. +:frontmatter-tags-products: [kibana] +:frontmatter-tags-content-type: [how-to] +:frontmatter-tags-user-goals: [configure] To access cases in *{stack-manage-app}*, you must have the appropriate {kib} privileges: 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/docs/settings/fleet-settings.asciidoc b/docs/settings/fleet-settings.asciidoc index 0960937564b7d..a222e4e5af578 100644 --- a/docs/settings/fleet-settings.asciidoc +++ b/docs/settings/fleet-settings.asciidoc @@ -95,6 +95,8 @@ List of agent policies that are configured when the {fleet} app starts. String identifying this policy's namespace. `monitoring_enabled`::: List of keywords that specify the monitoring data to collect. Valid values include `['logs']`, `['metrics']`, and `['logs', 'metrics']`. + `keep_monitoring_alive`::: + If `true`, monitoring will be enabled, but logs/metrics collection will be disabled. Use this if you want to keep agent's monitoring server alive even when logs/metrics aren't being collected. `is_managed`::: If `true`, this policy is not editable by the user and can only be changed by updating the {kib} config. `is_default`::: diff --git a/package.json b/package.json index fb0d2494e9842..5718ec17493f3 100644 --- a/package.json +++ b/package.json @@ -1196,6 +1196,7 @@ "@kbn/eslint-config": "link:packages/kbn-eslint-config", "@kbn/eslint-plugin-disable": "link:packages/kbn-eslint-plugin-disable", "@kbn/eslint-plugin-eslint": "link:packages/kbn-eslint-plugin-eslint", + "@kbn/eslint-plugin-i18n": "link:packages/kbn-eslint-plugin-i18n", "@kbn/eslint-plugin-imports": "link:packages/kbn-eslint-plugin-imports", "@kbn/eslint-plugin-telemetry": "link:packages/kbn-eslint-plugin-telemetry", "@kbn/expect": "link:packages/kbn-expect", diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/collect_multi_namespace_references.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/collect_multi_namespace_references.test.ts index 5450f0c739ce3..a82c18d42876b 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/collect_multi_namespace_references.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/collect_multi_namespace_references.test.ts @@ -411,11 +411,12 @@ describe('collectMultiNamespaceReferences', () => { expect(mockFindSharedOriginObjects).toHaveBeenCalledWith( expect.anything(), [ - { type: obj1.type, origin: obj1.id }, - { type: obj2.type, origin: obj2.originId }, // If the found object has an `originId`, that is used instead of the object's `id`. - { type: obj3.type, origin: obj3.id }, + { type: obj1.type, id: obj1.id }, + { type: obj2.type, id: obj2.id, origin: obj2.originId }, + { type: obj3.type, id: obj3.id }, ], - ALIAS_OR_SHARED_ORIGIN_SEARCH_PER_PAGE + ALIAS_OR_SHARED_ORIGIN_SEARCH_PER_PAGE, + undefined ); expect(result.objects).toEqual([ // Note: in a realistic scenario, `spacesWithMatchingOrigins` would be a superset of `spaces`. But for the purposes of this unit @@ -441,8 +442,9 @@ describe('collectMultiNamespaceReferences', () => { expect(mockFindSharedOriginObjects).toHaveBeenCalledTimes(1); expect(mockFindSharedOriginObjects).toHaveBeenCalledWith( expect.anything(), - [{ type: obj1.type, origin: obj1.id }], - ALIAS_OR_SHARED_ORIGIN_SEARCH_PER_PAGE + [{ type: obj1.type, id: obj1.id }], + ALIAS_OR_SHARED_ORIGIN_SEARCH_PER_PAGE, + undefined ); }); @@ -458,6 +460,25 @@ describe('collectMultiNamespaceReferences', () => { 'Failed to retrieve shared origin objects: Oh no!' ); }); + + it('passes options to findSharedOriginObjects', async () => { + const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' }; + const obj2 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-2' }; + const params = setup([obj1, obj2]); + mockMgetResults({ found: true }, { found: false }); // results for obj1 and obj2 + + await collectMultiNamespaceReferences({ + ...params, + options: { purpose: 'updateObjectsSpaces' }, + }); + expect(mockFindSharedOriginObjects).toHaveBeenCalledTimes(1); + expect(mockFindSharedOriginObjects).toHaveBeenCalledWith( + expect.anything(), + [{ type: obj1.type, id: obj1.id }], + ALIAS_OR_SHARED_ORIGIN_SEARCH_PER_PAGE, + 'updateObjectsSpaces' + ); + }); }); describe('with security enabled', () => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/collect_multi_namespace_references.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/collect_multi_namespace_references.ts index 87254d1608515..ab11f76d9e967 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/collect_multi_namespace_references.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/collect_multi_namespace_references.ts @@ -108,12 +108,14 @@ export async function collectMultiNamespaceReferences( ); const objectOriginsToSearchFor = foundObjects.map(({ type, id, originId }) => ({ type, - origin: originId || id, + id, + origin: originId, })); const originsMap = await findSharedOriginObjects( createPointInTimeFinder, objectOriginsToSearchFor, - ALIAS_OR_SHARED_ORIGIN_SEARCH_PER_PAGE + ALIAS_OR_SHARED_ORIGIN_SEARCH_PER_PAGE, + options?.purpose ); const results = objectsWithContext.map((obj) => { const aliasesVal = aliasesMap.get(getObjectKey(obj)); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/find_shared_origin_objects.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/find_shared_origin_objects.test.ts index c9f90073da24f..1622d953d7a36 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/find_shared_origin_objects.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/find_shared_origin_objects.test.ts @@ -59,10 +59,10 @@ describe('findSharedOriginObjects', () => { }); } - const obj1 = { type: 'type-1', origin: 'id-1' }; - const obj2 = { type: 'type-2', origin: 'id-2' }; - const obj3 = { type: 'type-3', origin: 'id-3' }; - const obj4 = { type: 'type-4', origin: 'id-4' }; + const obj1 = { type: 'type-1', id: 'id-1', origin: 'origin-1' }; + const obj2 = { type: 'type-2', id: 'id-2', origin: 'origin-2' }; + const obj3 = { type: 'type-3', id: 'id-3', origin: 'origin-3' }; + const obj4 = { type: 'type-4', id: 'id-4', origin: 'origin-4' }; it('uses the PointInTimeFinder to search for legacy URL aliases', async () => { mockFindResults( @@ -156,4 +156,220 @@ describe('findSharedOriginObjects', () => { expect(pointInTimeFinder.find).toHaveBeenCalledTimes(1); expect(pointInTimeFinder.close).toHaveBeenCalledTimes(2); }); + + describe(`when options.purpose is 'updateObjectsSpaces'`, () => { + it('calls createPointInTimeFinder with filter to ignore direct ID matches', async () => { + const objects = [obj1, obj2, obj3]; + await findSharedOriginObjects(createPointInTimeFinder, objects, 999, 'updateObjectsSpaces'); + expect(createPointInTimeFinder).toHaveBeenCalledTimes(1); + expect(createPointInTimeFinder).toHaveBeenCalledWith( + expect.objectContaining({ + filter: expect.objectContaining({ + arguments: expect.arrayContaining([ + expect.objectContaining({ + arguments: expect.arrayContaining([ + expect.objectContaining({ + arguments: [ + { + arguments: [ + { + isQuoted: false, + type: 'literal', + value: 'type-1.id', + }, + { + isQuoted: false, + type: 'literal', + value: 'type-1:id-1', + }, + ], + function: 'is', + type: 'function', + }, + ], + function: 'not', + type: 'function', + }), + ]), + }), + expect.objectContaining({ + arguments: expect.arrayContaining([ + expect.objectContaining({ + arguments: [ + { + arguments: [ + { + isQuoted: false, + type: 'literal', + value: 'type-2.id', + }, + { + isQuoted: false, + type: 'literal', + value: 'type-2:id-2', + }, + ], + function: 'is', + type: 'function', + }, + ], + function: 'not', + type: 'function', + }), + ]), + }), + expect.objectContaining({ + arguments: expect.arrayContaining([ + expect.objectContaining({ + arguments: [ + { + arguments: [ + { + isQuoted: false, + type: 'literal', + value: 'type-3.id', + }, + { + isQuoted: false, + type: 'literal', + value: 'type-3:id-3', + }, + ], + function: 'is', + type: 'function', + }, + ], + function: 'not', + type: 'function', + }), + ]), + }), + ]), + }), + }), + undefined, + { disableExtensions: true } + ); + }); + + it('calls createPointInTimeFinder without redundant filter when object does not have an origin ID', async () => { + const objects = [obj1, { ...obj2, origin: undefined }, obj3]; + await findSharedOriginObjects(createPointInTimeFinder, objects, 999, 'updateObjectsSpaces'); + expect(createPointInTimeFinder).toHaveBeenCalledTimes(1); + expect(createPointInTimeFinder).toHaveBeenCalledWith( + expect.objectContaining({ + filter: expect.objectContaining({ + arguments: expect.arrayContaining([ + expect.objectContaining({ + arguments: expect.arrayContaining([ + expect.objectContaining({ + arguments: [ + { + arguments: [ + { + isQuoted: false, + type: 'literal', + value: 'type-1.id', + }, + { + isQuoted: false, + type: 'literal', + value: 'type-1:origin-1', + }, + ], + function: 'is', + type: 'function', + }, + { + arguments: [ + { + isQuoted: false, + type: 'literal', + value: 'type-1.originId', + }, + { + isQuoted: false, + type: 'literal', + value: 'origin-1', + }, + ], + function: 'is', + type: 'function', + }, + ], + function: 'or', + type: 'function', + }), + ]), + }), + expect.objectContaining({ + arguments: expect.arrayContaining([ + expect.objectContaining({ + arguments: [ + { + isQuoted: false, + type: 'literal', + value: 'type-2.originId', + }, + { + isQuoted: false, + type: 'literal', + value: 'id-2', + }, + ], + function: 'is', + type: 'function', + }), + ]), + }), + expect.objectContaining({ + arguments: expect.arrayContaining([ + expect.objectContaining({ + arguments: [ + { + arguments: [ + { + isQuoted: false, + type: 'literal', + value: 'type-3.id', + }, + { + isQuoted: false, + type: 'literal', + value: 'type-3:origin-3', + }, + ], + function: 'is', + type: 'function', + }, + { + arguments: [ + { + isQuoted: false, + type: 'literal', + value: 'type-3.originId', + }, + { + isQuoted: false, + type: 'literal', + value: 'origin-3', + }, + ], + function: 'is', + type: 'function', + }, + ], + function: 'or', + type: 'function', + }), + ]), + }), + ]), + }), + }), + undefined, + { disableExtensions: true } + ); + }); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/find_shared_origin_objects.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/find_shared_origin_objects.ts index 8ed1f0a3c965a..bdf1a11375763 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/find_shared_origin_objects.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/find_shared_origin_objects.ts @@ -9,13 +9,22 @@ import * as esKuery from '@kbn/es-query'; import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; import { getObjectKey } from '@kbn/core-saved-objects-base-server-internal'; +import { SavedObjectsCollectMultiNamespaceReferencesPurpose } from '@kbn/core-saved-objects-api-server/src/apis'; +import { + KQL_FUNCTION_AND, + KQL_FUNCTION_IS, + KQL_FUNCTION_NOT, + KQL_FUNCTION_OR, +} from '@kbn/es-query/src/kuery/functions'; import type { CreatePointInTimeFinderFn } from '../../point_in_time_finder'; interface ObjectOrigin { /** The object's type. */ type: string; - /** The object's origin is its `originId` field, or its `id` field if that is unavailable. */ - origin: string; + /** The object's ID. */ + id: string; + /** The object's origin is its `originId` field */ + origin: string | undefined; } /** @@ -26,14 +35,15 @@ interface ObjectOrigin { export async function findSharedOriginObjects( createPointInTimeFinder: CreatePointInTimeFinderFn, objects: ObjectOrigin[], - perPage?: number + perPage?: number, + purpose?: SavedObjectsCollectMultiNamespaceReferencesPurpose ) { if (!objects.length) { return new Map>(); } const uniqueObjectTypes = objects.reduce((acc, { type }) => acc.add(type), new Set()); - const filter = createAliasKueryFilter(objects); + const filter = createOriginKueryFilter(objects, purpose); const finder = createPointInTimeFinder( { type: [...uniqueObjectTypes], @@ -80,17 +90,54 @@ export async function findSharedOriginObjects( return objectsMap; } -function createAliasKueryFilter(objects: Array<{ type: string; origin: string }>) { +function createOriginKueryFilter( + objects: ObjectOrigin[], + purpose?: SavedObjectsCollectMultiNamespaceReferencesPurpose +) { const { buildNode } = esKuery.nodeTypes.function; // Note: these nodes include '.attributes' for type-level fields because these are eventually passed to `validateConvertFilterToKueryNode`, which requires it const kueryNodes = objects - .reduce((acc, { type, origin }) => { + .reduce((acc, { type, id, origin }) => { // Escape Kuery values to prevent parsing errors and unintended behavior (object types/IDs can contain KQL special characters/operators) - const match1 = buildNode('is', `${type}.id`, esKuery.escapeKuery(`${type}:${origin}`)); // here we are looking for the raw document `_id` field, which has a `type:` prefix - const match2 = buildNode('is', `${type}.originId`, esKuery.escapeKuery(origin)); // here we are looking for the saved object's `originId` field, which does not have a `type:` prefix - acc.push([match1, match2]); + + // Look for objects with an ID that matches the origin or ID (has a `type:` prefix) + const idMatchesOrigin = buildNode( + KQL_FUNCTION_IS, + `${type}.id`, + esKuery.escapeKuery(`${type}:${origin || id}`) + ); + + // Look for objects with an `originId` that matches the origin or ID (does not have a `type:` prefix) + const originMatch = buildNode( + KQL_FUNCTION_IS, + `${type}.originId`, + esKuery.escapeKuery(origin || id) + ); + + // If we are updating an object's spaces (as opposed to copying)... + if (purpose === 'updateObjectsSpaces') { + // we never want to match on the raw document `_id` fields. + // If they are equal, this just means that the object already exists in that space and it's ok. + const notIdMatch = buildNode( + KQL_FUNCTION_NOT, + buildNode(KQL_FUNCTION_IS, `${type}.id`, esKuery.escapeKuery(`${type}:${id}`)) + ); + + // If this object has an origin ID, then we do still want to match if another object's ID matches the + // object's origin (idMatchesOrigin), or if another object's origin matches the object's origin (originMatch). + // But if this object does not have an origin ID, we can skip the idMatchesOrigin part altogether + // and just check if another object's origin ID matches this object's ID (originMatch). + // (maybe slightly more efficient?) + acc.push( + buildNode(KQL_FUNCTION_AND, [ + notIdMatch, + origin ? buildNode(KQL_FUNCTION_OR, [idMatchesOrigin, originMatch]) : originMatch, + ]) + ); + } else acc.push([idMatchesOrigin, originMatch]); // If we are copying, things are much simpler + return acc; }, []) .flat(); - return buildNode('or', kueryNodes); + return buildNode(KQL_FUNCTION_OR, kueryNodes); } diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/collect_multinamespace_references.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/collect_multinamespace_references.ts index fcd0d079961bd..f13d2f7261329 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/collect_multinamespace_references.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/collect_multinamespace_references.ts @@ -24,6 +24,13 @@ export interface SavedObjectsCollectMultiNamespaceReferencesObject { type: string; } +/** + * Purpose for collecting references. + */ +export type SavedObjectsCollectMultiNamespaceReferencesPurpose = + | 'collectMultiNamespaceReferences' + | 'updateObjectsSpaces'; + /** * Options for collecting references. * @@ -32,7 +39,7 @@ export interface SavedObjectsCollectMultiNamespaceReferencesObject { export interface SavedObjectsCollectMultiNamespaceReferencesOptions extends SavedObjectsBaseOptions { /** Optional purpose used to determine filtering and authorization checks; default is 'collectMultiNamespaceReferences' */ - purpose?: 'collectMultiNamespaceReferences' | 'updateObjectsSpaces'; + purpose?: SavedObjectsCollectMultiNamespaceReferencesPurpose; } /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/index.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/index.ts index ab3c3ca12f572..d343e1472b86d 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/index.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/index.ts @@ -35,6 +35,7 @@ export type { SavedObjectReferenceWithContext, SavedObjectsCollectMultiNamespaceReferencesResponse, SavedObjectsCollectMultiNamespaceReferencesOptions, + SavedObjectsCollectMultiNamespaceReferencesPurpose, } from './collect_multinamespace_references'; export type { SavedObjectsCreateOptions } from './create'; export type { diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 4ebb8065dbe1d..7fc18bb58a00b 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1645,6 +1645,9 @@ "overrides": { "type": "flattened", "index": false + }, + "keep_monitoring_alive": { + "type": "boolean" } } }, @@ -2594,6 +2597,106 @@ "dynamic": false, "properties": {} }, + "infrastructure-ui-source": { + "dynamic": false, + "properties": {} + }, + "inventory-view": { + "dynamic": false, + "properties": {} + }, + "metrics-explorer-view": { + "dynamic": false, + "properties": {} + }, + "upgrade-assistant-reindex-operation": { + "dynamic": false, + "properties": { + "indexName": { + "type": "keyword" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-ml-upgrade-operation": { + "dynamic": false, + "properties": { + "snapshotId": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + } + } + }, + "monitoring-telemetry": { + "properties": { + "reportedClusterUuids": { + "type": "keyword" + } + } + }, + "apm-telemetry": { + "dynamic": false, + "properties": {} + }, + "apm-server-schema": { + "properties": { + "schemaJson": { + "type": "text", + "index": false + } + } + }, + "apm-service-group": { + "properties": { + "groupName": { + "type": "keyword" + }, + "kuery": { + "type": "text" + }, + "description": { + "type": "text" + }, + "color": { + "type": "text" + } + } + }, + "apm-custom-dashboards": { + "properties": { + "dashboardSavedObjectId": { + "type": "keyword" + }, + "kuery": { + "type": "text" + }, + "serviceEnvironmentFilterEnabled": { + "type": "boolean" + }, + "serviceNameFilterEnabled": { + "type": "boolean" + } + } + }, + "enterprise_search_telemetry": { + "dynamic": false, + "properties": {} + }, + "app_search_telemetry": { + "dynamic": false, + "properties": {} + }, + "workplace_search_telemetry": { + "dynamic": false, + "properties": {} + }, "siem-ui-timeline-note": { "properties": { "eventId": { @@ -3055,105 +3158,5 @@ "index": false } } - }, - "infrastructure-ui-source": { - "dynamic": false, - "properties": {} - }, - "inventory-view": { - "dynamic": false, - "properties": {} - }, - "metrics-explorer-view": { - "dynamic": false, - "properties": {} - }, - "upgrade-assistant-reindex-operation": { - "dynamic": false, - "properties": { - "indexName": { - "type": "keyword" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-ml-upgrade-operation": { - "dynamic": false, - "properties": { - "snapshotId": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 - } - } - } - } - }, - "monitoring-telemetry": { - "properties": { - "reportedClusterUuids": { - "type": "keyword" - } - } - }, - "apm-telemetry": { - "dynamic": false, - "properties": {} - }, - "apm-server-schema": { - "properties": { - "schemaJson": { - "type": "text", - "index": false - } - } - }, - "apm-service-group": { - "properties": { - "groupName": { - "type": "keyword" - }, - "kuery": { - "type": "text" - }, - "description": { - "type": "text" - }, - "color": { - "type": "text" - } - } - }, - "apm-custom-dashboards": { - "properties": { - "dashboardSavedObjectId": { - "type": "keyword" - }, - "kuery": { - "type": "text" - }, - "serviceEnvironmentFilterEnabled": { - "type": "boolean" - }, - "serviceNameFilterEnabled": { - "type": "boolean" - } - } - }, - "enterprise_search_telemetry": { - "dynamic": false, - "properties": {} - }, - "app_search_telemetry": { - "dynamic": false, - "properties": {} - }, - "workplace_search_telemetry": { - "dynamic": false, - "properties": {} } } diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 6f2800a8d6348..65183620e756d 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`, @@ -765,6 +764,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { api: `${FLEET_DOCS}fleet-api-docs.html`, uninstallAgent: `${SECURITY_SOLUTION_DOCS}uninstall-agent.html`, installAndUninstallIntegrationAssets: `${FLEET_DOCS}install-uninstall-integration-assets.html`, + elasticAgentInputConfiguration: `${FLEET_DOCS}elastic-agent-input-configuration.html`, }, ecs: { guide: `${ELASTIC_WEBSITE_URL}guide/en/ecs/current/index.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 5f513392588a6..6d4245b769a27 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; @@ -523,6 +522,7 @@ export interface DocLinks { api: string; uninstallAgent: string; installAndUninstallIntegrationAssets: string; + elasticAgentInputConfiguration: string; }>; readonly ecs: { readonly guide: string; diff --git a/packages/kbn-eslint-config/.eslintrc.js b/packages/kbn-eslint-config/.eslintrc.js index 36504cb8b8355..e9fae3fd1b290 100644 --- a/packages/kbn-eslint-config/.eslintrc.js +++ b/packages/kbn-eslint-config/.eslintrc.js @@ -8,6 +8,7 @@ module.exports = { '@kbn/eslint-plugin-eslint', '@kbn/eslint-plugin-imports', '@kbn/eslint-plugin-telemetry', + '@kbn/eslint-plugin-i18n', 'prettier', ], diff --git a/packages/kbn-eslint-plugin-i18n/README.mdx b/packages/kbn-eslint-plugin-i18n/README.mdx new file mode 100644 index 0000000000000..0a22ff846726e --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/README.mdx @@ -0,0 +1,17 @@ +--- +id: kibDevDocsOpsEslintPluginI18N +slug: /kibana-dev-docs/ops/kbn-eslint-plugin-i18n +title: '@kbn/eslint-plugin-i18n' +description: Custom ESLint rules to support translations in the Kibana repository +tags: ['kibana', 'dev', 'contributor', 'operations', 'eslint', 'i18n'] +--- + +`@kbn/eslint-plugin-i18n` is an ESLint plugin providing custom rules for validating JSXCode in the Kibana repo to make sure they are translated. + +## `@kbn/i18n/strings_should_be_translated_with_i18n` + +This rule warns engineers to translate their strings by using i18n.translate from the '@kbn/i18n' package. It provides an autofix that takes into account the context of the translatable string in the JSX tree to generate a translation ID. + +## `@kbn/i18n/strings_should_be_translated_with_formatted_message` + +This rule warns engineers to translate their strings by using `` from the '@kbn/i18n-react' package. It provides an autofix that takes into account the context of the translatable string in the JSX tree and to generate a translation ID. diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_function_name.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_function_name.ts new file mode 100644 index 0000000000000..c82ce04480b82 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_function_name.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; +import { lowerCaseFirstLetter } from './utils'; + +export function getFunctionName(func: TSESTree.FunctionDeclaration | TSESTree.Node): string { + if ( + 'id' in func && + func.id && + func.type === AST_NODE_TYPES.FunctionDeclaration && + func.id.type === AST_NODE_TYPES.Identifier + ) { + return lowerCaseFirstLetter(func.id.name); + } + + if ( + func.parent && + (func.parent.type !== AST_NODE_TYPES.VariableDeclarator || + func.parent.id.type !== AST_NODE_TYPES.Identifier) + ) { + return getFunctionName(func.parent); + } + + if (func.parent?.id && 'name' in func.parent.id) { + return lowerCaseFirstLetter(func.parent.id.name); + } + + return ''; +} diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.test.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.test.ts new file mode 100644 index 0000000000000..d1157b7b16f10 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { getI18nIdentifierFromFilePath } from './get_i18n_identifier_from_file_path'; + +const SYSTEMPATH = 'systemPath'; + +const testMap = [ + ['x-pack/plugins/observability/foo/bar/baz/header_actions.tsx', 'xpack.observability'], + ['x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx', 'xpack.apm'], + ['x-pack/plugins/cases/public/components/foo.tsx', 'xpack.cases'], + [ + 'packages/kbn-alerts-ui-shared/src/alert_lifecycle_status_badge/index.tsx', + 'app_not_found_in_i18nrc', + ], +]; + +describe('Get i18n Identifier for file', () => { + test.each(testMap)( + 'should get the right i18n identifier for a file inside an x-pack plugin', + (path, expectedValue) => { + const appName = getI18nIdentifierFromFilePath(`${SYSTEMPATH}/${path}`, SYSTEMPATH); + expect(appName).toBe(expectedValue); + } + ); +}); diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.ts new file mode 100644 index 0000000000000..360f70e0b3fa8 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.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 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 fs from 'fs'; +import { join, parse, resolve } from 'path'; +import { findKey } from 'lodash'; + +export function getI18nIdentifierFromFilePath(fileName: string, cwd: string) { + const { dir } = parse(fileName); + const relativePathToFile = dir.replace(cwd, ''); + + const relativePathArray = relativePathToFile.split('/'); + + const path = `${relativePathArray[2]}/${relativePathArray[3]}`; + + const xpackRC = resolve(join(__dirname, '../../../'), 'x-pack/.i18nrc.json'); + + const i18nrcFile = fs.readFileSync(xpackRC, 'utf8'); + const i18nrc = JSON.parse(i18nrcFile); + + return i18nrc && i18nrc.paths + ? findKey(i18nrc.paths, (v) => v === path) ?? 'app_not_found_in_i18nrc' + : 'app_not_found_in_i18nrc'; +} diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts new file mode 100644 index 0000000000000..f26bc62c555f0 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts @@ -0,0 +1,50 @@ +/* + * 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 { SourceCode } from 'eslint'; + +const KBN_I18N_I18N_IMPORT = "import { i18n } from '@kbn/i18n';" as const; +const KBN_I18N_REACT_FORMATTED_MESSAGE_IMPORT = + "import { FormattedMessage } from '@kbn/i18n-react';" as const; +export function getI18nImportFixer({ + sourceCode, + mode, +}: { + sourceCode: SourceCode; + mode: 'i18n.translate' | 'FormattedMessage'; +}) { + const hasI18nImportLine = Boolean( + sourceCode.lines.find((l) => + mode === 'i18n.translate' + ? l === KBN_I18N_I18N_IMPORT + : l === KBN_I18N_REACT_FORMATTED_MESSAGE_IMPORT + ) + ); + + if (hasI18nImportLine) return { hasI18nImportLine }; + + // Translation package has not been imported yet so we need to add it. + // Pretty safe bet to add it underneath the React import. + const reactImportLineIndex = sourceCode.lines.findIndex((l) => l.includes("from 'react'")); + + const targetLine = sourceCode.lines[reactImportLineIndex]; + const column = targetLine.length; + + const start = sourceCode.getIndexFromLoc({ line: reactImportLineIndex + 1, column: 0 }); + const end = sourceCode.getIndexFromLoc({ + line: reactImportLineIndex + 1, + column, + }); + + return { + hasI18nImportLine, + i18nPackageImportLine: + mode === 'i18n.translate' ? KBN_I18N_I18N_IMPORT : KBN_I18N_REACT_FORMATTED_MESSAGE_IMPORT, + rangeToAddI18nImportLine: [start, end] as [number, number], + }; +} diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_intent_from_node.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_intent_from_node.ts new file mode 100644 index 0000000000000..4cbd6bb9e330d --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_intent_from_node.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { TSESTree } from '@typescript-eslint/typescript-estree'; +import { lowerCaseFirstLetter, upperCaseFirstLetter } from './utils'; + +export function getIntentFromNode(originalNode: TSESTree.JSXText): string { + const value = lowerCaseFirstLetter( + originalNode.value + .replace(/[?!@#$%^&*()_+\][{}|/<>,'"]/g, '') + .trim() + .split(' ') + .filter((v, i) => i < 4) + .map(upperCaseFirstLetter) + .join('') + ); + + const { parent } = originalNode; + + if ( + parent && + 'openingElement' in parent && + 'name' in parent.openingElement && + 'name' in parent.openingElement.name + ) { + const parentTagName = String(parent.openingElement.name.name); + + if (parentTagName.includes('Eui')) { + return `${value}${parentTagName.replace('Eui', '')}Label`; + } + + return `${lowerCaseFirstLetter(parentTagName)}.${value}Label`; + } + + return `${value}Label`; +} diff --git a/packages/kbn-eslint-plugin-i18n/helpers/utils.ts b/packages/kbn-eslint-plugin-i18n/helpers/utils.ts new file mode 100644 index 0000000000000..74ce8aa7c5c55 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/helpers/utils.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +export function lowerCaseFirstLetter(str: string) { + return str.charAt(0).toLowerCase() + str.slice(1); +} + +export function upperCaseFirstLetter(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +export function isTruthy(value: T): value is NonNullable { + return value != null; +} diff --git a/packages/kbn-eslint-plugin-i18n/index.ts b/packages/kbn-eslint-plugin-i18n/index.ts new file mode 100644 index 0000000000000..d6fba728b7d21 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { StringsShouldBeTranslatedWithI18n } from './rules/strings_should_be_translated_with_i18n'; +import { StringsShouldBeTranslatedWithFormattedMessage } from './rules/strings_should_be_translated_with_formatted_message'; + +/** + * Custom ESLint rules, add `'@kbn/eslint-plugin-telemetry'` to your eslint config to use them + * @internal + */ +export const rules = { + strings_should_be_translated_with_i18n: StringsShouldBeTranslatedWithI18n, + strings_should_be_translated_with_formatted_message: + StringsShouldBeTranslatedWithFormattedMessage, +}; diff --git a/packages/kbn-eslint-plugin-i18n/jest.config.js b/packages/kbn-eslint-plugin-i18n/jest.config.js new file mode 100644 index 0000000000000..1ab5f78f024bf --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/jest.config.js @@ -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 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-eslint-plugin-i18n'], +}; diff --git a/packages/kbn-eslint-plugin-i18n/kibana.jsonc b/packages/kbn-eslint-plugin-i18n/kibana.jsonc new file mode 100644 index 0000000000000..7d8c994400108 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/kibana.jsonc @@ -0,0 +1,6 @@ +{ + "type": "shared-common", + "id": "@kbn/eslint-plugin-i18n", + "owner": "@elastic/actionable-observability", + "devOnly": true +} diff --git a/packages/kbn-eslint-plugin-i18n/package.json b/packages/kbn-eslint-plugin-i18n/package.json new file mode 100644 index 0000000000000..1668a7e0f3690 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/eslint-plugin-i18n", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts new file mode 100644 index 0000000000000..aa545c9bb0ee0 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts @@ -0,0 +1,176 @@ +/* + * 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 { RuleTester } from 'eslint'; +import { StringsShouldBeTranslatedWithFormattedMessage } from './strings_should_be_translated_with_formatted_message'; + +const tsTester = [ + '@typescript-eslint/parser', + new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, + }), +] as const; + +const babelTester = [ + '@babel/eslint-parser', + new RuleTester({ + parser: require.resolve('@babel/eslint-parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + requireConfigFile: false, + babelOptions: { + presets: ['@kbn/babel-preset/node_preset'], + }, + }, + }), +] as const; + +const valid = [ + { + filename: 'x-pack/plugins/observability/public/test_component.tsx', + code: ` +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +function TestComponent() { + return ( +
+
+ ) +}`, + }, + { + filename: 'x-pack/plugins/observability/public/another_component.tsx', + code: ` +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +function AnotherComponent() { + return ( + + + + + + + + + ) +}`, + }, + { + filename: 'x-pack/plugins/observability/public/yet_another_component.tsx', + code: ` +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +function YetAnotherComponent() { + return ( +
+ + +
+ ) +}`, + }, +]; + +const invalid = [ + { + filename: valid[0].filename, + code: ` +import React from 'react'; + +function TestComponent() { + return ( +
This is a test
+ ) +}`, + errors: [ + { + line: 6, + message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + }, + ], + output: valid[0].code, + }, + { + filename: valid[1].filename, + code: ` +import React from 'react'; + +function AnotherComponent() { + return ( + + + + This is a test + + + + ) +}`, + errors: [ + { + line: 9, + message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + }, + ], + output: valid[1].code, + }, + { + filename: valid[2].filename, + code: ` +import React from 'react'; + +function YetAnotherComponent() { + return ( +
+ Select me +
+ ) +}`, + errors: [ + { + line: 7, + message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + }, + ], + output: valid[2].code, + }, +]; + +for (const [name, tester] of [tsTester, babelTester]) { + describe(name, () => { + tester.run( + '@kbn/event_generating_elements_should_be_instrumented', + StringsShouldBeTranslatedWithFormattedMessage, + { + valid, + invalid, + } + ); + }); +} diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts new file mode 100644 index 0000000000000..251fb3b3752fc --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts @@ -0,0 +1,77 @@ +/* + * 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 { TSESTree } from '@typescript-eslint/typescript-estree'; +import type { Rule } from 'eslint'; +import { getIntentFromNode } from '../helpers/get_intent_from_node'; +import { getI18nIdentifierFromFilePath } from '../helpers/get_i18n_identifier_from_file_path'; +import { getFunctionName } from '../helpers/get_function_name'; +import { getI18nImportFixer } from '../helpers/get_i18n_import_fixer'; +import { isTruthy } from '../helpers/utils'; + +export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { + meta: { + type: 'suggestion', + fixable: 'code', + }, + create(context) { + const { cwd, filename, getScope, sourceCode, report } = context; + + return { + JSXText: (node: TSESTree.JSXText) => { + const value = node.value.trim(); + + // If the JSXText element is empty we don't need to do anything + if (!value) return; + + // Get the whitespaces before the string so we can add them to the autofix suggestion + const regex = /^(\s*)(\S)(.*)/; + const whiteSpaces = node.value.match(regex)?.[1] ?? ''; + + // Start building the translation ID suggestion + const i18nAppId = getI18nIdentifierFromFilePath(filename, cwd); + const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration; + const functionName = getFunctionName(functionDeclaration); + const intent = getIntentFromNode(node); + + const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' + + // Check if i18n has already been imported into the file. + const { + hasI18nImportLine, + i18nPackageImportLine: i18nImportLine, + rangeToAddI18nImportLine, + } = getI18nImportFixer({ + sourceCode, + mode: 'FormattedMessage', + }); + + // Show warning to developer and offer autofix suggestion + report({ + node: node as any, + message: + 'Strings should be translated with . Use the autofix suggestion or add your own.', + fix(fixer) { + return [ + fixer.replaceText( + node, + `${whiteSpaces}\n` + ), + !hasI18nImportLine + ? fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) + : null, + ].filter(isTruthy); + }, + }); + }, + } as Rule.RuleListener; + }, +}; diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts new file mode 100644 index 0000000000000..4c5916831b6cb --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts @@ -0,0 +1,164 @@ +/* + * 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 { RuleTester } from 'eslint'; +import { StringsShouldBeTranslatedWithI18n } from './strings_should_be_translated_with_i18n'; + +const tsTester = [ + '@typescript-eslint/parser', + new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, + }), +] as const; + +const babelTester = [ + '@babel/eslint-parser', + new RuleTester({ + parser: require.resolve('@babel/eslint-parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + requireConfigFile: false, + babelOptions: { + presets: ['@kbn/babel-preset/node_preset'], + }, + }, + }), +] as const; + +const valid = [ + { + filename: 'x-pack/plugins/observability/public/test_component.tsx', + code: ` +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +function TestComponent() { + return ( +
{i18n.translate('app_not_found_in_i18nrc.testComponent.div.thisIsATestLabel', { defaultMessage: "This is a test"})}
+ ) +}`, + }, + { + filename: 'x-pack/plugins/observability/public/another_component.tsx', + code: ` +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +function AnotherComponent() { + return ( + + + + {i18n.translate('app_not_found_in_i18nrc.anotherComponent.thisIsATestButtonLabel', { defaultMessage: "This is a test"})} + + + + ) +}`, + }, + { + filename: 'x-pack/plugins/observability/public/yet_another_component.tsx', + code: ` +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +function YetAnotherComponent() { + return ( +
+ {i18n.translate('app_not_found_in_i18nrc.yetAnotherComponent.selectMeSelectLabel', { defaultMessage: "Select me"})} +
+ ) +}`, + }, +]; + +const invalid = [ + { + filename: valid[0].filename, + code: ` +import React from 'react'; + +function TestComponent() { + return ( +
This is a test
+ ) +}`, + errors: [ + { + line: 6, + message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + }, + ], + output: valid[0].code, + }, + { + filename: valid[1].filename, + code: ` +import React from 'react'; + +function AnotherComponent() { + return ( + + + + This is a test + + + + ) +}`, + errors: [ + { + line: 9, + message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + }, + ], + output: valid[1].code, + }, + { + filename: valid[2].filename, + code: ` +import React from 'react'; + +function YetAnotherComponent() { + return ( +
+ Select me +
+ ) +}`, + errors: [ + { + line: 7, + message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + }, + ], + output: valid[2].code, + }, +]; + +for (const [name, tester] of [tsTester, babelTester]) { + describe(name, () => { + tester.run( + '@kbn/event_generating_elements_should_be_instrumented', + StringsShouldBeTranslatedWithI18n, + { + valid, + invalid, + } + ); + }); +} diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts new file mode 100644 index 0000000000000..a1e0da36921b6 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts @@ -0,0 +1,74 @@ +/* + * 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 { TSESTree } from '@typescript-eslint/typescript-estree'; +import type { Rule } from 'eslint'; +import { getIntentFromNode } from '../helpers/get_intent_from_node'; +import { getI18nIdentifierFromFilePath } from '../helpers/get_i18n_identifier_from_file_path'; +import { getFunctionName } from '../helpers/get_function_name'; +import { getI18nImportFixer } from '../helpers/get_i18n_import_fixer'; +import { isTruthy } from '../helpers/utils'; + +export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { + meta: { + type: 'suggestion', + fixable: 'code', + }, + create(context) { + const { cwd, filename, getScope, sourceCode, report } = context; + + return { + JSXText: (node: TSESTree.JSXText) => { + const value = node.value.trim(); + + // If the JSXText element is empty we don't need to do anything + if (!value) return; + + // Get the whitespaces before the string so we can add them to the autofix suggestion + const regex = /^(\s*)(\S)(.*)/; + const whiteSpaces = node.value.match(regex)?.[1] ?? ''; + + // Start building the translation ID suggestion + const i18nAppId = getI18nIdentifierFromFilePath(filename, cwd); + const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration; + const functionName = getFunctionName(functionDeclaration); + const intent = getIntentFromNode(node); + + const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' + + // Check if i18n has already been imported into the file. + const { + hasI18nImportLine, + i18nPackageImportLine: i18nImportLine, + rangeToAddI18nImportLine, + } = getI18nImportFixer({ + sourceCode, + mode: 'i18n.translate', + }); + + // Show warning to developer and offer autofix suggestion + report({ + node: node as any, + message: + 'Strings should be translated with i18n. Use the autofix suggestion or add your own.', + fix(fixer) { + return [ + fixer.replaceText( + node, + `${whiteSpaces}{i18n.translate('${translationIdSuggestion}', { defaultMessage: "${value}"})}` + ), + !hasI18nImportLine + ? fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) + : null, + ].filter(isTruthy); + }, + }); + }, + } as Rule.RuleListener; + }, +}; diff --git a/packages/kbn-eslint-plugin-i18n/tsconfig.json b/packages/kbn-eslint-plugin-i18n/tsconfig.json new file mode 100644 index 0000000000000..a6dec1c1a62c5 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node"], + "lib": ["es2021"] + }, + "include": ["**/*.ts"], + "exclude": ["target/**/*"], + "kbn_references": [] +} diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 01794d226e312..62780b66727ef 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -104,7 +104,7 @@ describe('checking migration metadata changes on all registered SO types', () => "index-pattern": "997108a9ea1e8076e22231e1c95517cdb192b9c5", "infrastructure-monitoring-log-view": "5f86709d3c27aed7a8379153b08ee5d3d90d77f5", "infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4", - "ingest-agent-policies": "f11cc19275f4c3e4ee7c5cd6423b6706b21b989d", + "ingest-agent-policies": "7633e578f60c074f8267bc50ec4763845e431437", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", "ingest-outputs": "b4e636b13a5d0f89f0400fb67811d4cca4736eb0", "ingest-package-policies": "a0c9fb48e04dcd638e593db55f1c6451523f90ea", diff --git a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx index c889f2e0dcd06..ad344d482c0cf 100644 --- a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx +++ b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx @@ -124,7 +124,6 @@ const IndexPatternEditorFlyoutContentComponent = ({ timeFieldName: formData.timestampField?.value, id: formData.id, name: formData.name, - allowHidden: formData.allowHidden, }; if (type === INDEX_PATTERN_TYPE.ROLLUP && rollupIndex) { diff --git a/src/plugins/data_views/common/data_views/__snapshots__/data_views.test.ts.snap b/src/plugins/data_views/common/data_views/__snapshots__/data_views.test.ts.snap index 53b77e28a416c..0d646f4afc28d 100644 --- a/src/plugins/data_views/common/data_views/__snapshots__/data_views.test.ts.snap +++ b/src/plugins/data_views/common/data_views/__snapshots__/data_views.test.ts.snap @@ -28,7 +28,6 @@ exports[`IndexPatterns delete will throw if insufficient access 1`] = `[DataView exports[`IndexPatterns savedObjectToSpec 1`] = ` Object { - "allowHidden": undefined, "allowNoIndex": undefined, "fieldAttrs": Object { "aRuntimeField": Object { diff --git a/src/plugins/data_views/common/data_views/data_view.ts b/src/plugins/data_views/common/data_views/data_view.ts index 3cea4505b572c..ffda65af2a895 100644 --- a/src/plugins/data_views/common/data_views/data_view.ts +++ b/src/plugins/data_views/common/data_views/data_view.ts @@ -152,8 +152,6 @@ export class DataView implements DataViewBase { */ public matchedIndices: string[] = []; - private allowHidden: boolean = false; - /** * constructor * @param config - config data and dependencies @@ -189,7 +187,6 @@ export class DataView implements DataViewBase { this.runtimeFieldMap = cloneDeep(spec.runtimeFieldMap) || {}; this.namespaces = spec.namespaces || []; this.name = spec.name || ''; - this.allowHidden = spec.allowHidden || false; } /** @@ -204,8 +201,6 @@ export class DataView implements DataViewBase { getIndexPattern = () => this.title; - getAllowHidden = () => this.allowHidden; - /** * Set index pattern * @param string index pattern string diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index d5509f03db580..8bac81f19ef62 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -584,7 +584,6 @@ export class DataViewsService { allowNoIndex: true, pattern: dataView.getIndexPattern(), metaFields, - allowHidden: dataView.getAllowHidden(), }); }; @@ -597,7 +596,6 @@ export class DataViewsService { rollupIndex: options.rollupIndex, allowNoIndex: true, indexFilter: options.indexFilter, - allowHidden: options.allowHidden, }); }; @@ -749,7 +747,6 @@ export class DataViewsService { fieldAttrs, allowNoIndex, name, - allowHidden, }, } = savedObject; @@ -777,7 +774,6 @@ export class DataViewsService { allowNoIndex, runtimeFieldMap: parsedRuntimeFieldMap, name, - allowHidden, }; }; diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 522bec8873534..9061c4643dce9 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -157,10 +157,6 @@ export interface DataViewAttributes { * Name of the data view. Human readable name used to differentiate data view. */ name?: string; - /** - * Allow hidden and system indices when loading field list - */ - allowHidden?: boolean; } /** @@ -313,7 +309,6 @@ export interface GetFieldsOptions { indexFilter?: QueryDslQueryContainer; includeUnmapped?: boolean; fields?: string[]; - allowHidden?: boolean; } /** @@ -517,10 +512,6 @@ export type DataViewSpec = { * Name of the data view. Human readable name used to differentiate data view. */ name?: string; - /** - * Whether the data view is hidden from the user - */ - allowHidden?: boolean; }; // eslint-disable-next-line @typescript-eslint/consistent-type-definitions diff --git a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts index f23933bc5775e..b1d22dc5523c8 100644 --- a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts +++ b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts @@ -69,23 +69,12 @@ export class IndexPatternsFetcher { rollupIndex?: string; indexFilter?: QueryDslQueryContainer; fields?: string[]; - allowHidden?: boolean; }): Promise<{ fields: FieldDescriptor[]; indices: string[] }> { - const { - pattern, - metaFields = [], - fieldCapsOptions, - type, - rollupIndex, - indexFilter, - allowHidden, - } = options; + const { pattern, metaFields = [], fieldCapsOptions, type, rollupIndex, indexFilter } = options; const allowNoIndices = fieldCapsOptions ? fieldCapsOptions.allow_no_indices : this.allowNoIndices; - const expandWildcards = allowHidden ? 'all' : 'open'; - const fieldCapsResponse = await getFieldCapabilities({ callCluster: this.elasticsearchClient, indices: pattern, @@ -96,7 +85,6 @@ export class IndexPatternsFetcher { }, indexFilter, fields: options.fields || ['*'], - expandWildcards, }); if (this.rollupsEnabled && type === 'rollup' && rollupIndex) { diff --git a/src/plugins/data_views/server/fetcher/lib/es_api.ts b/src/plugins/data_views/server/fetcher/lib/es_api.ts index 2128e52da537b..988e4a4ec28c8 100644 --- a/src/plugins/data_views/server/fetcher/lib/es_api.ts +++ b/src/plugins/data_views/server/fetcher/lib/es_api.ts @@ -7,7 +7,6 @@ */ import { ElasticsearchClient } from '@kbn/core/server'; -import { ExpandWildcard } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { QueryDslQueryContainer } from '../../../common/types'; import { convertEsError } from './errors'; @@ -46,7 +45,6 @@ interface FieldCapsApiParams { fieldCapsOptions?: { allow_no_indices: boolean; include_unmapped?: boolean }; indexFilter?: QueryDslQueryContainer; fields?: string[]; - expandWildcard?: ExpandWildcard; } /** diff --git a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_capabilities.test.js b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_capabilities.test.js index f41c71498e81e..1b3be374bbd7c 100644 --- a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_capabilities.test.js +++ b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_capabilities.test.js @@ -34,7 +34,6 @@ describe('index_patterns/field_capabilities/field_capabilities', () => { const fillUndefinedParams = (args) => ({ callCluster: undefined, indices: undefined, - expandWildcard: undefined, fieldCapsOptions: undefined, indexFilter: undefined, fields: undefined, diff --git a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_capabilities.ts b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_capabilities.ts index 6bef117151609..73e550ebd68ce 100644 --- a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_capabilities.ts +++ b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_capabilities.ts @@ -9,7 +9,6 @@ import { defaults, keyBy, sortBy } from 'lodash'; import { ElasticsearchClient } from '@kbn/core/server'; -import { ExpandWildcard } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { callFieldCapsApi } from '../es_api'; import { readFieldCapsResponse } from './field_caps_response'; import { mergeOverrides } from './overrides'; @@ -23,7 +22,6 @@ interface FieldCapabilitiesParams { fieldCapsOptions?: { allow_no_indices: boolean; include_unmapped?: boolean }; indexFilter?: QueryDslQueryContainer; fields?: string[]; - expandWildcards?: ExpandWildcard; } /** @@ -44,7 +42,6 @@ export async function getFieldCapabilities(params: FieldCapabilitiesParams) { indexFilter, metaFields = [], fields, - expandWildcards, } = params; const esFieldCaps = await callFieldCapsApi({ callCluster, @@ -52,7 +49,6 @@ export async function getFieldCapabilities(params: FieldCapabilitiesParams) { fieldCapsOptions, indexFilter, fields, - expandWildcard: expandWildcards, }); const fieldCapsArr = readFieldCapsResponse(esFieldCaps.body); const fieldsFromFieldCapsByName = keyBy(fieldCapsArr, 'name'); diff --git a/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts b/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts index 7d39b41d5caee..15d761935c0a7 100644 --- a/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts +++ b/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts @@ -50,7 +50,6 @@ interface IQuery { allow_no_index?: boolean; include_unmapped?: boolean; fields?: string[]; - allow_hidden?: boolean; } const querySchema = schema.object({ @@ -63,7 +62,6 @@ const querySchema = schema.object({ allow_no_index: schema.maybe(schema.boolean()), include_unmapped: schema.maybe(schema.boolean()), fields: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), - allow_hidden: schema.maybe(schema.boolean()), }); const fieldSubTypeSchema = schema.object({ @@ -124,7 +122,6 @@ const handler: (isRollupsEnabled: () => boolean) => RequestHandler<{}, IQuery, I rollup_index: rollupIndex, allow_no_index: allowNoIndex, include_unmapped: includeUnmapped, - allow_hidden: allowHidden, } = request.query; // not available to get request @@ -150,7 +147,6 @@ const handler: (isRollupsEnabled: () => boolean) => RequestHandler<{}, IQuery, I includeUnmapped, }, indexFilter, - allowHidden, ...(parsedFields.length > 0 ? { fields: parsedFields } : {}), }); diff --git a/test/functional/apps/management/data_views/_data_view_create_delete.ts b/test/functional/apps/management/data_views/_data_view_create_delete.ts index e55afd799a9b8..edf2f000fcb27 100644 --- a/test/functional/apps/management/data_views/_data_view_create_delete.ts +++ b/test/functional/apps/management/data_views/_data_view_create_delete.ts @@ -17,7 +17,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const testSubjects = getService('testSubjects'); const find = getService('find'); - const es = getService('es'); const PageObjects = getPageObjects(['settings', 'common', 'header']); describe('creating and deleting default data view', function describeIndexTests() { @@ -251,32 +250,5 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); - - describe('hidden index support', () => { - it('can create data view against hidden index', async () => { - const pattern = 'logstash-2015.09.21'; - - await es.transport.request({ - path: '/logstash-2015.09.21/_settings', - method: 'PUT', - body: { - index: { - hidden: true, - }, - }, - }); - - await PageObjects.settings.createIndexPattern( - pattern, - undefined, - undefined, - undefined, - undefined, - true - ); - const patternName = await PageObjects.settings.getIndexPageHeading(); - expect(patternName).to.be(pattern); - }); - }); }); } diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index 568c45beb0dfb..d2fba8e620153 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -470,19 +470,13 @@ export class SettingsPageObject extends FtrService { await customDataViewIdInput.type(value); } - async allowHiddenClick() { - await this.testSubjects.click('toggleAdvancedSetting'); - await this.testSubjects.click('allowHiddenField'); - } - async createIndexPattern( indexPatternName: string, // null to bypass default value timefield: string | null = '@timestamp', isStandardIndexPattern = true, customDataViewId?: string, - dataViewName?: string, - allowHidden?: boolean + dataViewName?: string ) { await this.retry.try(async () => { await this.header.waitUntilLoadingHasFinished(); @@ -495,11 +489,6 @@ export class SettingsPageObject extends FtrService { } else { await this.clickAddNewIndexPatternButton(); } - - if (allowHidden) { - await this.allowHiddenClick(); - } - await this.header.waitUntilLoadingHasFinished(); if (!isStandardIndexPattern) { await this.selectRollupIndexPatternType(); diff --git a/tsconfig.base.json b/tsconfig.base.json index 3d3f2fd964a06..fd0601d9416fe 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -714,6 +714,8 @@ "@kbn/eslint-plugin-disable/*": ["packages/kbn-eslint-plugin-disable/*"], "@kbn/eslint-plugin-eslint": ["packages/kbn-eslint-plugin-eslint"], "@kbn/eslint-plugin-eslint/*": ["packages/kbn-eslint-plugin-eslint/*"], + "@kbn/eslint-plugin-i18n": ["packages/kbn-eslint-plugin-i18n"], + "@kbn/eslint-plugin-i18n/*": ["packages/kbn-eslint-plugin-i18n/*"], "@kbn/eslint-plugin-imports": ["packages/kbn-eslint-plugin-imports"], "@kbn/eslint-plugin-imports/*": ["packages/kbn-eslint-plugin-imports/*"], "@kbn/eslint-plugin-telemetry": ["packages/kbn-eslint-plugin-telemetry"], diff --git a/x-pack/plugins/cases/docs/openapi/bundled.json b/x-pack/plugins/cases/docs/openapi/bundled.json index b36a745179833..9ef79cb9203ed 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.json +++ b/x-pack/plugins/cases/docs/openapi/bundled.json @@ -3876,7 +3876,7 @@ "from": { "in": "query", "name": "from", - "description": "[preview] Returns only cases that were created after a specific date. The date must be specified as a KQL data range or date match expression. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", + "description": "Returns only cases that were created after a specific date. The date must be specified as a KQL data range or date match expression.\n", "schema": { "type": "string", "example": "now-1d" @@ -4052,7 +4052,7 @@ "to": { "in": "query", "name": "to", - "description": "[preview] Returns only cases that were created before a specific date. The date must be specified as a KQL data range or date match expression. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", + "description": "Returns only cases that were created before a specific date. The date must be specified as a KQL data range or date match expression.\n", "schema": { "type": "string" }, diff --git a/x-pack/plugins/cases/docs/openapi/bundled.yaml b/x-pack/plugins/cases/docs/openapi/bundled.yaml index ddee756120e01..c24f9f3c67fa3 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.yaml +++ b/x-pack/plugins/cases/docs/openapi/bundled.yaml @@ -2354,7 +2354,7 @@ components: in: query name: from description: | - [preview] Returns only cases that were created after a specific date. The date must be specified as a KQL data range or date match expression. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. + Returns only cases that were created after a specific date. The date must be specified as a KQL data range or date match expression. schema: type: string example: now-1d @@ -2480,7 +2480,7 @@ components: in: query name: to description: | - [preview] Returns only cases that were created before a specific date. The date must be specified as a KQL data range or date match expression. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. + Returns only cases that were created before a specific date. The date must be specified as a KQL data range or date match expression. schema: type: string example: now+1d diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/from.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/from.yaml index 3b20869c0da6a..6f9a24dae5956 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/from.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/from.yaml @@ -1,10 +1,8 @@ in: query name: from description: > - [preview] Returns only cases that were created after a specific date. + Returns only cases that were created after a specific date. The date must be specified as a KQL data range or date match expression. - This functionality is in technical preview and may be changed or removed in a future release. - Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. schema: type: string example: now-1d \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/to.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/to.yaml index fb41e6b8223b9..c176ce8407803 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/to.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/to.yaml @@ -1,10 +1,8 @@ in: query name: to description: > - [preview] Returns only cases that were created before a specific date. + Returns only cases that were created before a specific date. The date must be specified as a KQL data range or date match expression. - This functionality is in technical preview and may be changed or removed in a future release. - Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. schema: type: string example: now+1d \ No newline at end of file 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/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index f87ab5a3edacc..e260764d57de8 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -4573,7 +4573,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/output_create_request" + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/output_create_request" + } + } } } } @@ -4650,7 +4655,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/output_update_request" + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/output_update_request" + } + } } } } @@ -7026,6 +7036,11 @@ ] } }, + "keep_monitoring_alive": { + "description": "When set to true, monitoring will be enabled but logs/metrics collection will be disabled", + "type": "boolean", + "nullable": true + }, "data_output_id": { "type": "string", "nullable": true diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 4629a95af27e8..9ed380d26726b 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -2853,7 +2853,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/output_create_request' + type: object + properties: + item: + $ref: '#/components/schemas/output_create_request' '400': $ref: '#/components/responses/error' operationId: get-output @@ -2900,7 +2903,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/output_update_request' + type: object + properties: + item: + $ref: '#/components/schemas/output_update_request' '400': $ref: '#/components/responses/error' parameters: @@ -4485,6 +4491,12 @@ components: enum: - metrics - logs + keep_monitoring_alive: + description: >- + When set to true, monitoring will be enabled but logs/metrics + collection will be disabled + type: boolean + nullable: true data_output_id: type: string nullable: true diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml index ac29a287b91c8..251f76fad966f 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml @@ -16,6 +16,10 @@ properties: enum: - metrics - logs + keep_monitoring_alive: + description: When set to true, monitoring will be enabled but logs/metrics collection will be disabled + type: boolean + nullable: true data_output_id: type: string nullable: true diff --git a/x-pack/plugins/fleet/common/openapi/paths/outputs@{output_id}.yaml b/x-pack/plugins/fleet/common/openapi/paths/outputs@{output_id}.yaml index 6049f98ca5062..ec2ce9dc0be71 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/outputs@{output_id}.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/outputs@{output_id}.yaml @@ -8,7 +8,10 @@ get: content: application/json: schema: - $ref: ../components/schemas/output_create_request.yaml + type: object + properties: + item: + $ref: ../components/schemas/output_create_request.yaml '400': $ref: ../components/responses/error.yaml operationId: get-output @@ -55,7 +58,10 @@ put: content: application/json: schema: - $ref: ../components/schemas/output_update_request.yaml + type: object + properties: + item: + $ref: ../components/schemas/output_update_request.yaml '400': $ref: ../components/responses/error.yaml parameters: diff --git a/x-pack/plugins/fleet/common/services/routes.ts b/x-pack/plugins/fleet/common/services/routes.ts index d39fcf1dfa5de..5aeb25c0e90bf 100644 --- a/x-pack/plugins/fleet/common/services/routes.ts +++ b/x-pack/plugins/fleet/common/services/routes.ts @@ -93,6 +93,12 @@ export const epmRouteService = { getBulkAssetsPath: () => { return EPM_API_ROUTES.BULK_ASSETS_PATTERN; }, + getInputsTemplatesPath: (pkgName: string, pkgVersion: string) => { + return EPM_API_ROUTES.INPUTS_PATTERN.replace('{pkgName}', pkgName).replace( + '{pkgVersion}', + pkgVersion + ); + }, }; export const packagePolicyRouteService = { diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index a653bbfb8b223..4e5d6250208a5 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -52,6 +52,7 @@ export interface AgentPolicy extends Omit { revision: number; agents?: number; is_protected: boolean; + keep_monitoring_alive?: boolean; } export interface FullAgentPolicyInputStream { @@ -87,6 +88,14 @@ export type FullAgentPolicyOutput = Pick [key: string]: any; }; +export interface FullAgentPolicyMonitoring { + namespace?: string; + use_output?: string; + enabled: boolean; + metrics: boolean; + logs: boolean; +} + export interface FullAgentPolicy { id: string; outputs: { @@ -103,13 +112,7 @@ export interface FullAgentPolicy { inputs: FullAgentPolicyInput[]; revision?: number; agent?: { - monitoring: { - namespace?: string; - use_output?: string; - enabled: boolean; - metrics: boolean; - logs: boolean; - }; + monitoring: FullAgentPolicyMonitoring; download: { sourceURI: string }; features: Record; protection?: { diff --git a/x-pack/plugins/fleet/common/types/rest_spec/epm.ts b/x-pack/plugins/fleet/common/types/rest_spec/epm.ts index fcec6fc0f8fa5..a71f3a4b695cf 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/epm.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/epm.ts @@ -209,3 +209,19 @@ export interface GetBulkAssetsRequest { export interface GetBulkAssetsResponse { items: SimpleSOAssetType[]; } + +export interface GetInputsTemplatesRequest { + params: { + pkgName: string; + pkgVersion: string; + }; + query: { + format: 'json' | 'yml' | 'yaml'; + }; +} + +export type GetInputsTemplatesResponse = + | string + | { + inputs: any; + }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/configs/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/configs/index.tsx new file mode 100644 index 0000000000000..5fb838637ade4 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/configs/index.tsx @@ -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 React from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiCodeBlock, + EuiSpacer, + EuiSkeletonText, + EuiCallOut, + EuiLink, + EuiCode, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { PackageInfo } from '../../../../../types'; + +import { useGetInputsTemplatesQuery, useStartServices } from '../../../../../hooks'; + +interface ConfigsProps { + packageInfo: PackageInfo; +} + +export const Configs: React.FC = ({ packageInfo }) => { + const { notifications, docLinks } = useStartServices(); + const { name: pkgName, version: pkgVersion, title: pkgTitle } = packageInfo; + const notInstalled = packageInfo.status !== 'installing'; + + const { + data: configs, + error, + isLoading, + } = useGetInputsTemplatesQuery({ pkgName, pkgVersion }, { format: 'yaml' }); + + if (error) { + notifications.toasts.addError(error, { + title: i18n.translate('xpack.fleet.epm.InputTemplates.loadingErro', { + defaultMessage: 'Error input templates', + }), + }); + } + + return ( + + + + {isLoading && !configs ? ( + + ) : ( + <> + +

+ elastic-agent.yml, + inputsDir: inputs.d, + userGuideLink: ( + + + + ), + }} + /> +

+
+ {notInstalled && ( + <> + + + } + color="warning" + iconType="warning" + /> + + )} + + + {configs} + + + )} +
+
+ ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index b803e463d22fe..463dd453f8f86 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -83,6 +83,7 @@ import { PackagePoliciesPage } from './policies'; import { SettingsPage } from './settings'; import { CustomViewPage } from './custom'; import { DocumentationPage } from './documentation'; +import { Configs } from './configs'; import './index.scss'; @@ -92,7 +93,8 @@ export type DetailViewPanelName = | 'assets' | 'settings' | 'custom' - | 'api-reference'; + | 'api-reference' + | 'configs'; export interface DetailParams { pkgkey: string; @@ -625,6 +627,24 @@ export function Detail() { }); } + if (canReadPackageSettings) { + tabs.push({ + id: 'configs', + name: ( + + ), + isSelected: panel === 'configs', + 'data-test-subj': `tab-configs`, + href: getHref('integration_details_configs', { + pkgkey: packageInfoKey, + ...(integration ? { integration } : {}), + }), + }); + } + if (canReadPackageSettings && showCustomTab) { tabs.push({ id: 'custom', @@ -746,6 +766,9 @@ export function Detail() { + + + diff --git a/x-pack/plugins/fleet/public/constants/page_paths.ts b/x-pack/plugins/fleet/public/constants/page_paths.ts index cfcd2355e6a3d..9601198112ab3 100644 --- a/x-pack/plugins/fleet/public/constants/page_paths.ts +++ b/x-pack/plugins/fleet/public/constants/page_paths.ts @@ -34,6 +34,7 @@ export type DynamicPage = | 'integration_details_custom' | 'integration_details_language_clients' | 'integration_details_api_reference' + | 'integration_details_configs' | 'integration_policy_edit' | 'integration_policy_upgrade' | 'policy_details' @@ -101,6 +102,7 @@ export const INTEGRATIONS_ROUTING_PATHS = { integration_details_policies: '/detail/:pkgkey/policies', integration_details_assets: '/detail/:pkgkey/assets', integration_details_settings: '/detail/:pkgkey/settings', + integration_details_configs: '/detail/:pkgkey/configs', integration_details_custom: '/detail/:pkgkey/custom', integration_details_api_reference: '/detail/:pkgkey/api-reference', integration_details_language_clients: '/language_clients/:pkgkey/overview', @@ -166,6 +168,10 @@ export const pagePathGetters: { INTEGRATIONS_BASE_PATH, `/detail/${pkgkey}/settings${integration ? `?integration=${integration}` : ''}`, ], + integration_details_configs: ({ pkgkey, integration }) => [ + INTEGRATIONS_BASE_PATH, + `/detail/${pkgkey}/configs${integration ? `?integration=${integration}` : ''}`, + ], integration_details_custom: ({ pkgkey, integration }) => [ INTEGRATIONS_BASE_PATH, `/detail/${pkgkey}/custom${integration ? `?integration=${integration}` : ''}`, diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts index 24a36f969247d..03b2b5973c98a 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts @@ -26,6 +26,8 @@ import type { GetBulkAssetsRequest, GetBulkAssetsResponse, GetVerificationKeyIdResponse, + GetInputsTemplatesRequest, + GetInputsTemplatesResponse, } from '../../types'; import type { FleetErrorResponse, GetStatsResponse } from '../../../common/types'; import { API_VERSIONS } from '../../../common/constants'; @@ -313,3 +315,19 @@ export const sendGetBulkAssets = (body: GetBulkAssetsRequest['body']) => { body, }); }; + +export function useGetInputsTemplatesQuery( + { pkgName, pkgVersion }: GetInputsTemplatesRequest['params'], + query: GetInputsTemplatesRequest['query'] +) { + return useQuery( + ['inputsTemplates', pkgName, pkgVersion, query], + () => + sendRequestForRq({ + path: epmRouteService.getInputsTemplatesPath(pkgName, pkgVersion), + method: 'get', + query, + version: API_VERSIONS.public.v1, + }) + ); +} diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts index 1cd2e2f798db3..8c7d3ddcf5b45 100644 --- a/x-pack/plugins/fleet/public/types/index.ts +++ b/x-pack/plugins/fleet/public/types/index.ts @@ -135,6 +135,8 @@ export type { GetBulkAssetsRequest, GetBulkAssetsResponse, KibanaSavedObjectType, + GetInputsTemplatesRequest, + GetInputsTemplatesResponse, } from '../../common/types'; export { entries, diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 9bf37677ae905..8791d9871982b 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -147,6 +147,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, is_protected: { type: 'boolean' }, overrides: { type: 'flattened', index: false }, + keep_monitoring_alive: { type: 'boolean' }, }, }, migrations: { diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index 050b5f983c794..d9bf271210bc9 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -283,6 +283,20 @@ describe('getFullAgentPolicy', () => { }); }); + it('should return a policy with monitoring enabled but no logs/metrics if keep_monitoring_alive is true', async () => { + mockAgentPolicy({ + keep_monitoring_alive: true, + }); + + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy?.agent?.monitoring).toEqual({ + enabled: true, + logs: false, + metrics: false, + }); + }); + it('should get the permissions for monitoring', async () => { mockAgentPolicy({ namespace: 'testnamespace', diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 2cdd04483e108..d15e7adb9bd7f 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -18,7 +18,11 @@ import type { FleetProxy, FleetServerHost, } from '../../types'; -import type { FullAgentPolicyOutputPermissions, PackageInfo } from '../../../common/types'; +import type { + FullAgentPolicyMonitoring, + FullAgentPolicyOutputPermissions, + PackageInfo, +} from '../../../common/types'; import { agentPolicyService } from '../agent_policy'; import { dataTypes, kafkaCompressionType, outputType } from '../../../common/constants'; import { DEFAULT_OUTPUT } from '../../constants'; @@ -105,6 +109,35 @@ export async function getFullAgentPolicy( acc[name] = featureConfig; return acc; }, {} as NonNullable['features']); + + const defaultMonitoringConfig: FullAgentPolicyMonitoring = { + enabled: false, + logs: false, + metrics: false, + }; + + let monitoring: FullAgentPolicyMonitoring = { ...defaultMonitoringConfig }; + + // If the agent policy has monitoring enabled for at least one of "logs" or "metrics", generate + // a monitoring config for the resulting compiled agent policy + if (agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0) { + monitoring = { + namespace: agentPolicy.namespace, + use_output: getOutputIdForAgentPolicy(monitoringOutput), + enabled: true, + logs: agentPolicy.monitoring_enabled.includes(dataTypes.Logs), + metrics: agentPolicy.monitoring_enabled.includes(dataTypes.Metrics), + }; + // If the `keep_monitoring_alive` flag is set, enable monitoring but don't enable logs or metrics. + // This allows cloud or other environments to keep the monitoring server alive without tearing it down. + } else if (agentPolicy.keep_monitoring_alive) { + monitoring = { + enabled: true, + logs: false, + metrics: false, + }; + } + const fullAgentPolicy: FullAgentPolicy = { id: agentPolicy.id, outputs: { @@ -128,16 +161,7 @@ export async function getFullAgentPolicy( sourceURI: downloadSourceUri, ...(downloadSourceProxyUri ? { proxy_url: downloadSourceProxyUri } : {}), }, - monitoring: - agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0 - ? { - namespace: agentPolicy.namespace, - use_output: getOutputIdForAgentPolicy(monitoringOutput), - enabled: true, - logs: agentPolicy.monitoring_enabled.includes(dataTypes.Logs), - metrics: agentPolicy.monitoring_enabled.includes(dataTypes.Metrics), - } - : { enabled: false, logs: false, metrics: false }, + monitoring, features, protection: { enabled: agentPolicy.is_protected, diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index 0fb27475c0779..9b7e0d779cd9c 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -35,6 +35,7 @@ export const AgentPolicyBaseSchema = { schema.oneOf([schema.literal(dataTypes.Logs), schema.literal(dataTypes.Metrics)]) ) ), + keep_monitoring_alive: schema.maybe(schema.boolean({ defaultValue: false })), data_output_id: schema.maybe(schema.nullable(schema.string())), monitoring_output_id: schema.maybe(schema.nullable(schema.string())), download_source_id: schema.maybe(schema.nullable(schema.string())), diff --git a/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.test.ts b/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.test.ts index b9f5037ced6b1..5efdf9e125deb 100644 --- a/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.test.ts +++ b/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.test.ts @@ -249,7 +249,6 @@ describe('LogViewsClient class', () => { }, ], "dataViewReference": DataView { - "allowHidden": false, "allowNoIndex": false, "deleteFieldFormat": [Function], "fieldAttrs": Object {}, @@ -274,7 +273,6 @@ describe('LogViewsClient class', () => { }, "fields": FldList [], "flattenHit": [Function], - "getAllowHidden": [Function], "getFieldAttrs": [Function], "getIndexPattern": [Function], "getName": [Function], diff --git a/x-pack/plugins/observability/kibana.jsonc b/x-pack/plugins/observability/kibana.jsonc index bdb4c3ca2949e..467c31969c28a 100644 --- a/x-pack/plugins/observability/kibana.jsonc +++ b/x-pack/plugins/observability/kibana.jsonc @@ -6,7 +6,10 @@ "id": "observability", "server": true, "browser": true, - "configPath": ["xpack", "observability"], + "configPath": [ + "xpack", + "observability" + ], "requiredPlugins": [ "aiops", "alerting", @@ -32,9 +35,26 @@ "unifiedSearch", "visualizations", "dashboard", + "expressions" + ], + "optionalPlugins": [ + "discover", + "home", + "licensing", + "usageCollection", + "cloud", + "spaces" + ], + "requiredBundles": [ + "data", + "kibanaReact", + "kibanaUtils", + "unifiedSearch", + "stackAlerts", + "spaces" ], - "optionalPlugins": ["discover", "home", "licensing", "usageCollection", "cloud", "spaces"], - "requiredBundles": ["data", "kibanaReact", "kibanaUtils", "unifiedSearch", "stackAlerts", "spaces"], - "extraPublicDirs": ["common"] + "extraPublicDirs": [ + "common" + ] } -} +} \ No newline at end of file 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/common/constants/client_defaults.ts b/x-pack/plugins/synthetics/common/constants/client_defaults.ts index c330a928d8f93..07d17554e83fc 100644 --- a/x-pack/plugins/synthetics/common/constants/client_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/client_defaults.ts @@ -46,11 +46,54 @@ export const CLIENT_DEFAULTS = { export const EXCLUDE_RUN_ONCE_FILTER = { bool: { must_not: { exists: { field: 'run_once' } } } }; export const FINAL_SUMMARY_FILTER = { - term: { - 'summary.final_attempt': true, + bool: { + filter: [ + { + exists: { + field: 'summary', + }, + }, + { + bool: { + should: [ + { + bool: { + should: [ + { + match: { + 'summary.final_attempt': true, + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + must_not: { + bool: { + should: [ + { + exists: { + field: 'summary.final_attempt', + }, + }, + ], + minimum_should_match: 1, + }, + }, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], }, }; +export const SUMMARY_FILTER = { exists: { field: 'summary' } }; + export const getLocationFilter = ({ locationName, locationId, diff --git a/x-pack/plugins/synthetics/e2e/synthetics/journeys/index.ts b/x-pack/plugins/synthetics/e2e/synthetics/journeys/index.ts index 63f460ec56771..40418a55e8d8e 100644 --- a/x-pack/plugins/synthetics/e2e/synthetics/journeys/index.ts +++ b/x-pack/plugins/synthetics/e2e/synthetics/journeys/index.ts @@ -18,7 +18,7 @@ export * from './alerting_default.journey'; export * from './global_parameters.journey'; export * from './detail_flyout'; // export * from './alert_rules/default_status_alert.journey'; -// export * from './test_now_mode.journey'; +export * from './test_now_mode.journey'; // export * from './data_retention.journey'; export * from './monitor_details_page/monitor_summary.journey'; export * from './test_run_details.journey'; diff --git a/x-pack/plugins/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts b/x-pack/plugins/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts index 3c99b173f781f..9df533b4df2ed 100644 --- a/x-pack/plugins/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts +++ b/x-pack/plugins/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts @@ -31,7 +31,7 @@ journey(`TestNowMode`, async ({ page, params }) => { if ( evt.resourceType() === 'fetch' && (evt.url().includes('service/monitors/trigger/') || - evt.url().includes('uptime/service/monitors/run_once')) + evt.url().includes('synthetics/service/monitors/run_once')) ) { evt .response() @@ -139,7 +139,7 @@ journey(`TestNowMode`, async ({ page, params }) => { await page.waitForSelector( '.euiTableRowCell--hideForMobile :has-text("Go to https://www.google.com")' ); - await page.waitForSelector('.euiTableRowCell--hideForMobile :has-text("1.42 s")'); + expect(await page.getByTestId('stepDurationText1').first()).toHaveText('1.4 sec'); await page.waitForSelector('text=Complete'); }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/step_duration_text.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/step_duration_text.tsx index 68f5d919f90e3..7bee1f0d01887 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/step_duration_text.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/step_duration_text.tsx @@ -29,7 +29,11 @@ export const StepDurationText = ({ step }: { step: JourneyStep }) => { }, [euiTheme.colors, step.synthetics.step?.duration?.us, step.synthetics.step?.status]); return ( - + {stepDuration.text} ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/error_duration.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/error_duration.tsx index bac7b09ecbf84..fb423aff733c4 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/error_duration.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/error_duration.tsx @@ -9,8 +9,8 @@ import { EuiDescriptionList } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import moment, { Moment } from 'moment'; +import { useErrorFailedTests } from '../hooks/use_error_failed_tests'; import { useFindMyKillerState } from '../hooks/use_find_my_killer_state'; -import { useErrorFailedTests } from '../hooks/use_last_error_state'; export const ErrorDuration: React.FC = () => { const { failedTests } = useErrorFailedTests(); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/error_started_at.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/error_started_at.tsx index 69331b8ebdb08..24eb3b2afd5e4 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/error_started_at.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/error_started_at.tsx @@ -8,7 +8,7 @@ import React, { ReactElement } from 'react'; import { EuiDescriptionList, EuiSkeletonText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useErrorFailedTests } from '../hooks/use_last_error_state'; +import { useErrorFailedTests } from '../hooks/use_error_failed_tests'; import { useDateFormat } from '../../../../../hooks/use_date_format'; export const ErrorStartedAt: React.FC = () => { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/failed_tests_list.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/failed_tests_list.tsx index db59766c4c585..4376680335e81 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/failed_tests_list.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/failed_tests_list.tsx @@ -39,6 +39,8 @@ export const FailedTestsList = ({ const formatter = useDateFormat(); + // TODO: this is broken for ping monitors + const columns = [ { field: '@timestamp', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_error_failed_tests.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_error_failed_tests.tsx index eb5268c506089..18d4ae204dfbf 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_error_failed_tests.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_error_failed_tests.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import { useEsSearch } from '@kbn/observability-shared-plugin/public'; import { useParams } from 'react-router-dom'; import { useMemo } from 'react'; +import { useReduxEsSearch } from '../../../hooks/use_redux_es_search'; import { Ping } from '../../../../../../common/runtime_types'; import { EXCLUDE_RUN_ONCE_FILTER, - FINAL_SUMMARY_FILTER, + SUMMARY_FILTER, } from '../../../../../../common/constants/client_defaults'; import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; import { useSyntheticsRefreshContext } from '../../../contexts'; @@ -24,7 +24,7 @@ export function useErrorFailedTests() { const { dateRangeStart, dateRangeEnd } = useGetUrlParams(); - const { data, loading } = useEsSearch( + const { data, loading } = useReduxEsSearch( { index: SYNTHETICS_INDEX_PATTERN, body: { @@ -32,7 +32,7 @@ export function useErrorFailedTests() { query: { bool: { filter: [ - FINAL_SUMMARY_FILTER, + SUMMARY_FILTER, EXCLUDE_RUN_ONCE_FILTER, { term: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_last_error_state.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_last_error_state.tsx deleted file mode 100644 index d86b41f08f5bb..0000000000000 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_last_error_state.tsx +++ /dev/null @@ -1,68 +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 { useParams } from 'react-router-dom'; -import { useMemo } from 'react'; -import { useReduxEsSearch } from '../../../hooks/use_redux_es_search'; -import { Ping } from '../../../../../../common/runtime_types'; -import { - EXCLUDE_RUN_ONCE_FILTER, - FINAL_SUMMARY_FILTER, -} from '../../../../../../common/constants/client_defaults'; -import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; -import { useSyntheticsRefreshContext } from '../../../contexts'; -import { useGetUrlParams } from '../../../hooks'; - -export function useErrorFailedTests() { - const { lastRefresh } = useSyntheticsRefreshContext(); - - const { errorStateId, monitorId } = useParams<{ errorStateId: string; monitorId: string }>(); - - const { dateRangeStart, dateRangeEnd } = useGetUrlParams(); - - const { data, loading } = useReduxEsSearch( - { - index: SYNTHETICS_INDEX_PATTERN, - body: { - size: 1000, - query: { - bool: { - filter: [ - FINAL_SUMMARY_FILTER, - EXCLUDE_RUN_ONCE_FILTER, - { - term: { - 'state.id': errorStateId, - }, - }, - { - term: { - config_id: monitorId, - }, - }, - ], - }, - }, - sort: [{ '@timestamp': 'desc' }], - }, - }, - [lastRefresh, monitorId, dateRangeStart, dateRangeEnd], - { name: 'getMonitorErrorFailedTests' } - ); - - return useMemo(() => { - const failedTests = - data?.hits.hits?.map((doc) => { - return doc._source as Ping; - }) ?? []; - - return { - failedTests, - loading, - }; - }, [data, loading]); -} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx index 69871de54c7af..969e98a21c36c 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx @@ -11,7 +11,7 @@ import { useSelectedLocation } from './use_selected_location'; import { Ping, PingState } from '../../../../../../common/runtime_types'; import { EXCLUDE_RUN_ONCE_FILTER, - FINAL_SUMMARY_FILTER, + SUMMARY_FILTER, } from '../../../../../../common/constants/client_defaults'; import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; import { useSyntheticsRefreshContext } from '../../../contexts'; @@ -37,7 +37,7 @@ export function useMonitorErrors(monitorIdArg?: string) { query: { bool: { filter: [ - FINAL_SUMMARY_FILTER, + SUMMARY_FILTER, EXCLUDE_RUN_ONCE_FILTER, { range: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_cell_tooltip.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_cell_tooltip.tsx index a6dc7956280ce..a8d027889dbb0 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_cell_tooltip.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_cell_tooltip.tsx @@ -11,13 +11,16 @@ import { EuiProgress } from '@elastic/eui'; import { TooltipTable, TooltipHeader, TooltipValue, TooltipContainer } from '@elastic/charts'; -import { usePingStatusesIsLoading } from '../hooks/use_ping_statuses'; import { MonitorStatusTimeBin, SUCCESS_VIZ_COLOR, DANGER_VIZ_COLOR } from './monitor_status_data'; import * as labels from './labels'; -export const MonitorStatusCellTooltip = ({ timeBin }: { timeBin?: MonitorStatusTimeBin }) => { - const isLoading = usePingStatusesIsLoading(); - +export const MonitorStatusCellTooltip = ({ + timeBin, + isLoading, +}: { + timeBin?: MonitorStatusTimeBin; + isLoading: boolean; +}) => { if (!timeBin) { return <>{''}; } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx index 4f67b8621104c..cbbdae88c0275 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx @@ -10,6 +10,8 @@ import React, { useMemo } from 'react'; import { EuiPanel, useEuiTheme, EuiResizeObserver, EuiSpacer } from '@elastic/eui'; import { Chart, Settings, Heatmap, ScaleType, Tooltip } from '@elastic/charts'; +import { usePingStatusesIsLoading } from '../hooks/use_ping_statuses'; + import { MonitorStatusHeader } from './monitor_status_header'; import { MonitorStatusCellTooltip } from './monitor_status_cell_tooltip'; import { MonitorStatusLegend } from './monitor_status_legend'; @@ -33,6 +35,7 @@ export const MonitorStatusPanel = ({ const { euiTheme, colorMode } = useEuiTheme(); const { timeBins, handleResize, getTimeBinByXValue, xDomain, intervalByWidth } = useMonitorStatusData({ from, to }); + const isPingStatusesLoading = usePingStatusesIsLoading(); const heatmap = useMemo(() => { return getMonitorStatusChartTheme(euiTheme, brushable); @@ -61,7 +64,10 @@ export const MonitorStatusPanel = ({ > ( - + )} /> = N pings for a journey - * of N steps, because an individual step may also contain multiple documents. - */ -const REMOVE_NON_SUMMARY_BROWSER_CHECKS = { - must_not: [ - { - bool: { - filter: [ - { - term: { - 'monitor.type': 'browser', - }, - }, - { - bool: { - must_not: [ - { - exists: { - field: 'summary', - }, - }, - ], - }, - }, - ], - }, - }, - ], -}; - function isStringArray(value: unknown): value is string[] { if (!Array.isArray(value)) return false; // are all array items strings @@ -72,14 +36,6 @@ type GetParamsWithFields = GetPingsParams & { type GetParamsWithoutFields = GetPingsParams; -export function queryPings( - params: GetPingsParams & { uptimeEsClient: UptimeEsClient } -): Promise; - -export function queryPings( - params: GetParamsWithFields & { uptimeEsClient: UptimeEsClient } -): Promise<{ total: number; pings: F[] }>; - export async function queryPings( params: (GetParamsWithFields | GetParamsWithoutFields) & { uptimeEsClient: UptimeEsClient } ): Promise { @@ -105,12 +61,12 @@ export async function queryPings( query: { bool: { filter: [ + SUMMARY_FILTER, { range: { '@timestamp': { gte: from, lte: to } } }, ...(monitorId ? [{ term: { 'monitor.id': monitorId } }] : []), ...(status ? [{ term: { 'monitor.status': status } }] : []), ...(finalAttempt ? [{ term: { 'summary.final_attempt': finalAttempt } }] : []), ] as QueryDslQueryContainer[], - ...REMOVE_NON_SUMMARY_BROWSER_CHECKS, }, }, sort: [{ '@timestamp': { order: (sort ?? 'desc') as 'asc' | 'desc' } }], diff --git a/x-pack/test/accessibility/apps/canvas.ts b/x-pack/test/accessibility/apps/canvas.ts index 231bd1c161fde..f3dfa9305fb95 100644 --- a/x-pack/test/accessibility/apps/canvas.ts +++ b/x-pack/test/accessibility/apps/canvas.ts @@ -10,16 +10,22 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const testSubjects = getService('testSubjects'); - const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const retry = getService('retry'); const { canvas } = getPageObjects(['canvas']); describe('Canvas Accessibility', () => { before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/canvas/default'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/canvas/default' + ); await canvas.goToListingPage(); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('loads workpads', async function () { await retry.waitFor( 'canvas workpads visible', diff --git a/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts b/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts index 9ae541c834714..7d3cf599558f4 100644 --- a/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts +++ b/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts @@ -17,7 +17,8 @@ export default function ({ getService }: FtrProviderContext) { id: model.name, })); - describe('trained models', function () { + // FLAKY: https://github.com/elastic/kibana/issues/165084 + describe.skip('trained models', function () { // 'Created at' will be different on each run, // so we will just assert that the value is in the expected timestamp format. const builtInModelData = { @@ -453,7 +454,8 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToTrainedModels(); }); - describe('with imported models', function () { + // FLAKY: https://github.com/elastic/kibana/issues/168899 + describe.skip('with imported models', function () { before(async () => { await ml.navigation.navigateToTrainedModels(); }); diff --git a/x-pack/test/functional/es_archives/canvas/default/data.json.gz b/x-pack/test/functional/es_archives/canvas/default/data.json.gz deleted file mode 100644 index ff26856f3d704..0000000000000 Binary files a/x-pack/test/functional/es_archives/canvas/default/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/canvas/default/mappings.json b/x-pack/test/functional/es_archives/canvas/default/mappings.json deleted file mode 100644 index 61481a0f949fa..0000000000000 --- a/x-pack/test/functional/es_archives/canvas/default/mappings.json +++ /dev/null @@ -1,314 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "dynamic": "strict", - "properties": { - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "id": { - "index": false, - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "migrationVersion": { - "dynamic": "true", - "type": "object" - }, - "namespace": { - "type": "keyword" - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "initials": { - "type": "keyword" - }, - "disabledFeatures": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "telemetry": { - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchId": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} \ No newline at end of file diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts index 57bae974ce273..cec6ed8528a10 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts @@ -123,6 +123,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 1920, 1400 ); + await ml.alerting.selectSlackConnectorType(); + await ml.testExecution.logTestStep('should open connectors'); + await ml.alerting.clickCreateConnectorButton(); + await ml.alerting.setConnectorName('test-connector'); + await ml.alerting.setWebhookUrl('https://www.elastic.co'); + await ml.alerting.clickSaveActionButton(); + await commonScreenshots.takeScreenshot( + 'ml-health-check-action', + screenshotDirectories, + 1920, + 1400 + ); await ml.alerting.clickCancelSaveRuleButton(); await pageObjects.triggersActionsUI.clickCreateAlertButton(); @@ -150,12 +162,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 1920, 1400 ); - await ml.alerting.selectSlackConnectorType(); - await ml.testExecution.logTestStep('should open connectors'); - await ml.alerting.clickCreateConnectorButton(); - await ml.alerting.setConnectorName('test-connector'); - await ml.alerting.setWebhookUrl('https://www.elastic.co'); - await ml.alerting.clickSaveActionButton(); + await testSubjects.click('.slack-alerting-ActionTypeSelectOption'); + await commonScreenshots.takeScreenshot( + 'ml-anomaly-alert-action-score-matched', + screenshotDirectories, + 1920, + 1400 + ); await ml.alerting.openAddRuleVariable(); await ml.testExecution.logTestStep('take screenshot'); await commonScreenshots.takeScreenshot( @@ -164,6 +177,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 1920, 1400 ); + const actionFrequency = await testSubjects.find('summaryOrPerRuleSelect'); + await actionFrequency.click(); + const actionSummary = await testSubjects.find('actionNotifyWhen-option-summary'); + await actionSummary.click(); + await commonScreenshots.takeScreenshot( + 'ml-anomaly-alert-action-summary', + screenshotDirectories, + 1920, + 1400 + ); }); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/enable_risk_score_redirect.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/enable_risk_score_redirect.cy.ts index a684968bf8b33..43fc7c399593c 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/enable_risk_score_redirect.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/enable_risk_score_redirect.cy.ts @@ -20,34 +20,30 @@ import { ENTITY_ANALYTICS_URL } from '../../../urls/navigation'; import { PAGE_TITLE } from '../../../screens/entity_analytics_management'; // FLAKY: https://github.com/elastic/kibana/issues/165644 -describe( - 'Enable risk scores from dashboard', - { tags: ['@ess', '@serverless', '@brokenInServerless'] }, - () => { - before(() => { - cleanKibana(); - login(); - }); +describe('Enable risk scores from dashboard', { tags: ['@ess', '@serverless'] }, () => { + before(() => { + cleanKibana(); + login(); + }); - beforeEach(() => { - login(); - visit(ENTITY_ANALYTICS_URL); - }); + beforeEach(() => { + login(); + visit(ENTITY_ANALYTICS_URL); + }); - it('host risk enable button should redirect to entity management page', () => { - cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('exist'); + it('host risk enable button should redirect to entity management page', () => { + cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('exist'); - clickEnableRiskScore(RiskScoreEntity.host); + clickEnableRiskScore(RiskScoreEntity.host); - cy.get(PAGE_TITLE).should('have.text', 'Entity Risk Score'); - }); + cy.get(PAGE_TITLE).should('have.text', 'Entity Risk Score'); + }); - it('user risk enable button should redirect to entity management page', () => { - cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('exist'); + it('user risk enable button should redirect to entity management page', () => { + cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('exist'); - clickEnableRiskScore(RiskScoreEntity.user); + clickEnableRiskScore(RiskScoreEntity.user); - cy.get(PAGE_TITLE).should('have.text', 'Entity Risk Score'); - }); - } -); + cy.get(PAGE_TITLE).should('have.text', 'Entity Risk Score'); + }); +}); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts index 92fad4effbd4a..3ec2943223a81 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts @@ -46,8 +46,8 @@ import { getNewRule } from '../../../objects/rule'; import { clickOnFirstHostsAlerts, clickOnFirstUsersAlerts } from '../../../tasks/risk_scores'; import { OPTION_LIST_LABELS, OPTION_LIST_VALUES } from '../../../screens/common/filter_group'; import { setRowsPerPageTo } from '../../../tasks/table_pagination'; -import { clearSearchBar, kqlSearch } from '../../../tasks/security_header'; -import { setEndDate, setEndDateNow, updateDates } from '../../../tasks/date_picker'; +import { kqlSearch } from '../../../tasks/security_header'; +import { setEndDate, updateDates } from '../../../tasks/date_picker'; import { enableJob, navigateToNextPage, @@ -62,7 +62,7 @@ const SIEM_KIBANA_HOST_ALERTS = 2; const SIEM_KIBANA_HOST_NAME = 'siem-kibana'; const END_DATE = 'Jan 19, 2019 @ 20:33:29.186'; -describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] }, () => { +describe('Entity Analytics Dashboard', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); login(); @@ -177,8 +177,6 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } cy.get(HOSTS_DONUT_CHART).should('include.text', '1Total'); cy.get(HOSTS_TABLE_ROWS).should('have.length', 1); - - clearSearchBar(); }); describe('With alerts data', () => { @@ -206,10 +204,6 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } updateDates(); cy.get(HOSTS_TABLE_ALERT_CELL).first().should('include.text', 0); - - // CLEAR DATES - setEndDateNow(); - updateDates(); }); it('opens alerts page when alerts count is clicked', () => { @@ -265,8 +259,6 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } cy.get(USERS_DONUT_CHART).should('include.text', '1Total'); cy.get(USERS_TABLE_ROWS).should('have.length', 1); - - clearSearchBar(); }); describe('With alerts data', () => { @@ -294,10 +286,6 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } updateDates(); cy.get(USERS_TABLE_ALERT_CELL).first().should('include.text', 0); - - // CLEAR DATES - setEndDateNow(); - updateDates(); }); it('opens alerts page when alerts count is clicked', () => { @@ -398,8 +386,6 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } cy.get(HOSTS_DONUT_CHART).should('include.text', '1Total'); cy.get(HOSTS_TABLE_ROWS).should('have.length', 1); - - clearSearchBar(); }); describe('With alerts data', () => { @@ -427,10 +413,6 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } updateDates(); cy.get(HOSTS_TABLE_ALERT_CELL).first().should('include.text', 0); - - // CLEAR DATES - setEndDateNow(); - updateDates(); }); it('opens alerts page when alerts count is clicked', () => { @@ -491,8 +473,6 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } cy.get(USERS_DONUT_CHART).should('include.text', '1Total'); cy.get(USERS_TABLE_ROWS).should('have.length', 1); - - clearSearchBar(); }); describe('With alerts data', () => { @@ -520,10 +500,6 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } updateDates(); cy.get(USERS_TABLE_ALERT_CELL).first().should('include.text', 0); - - // CLEAR DATES - setEndDateNow(); - updateDates(); }); it('opens alerts page when alerts count is clicked', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics_serverless_splash_screen.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics_serverless_splash_screen.cy.ts index 94d77b45b157b..fcf869f3b0f63 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics_serverless_splash_screen.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics_serverless_splash_screen.cy.ts @@ -16,7 +16,7 @@ import { PAYWALL_DESCRIPTION } from '../../../screens/entity_analytics_serverles describe( 'Entity Analytics Dashboard in Serverless', { - tags: ['@serverless', '@brokenInServerless'], + tags: ['@serverless'], env: { ftrConfig: { productTypes: [ diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/upgrade_risk_score.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/upgrade_risk_score.cy.ts index e815e9eaa6cb4..55376429f8edc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/upgrade_risk_score.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/upgrade_risk_score.cy.ts @@ -36,8 +36,7 @@ import { deleteRiskEngineConfiguration } from '../../../tasks/api_calls/risk_eng const spaceId = 'default'; -// Flaky on serverless -describe('Upgrade risk scores', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { +describe('Upgrade risk scores', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); login(); @@ -72,18 +71,15 @@ describe('Upgrade risk scores', { tags: ['@ess', '@serverless', '@brokenInServer }); describe('upgrade risk engine', () => { - before(() => { + beforeEach(() => { cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); cy.task('esArchiverLoad', { archiveName: 'risk_users' }); - }); - - beforeEach(() => { login(); installRiskScoreModule(); visitWithTimeRange(ENTITY_ANALYTICS_URL); }); - after(() => { + afterEach(() => { cy.task('esArchiverUnload', 'risk_hosts'); cy.task('esArchiverUnload', 'risk_users'); deleteRiskScore({ riskScoreEntity: RiskScoreEntity.host, spaceId }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/date_picker.ts b/x-pack/test/security_solution_cypress/cypress/tasks/date_picker.ts index 1ea45dcd0b91b..7ae1fa8c30b73 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/date_picker.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/date_picker.ts @@ -31,9 +31,9 @@ export const setEndDateNow = (container: string = GLOBAL_FILTERS_CONTAINER) => { export const setEndDate = (date: string, container: string = GLOBAL_FILTERS_CONTAINER) => { cy.get(GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON(container)).first().click(); - cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click(); + cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click({ force: true }); - cy.get(DATE_PICKER_ABSOLUTE_INPUT).click(); + cy.get(DATE_PICKER_ABSOLUTE_INPUT).click({ force: true }); cy.get(DATE_PICKER_ABSOLUTE_INPUT).then(($el) => { if (Cypress.dom.isAttached($el)) { cy.wrap($el).click({ force: true }); @@ -57,7 +57,7 @@ export const setStartDate = (date: string, container: string = GLOBAL_FILTERS_CO }; export const updateDates = (container: string = GLOBAL_FILTERS_CONTAINER) => { - cy.get(GET_DATE_PICKER_APPLY_BUTTON(container)).click(); + cy.get(GET_DATE_PICKER_APPLY_BUTTON(container)).click({ force: true }); cy.get(GET_DATE_PICKER_APPLY_BUTTON(container)).should('not.have.text', 'Updating'); }; diff --git a/x-pack/test/security_solution_cypress/es_archives/risk_hosts/mappings.json b/x-pack/test/security_solution_cypress/es_archives/risk_hosts/mappings.json index 3e1b52cb22f5e..e250700644c15 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risk_hosts/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/risk_hosts/mappings.json @@ -31,10 +31,6 @@ }, "settings": { "index": { - "lifecycle": { - "name": "ml_host_risk_score_latest_default", - "rollover_alias": "ml_host_risk_score_latest_default" - }, "mapping": { "total_fields": { "limit": "10000" @@ -83,10 +79,6 @@ }, "settings": { "index": { - "lifecycle": { - "name": "ml_host_risk_score_default", - "rollover_alias": "ml_host_risk_score_default" - }, "mapping": { "total_fields": { "limit": "10000" diff --git a/x-pack/test/security_solution_cypress/es_archives/risk_hosts_no_data/mappings.json b/x-pack/test/security_solution_cypress/es_archives/risk_hosts_no_data/mappings.json index 3e1b52cb22f5e..e250700644c15 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risk_hosts_no_data/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/risk_hosts_no_data/mappings.json @@ -31,10 +31,6 @@ }, "settings": { "index": { - "lifecycle": { - "name": "ml_host_risk_score_latest_default", - "rollover_alias": "ml_host_risk_score_latest_default" - }, "mapping": { "total_fields": { "limit": "10000" @@ -83,10 +79,6 @@ }, "settings": { "index": { - "lifecycle": { - "name": "ml_host_risk_score_default", - "rollover_alias": "ml_host_risk_score_default" - }, "mapping": { "total_fields": { "limit": "10000" diff --git a/x-pack/test/security_solution_cypress/es_archives/risk_users/mappings.json b/x-pack/test/security_solution_cypress/es_archives/risk_users/mappings.json index 77eade9df7994..2591f2d216575 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risk_users/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/risk_users/mappings.json @@ -31,10 +31,6 @@ }, "settings": { "index": { - "lifecycle": { - "name": "ml_user_risk_score_latest_default", - "rollover_alias": "ml_user_risk_score_latest_default" - }, "mapping": { "total_fields": { "limit": "10000" @@ -83,10 +79,6 @@ }, "settings": { "index": { - "lifecycle": { - "name": "ml_user_risk_score_default", - "rollover_alias": "ml_user_risk_score_default" - }, "mapping": { "total_fields": { "limit": "10000" diff --git a/x-pack/test/security_solution_cypress/es_archives/risk_users_no_data/mappings.json b/x-pack/test/security_solution_cypress/es_archives/risk_users_no_data/mappings.json index 77eade9df7994..2591f2d216575 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risk_users_no_data/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/risk_users_no_data/mappings.json @@ -31,10 +31,6 @@ }, "settings": { "index": { - "lifecycle": { - "name": "ml_user_risk_score_latest_default", - "rollover_alias": "ml_user_risk_score_latest_default" - }, "mapping": { "total_fields": { "limit": "10000" @@ -83,10 +79,6 @@ }, "settings": { "index": { - "lifecycle": { - "name": "ml_user_risk_score_default", - "rollover_alias": "ml_user_risk_score_default" - }, "mapping": { "total_fields": { "limit": "10000" diff --git a/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts b/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts index fb6c22a761f1e..bedc8a52409b6 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts @@ -51,7 +51,7 @@ export const EXPECTED_RESULTS: Record { ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, spaces: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], - spacesWithMatchingOrigins: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], + // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) inboundReferences: [{ type: 'sharedtype', id: CASES.DEFAULT_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in the default space }, { @@ -65,7 +65,7 @@ export const EXPECTED_RESULTS: Record type: 'sharedtype', id: CASES.DEFAULT_ONLY.id, spaces: [DEFAULT_SPACE_ID], - spacesWithMatchingOrigins: [DEFAULT_SPACE_ID], // The first test assertion for spacesWithMatchingOrigins is an object that doesn't have any matching origins in other spaces + // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], }, { @@ -86,7 +86,7 @@ export const EXPECTED_RESULTS: Record type: 'sharedtype', id: CASES.ALL_SPACES.id, spaces: ['*'], - spacesWithMatchingOrigins: ['*'], + // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], }, ], @@ -94,7 +94,7 @@ export const EXPECTED_RESULTS: Record { ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, spaces: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], - spacesWithMatchingOrigins: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], + // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) inboundReferences: [{ type: 'sharedtype', id: CASES.SPACE_1_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in space 1 }, { @@ -116,7 +116,7 @@ export const EXPECTED_RESULTS: Record id: CASES.SPACE_1_ONLY.id, spaces: [SPACE_1_ID], spacesWithMatchingAliases: [DEFAULT_SPACE_ID, SPACE_2_ID], // aliases with a matching targetType and sourceId exist in two other spaces - spacesWithMatchingOrigins: ['other_space', SPACE_1_ID], // The second test assertion for spacesWithMatchingOrigins is an object that has a matching origin in one other space + spacesWithMatchingOrigins: ['other_space'], // The second test assertion for spacesWithMatchingOrigins is an object that has a matching origin in one other space inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], }, { @@ -130,7 +130,7 @@ export const EXPECTED_RESULTS: Record type: 'sharedtype', id: CASES.ALL_SPACES.id, spaces: ['*'], - spacesWithMatchingOrigins: ['*'], + // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], }, ], @@ -138,7 +138,7 @@ export const EXPECTED_RESULTS: Record { ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, spaces: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], - spacesWithMatchingOrigins: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], + // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) inboundReferences: [{ type: 'sharedtype', id: CASES.SPACE_2_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in space 2 }, { @@ -173,7 +173,7 @@ export const EXPECTED_RESULTS: Record type: 'sharedtype', id: CASES.ALL_SPACES.id, spaces: ['*'], - spacesWithMatchingOrigins: ['*'], + // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], }, ], diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/compliance_dashboard.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/compliance_dashboard.ts index 54fa5a725e29a..6a6734b8f3fe3 100644 --- a/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/compliance_dashboard.ts +++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/compliance_dashboard.ts @@ -56,7 +56,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.svlCommonPage.forceLogout(); }); - describe('Kubernetes Dashboard', () => { + // FLAKY: https://github.com/elastic/kibana/issues/168904 + describe.skip('Kubernetes Dashboard', () => { it('displays accurate summary compliance score', async () => { const scoreElement = await dashboard.getKubernetesComplianceScore(); diff --git a/yarn.lock b/yarn.lock index d0fcadff0f1b6..d704bf985e9ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4366,6 +4366,10 @@ version "0.0.0" uid "" +"@kbn/eslint-plugin-i18n@link:packages/kbn-eslint-plugin-i18n": + version "0.0.0" + uid "" + "@kbn/eslint-plugin-imports@link:packages/kbn-eslint-plugin-imports": version "0.0.0" uid ""