From 6740ffb8761097a35dd9be7b353fff33eef3d0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Mon, 6 May 2024 21:25:25 +0200 Subject: [PATCH 1/4] Fix flaky licensing test #148313 (#182712) --- x-pack/test/licensing_plugin/scenario.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/x-pack/test/licensing_plugin/scenario.ts b/x-pack/test/licensing_plugin/scenario.ts index 051a937d98d35..8c452103f9a7c 100644 --- a/x-pack/test/licensing_plugin/scenario.ts +++ b/x-pack/test/licensing_plugin/scenario.ts @@ -17,6 +17,7 @@ export function createScenario({ getService, getPageObjects }: FtrProviderContex const esSupertestWithoutAuth = getService('esSupertestWithoutAuth'); const security = getService('security'); const PageObjects = getPageObjects(['common', 'security']); + const retry = getService('retry'); const scenario = { async setup() { @@ -109,9 +110,27 @@ export function createScenario({ getService, getPageObjects }: FtrProviderContex }, async waitForPluginToDetectLicenseUpdate() { + const { + body: { license: esLicense }, + } = await esSupertestWithoutAuth + .get('/_license') + .auth('license_manager_user', 'license_manager_user-password') + .expect(200); // > --xpack.licensing.api_polling_frequency set in test config // to wait for Kibana server to re-fetch the license from Elasticsearch - await delay(500); + const pollingFrequency = 500; + + await retry.waitForWithTimeout( + 'waiting for the license.uid to match ES', + 4 * pollingFrequency, + async () => { + const { + body: { license: kbLicense }, + } = await supertest.get('/api/licensing/info').expect(200); + return kbLicense?.uid === esLicense?.uid; + }, + () => delay(pollingFrequency) + ); }, }; return scenario; From 1d8513855757f3bfd272953cbaba419bb2c09907 Mon Sep 17 00:00:00 2001 From: seanrathier Date: Mon, 6 May 2024 15:33:15 -0400 Subject: [PATCH 2/4] [Cloud Security] [Bug] Misconfiguration tab displays "wrong" message when CSPM and KSPM has certain status combination (#180747) ## Summary ### Resolves https://github.com/elastic/kibana/issues/160943 This PR resolves the bug when we have both CSPM and KSPM integration and 1 is in a state not-installed and the other is indexing we show the user the prompt to install KSPM or CSPM integration, however, we should be showing the user that we are indexing data. Moving the indexing status return earlier in the chain of checks resolves this bug. https://github.com/elastic/kibana/assets/4624273/e356ec02-a03c-4211-ac70-52ea165c0f7f ###Testing Steps The speed of the Transformers will make this difficult to test, so these instructions are the best way we know how to see the indexing state of the Findings. See the attached videos for visual cues 1. Turn off the `cloud_security_posture.findings_latest-default` transformer 2. Delete all the documents from the `logs-cloud_security_posture.findings_latest-default` and `logs-cloud_security_posture.findings-default` 3. Navigate to the Security > Findings > Misconfigurations tab. You should see the not-installed notification 4. Add a document on the `logs-cloud_security_posture.findings-default` (script below, change the timestamp to today's date) 5. Navigate back to the Security > Findings > Misconfigurations tab, you should see the indexing notification 6. Turn back on the `cloud_security_posture.findings_latest-default` and then select Schedule Now 7. Navigate back to the Security > Findings > Misconfigurations tab, you should see the findings now ``` POST logs-cloud_security_posture.findings-default/_doc { "@timestamp": "2024-04-23T08:20:19.464Z", "agent": { "name": "cloudbeatVM", "id": "1ffe2dfa-0a21-4a5c-9683-aaff5282698b", "ephemeral_id": "3a05f2eb-9e2f-4069-a470-2e99a930a851", "type": "cloudbeat", "version": "8.13.0" }, "resource": { "sub_type": "azure-role-definition", "name": "b797a779-6c2d-5688-a4bf-784679a0621b", "raw": { "tenant_id": "4fa94b7d-a743-486f-abcc-6c276c44cf4b", "name": "b797a779-6c2d-5688-a4bf-784679a0621b", "id": "/providers/Microsoft.Authorization/RoleDefinitions/b797a779-6c2d-5688-a4bf-784679a0621b", "type": "microsoft.authorization/roledefinitions", "properties": { "isServiceRole": false, "updatedBy": "2692a518-34a6-495f-a3a6-9f096536eb09", "createdBy": "2692a518-34a6-495f-a3a6-9f096536eb09", "permissions": [ { "notActions": [], "dataActions": [], "notDataActions": [], "actions": [ "Microsoft.Web/sites/*/read" ] } ], "roleName": "cloudbeatVM additional permissions", "description": "Additional read permissions for cloudbeatVM", "updatedOn": "2024-03-11T12:16:43.5570000Z", "type": "CustomRole", "createdOn": "2024-03-11T11:45:13.9270000Z", "assignableScopes": [ "/subscriptions/ef111ee2-6c89-4b09-92c6-5c2321f888df", "/subscriptions/ef111ee2-6c89-4b09-92c6-5c2321f888df/resourcegroups/kuba-az" ] } }, "id": "/providers/Microsoft.Authorization/RoleDefinitions/b797a779-6c2d-5688-a4bf-784679a0621b", "type": "identity-management" }, "cloud_security_posture": { "package_policy": { "id": "72847b69-9706-40f0-a494-d6ea7a729a8b", "revision": 3 } }, "elastic_agent": { "id": "1ffe2dfa-0a21-4a5c-9683-aaff5282698b", "version": "8.13.0", "snapshot": false }, "rule": { "references": "1. https://docs.microsoft.com/en-us/azure/billing/billing-add-change-azure-subscription-administrator\n2. https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-governance-strategy#gs-2-define-enterprise-segmentation-strategy\n3. https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-governance-strategy#gs-6-define-identity-and-privileged-access-strategy\n4. https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-privileged-access#pa-1-protect-and-limit-highly-privileged-users\n5. https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-privileged-access#pa-5-automate-entitlement-management\n6. https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-privileged-access#pa-2-restrict-administrative-access-to-business-critical-systems\n7. https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-governance-strategy#gs-2-define-enterprise-segmentation-strategy\n8. https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-governance-strategy#gs-6-define-identity-and-privileged-access-strategy\n9. https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-privileged-access#pa-7-follow-just-enough-administration-least-privilege-principle", "impact": "Subscriptions will need to be handled by Administrators with permissions.", "description": "The principle of least privilege should be followed and only necessary privileges should be assigned instead of allowing full administrative access.", "default_value": "", "section": "Identity and Access Management", "rationale": "Classic subscription admin roles offer basic access management and include Account Administrator, Service Administrator, and Co-Administrators.\nIt is recommended the least necessary permissions be given initially.\nPermissions can be added as needed by the account holder.\nThis ensures the account holder cannot perform actions which were not intended.", "version": "1.0", "benchmark": { "name": "CIS Microsoft Azure Foundations", "rule_number": "1.23", "id": "cis_azure", "posture_type": "kspm", "version": "v2.0.0" }, "tags": [ "CIS", "AZURE", "CIS 1.23", "Identity and Access Management" ], "remediation": "**From Azure Portal**\n\n1. From Azure Home select the Portal Menu.\n2. Select `Subscriptions`.\n3. Select `Access control (IAM)`.\n4. Select `Roles`.\n5. Click `Type` and select `CustomRole` from the drop down menu.\n6. Check the box next to each role which grants subscription administrator privileges.\n7. Select `Remove`.\n8. Select `Yes`.\n\n**From Azure CLI**\n\nList custom roles:\n\n```\naz role definition list --custom-role-only True\n```\n\nCheck for entries with `assignableScope` of `/` or the `subscription`, and an action of `*`.\n\nTo remove a violating role:\n\n```\naz role definition delete --name \n```\n\nNote that any role assignments must be removed before a custom role can be deleted.\nEnsure impact is assessed before deleting a custom role granting subscription administrator privileges.", "audit": "**From Azure Portal**\n\n1. From Azure Home select the Portal Menu.\n2. Select `Subscriptions`.\n3. Select `Access control (IAM)`.\n4. Select `Roles`.\n5. Click `Type` and select `CustomRole` from the drop down menu.\n6. Select `View` next to a role.\n7. Select `JSON`.\n8. Check for `assignableScopes` set to `/` or the subscription, and `actions` set to `*`.\n9. Repeat steps 6-8 for each custom role.\n\n**From Azure CLI**\n\nList custom roles:\n\n```\naz role definition list --custom-role-only True\n```\n\nCheck for entries with `assignableScope` of `/` or the `subscription`, and an action of `*`\n\n**From PowerShell**\n\n```\nConnect-AzAccount\nGet-AzRoleDefinition |Where-Object {($_.IsCustom -eq $true) -and ($_.Actions.contains('*'))}\n```\n\nCheck the output for `AssignableScopes` value set to '/' or the subscription.", "name": "Ensure That No Custom Subscription Administrator Roles Exist", "id": "05676b4e-3274-5984-9981-6aa1623c24ec", "profile_applicability": "* Level 1" }, "message": "Rule \"Ensure That No Custom Subscription Administrator Roles Exist\": passed", "error": { "message": [ "field [cluster_id] not present as part of path [cluster_id]" ] }, "cloud": { "provider": "azure", "account": { "id": "111" } }, "result": { "evaluation": "passed", "evidence": { "Resource": { "tenant_id": "4fa94b7d-a743-486f-abcc-6c276c44cf4b", "name": "b797a779-6c2d-5688-a4bf-784679a0621b", "id": "/providers/Microsoft.Authorization/RoleDefinitions/b797a779-6c2d-5688-a4bf-784679a0621b", "type": "microsoft.authorization/roledefinitions", "properties": { "isServiceRole": false, "updatedBy": "2692a518-34a6-495f-a3a6-9f096536eb09", "createdBy": "2692a518-34a6-495f-a3a6-9f096536eb09", "permissions": [ { "notActions": [], "dataActions": [], "notDataActions": [], "actions": [ "Microsoft.Web/sites/*/read" ] } ], "roleName": "cloudbeatVM additional permissions", "description": "Additional read permissions for cloudbeatVM", "updatedOn": "2024-03-11T12:16:43.5570000Z", "type": "CustomRole", "createdOn": "2024-03-11T11:45:13.9270000Z", "assignableScopes": [ "/subscriptions/ef111ee2-6c89-4b09-92c6-5c2321f888df", "/subscriptions/ef111ee2-6c89-4b09-92c6-5c2321f888df/resourcegroups/kuba-az" ] } } }, "expected": null }, "ecs": { "version": "8.6.0" }, "cloudbeat": { "commit_sha": "f01b313ffdaec0160c2d039b2a2d8bed38d5ba8c", "commit_time": "2024-03-11T09:01:17Z", "version": "8.13.0", "policy": { "commit_sha": "f01b313ffdaec0160c2d039b2a2d8bed38d5ba8c", "commit_time": "2024-03-11T09:01:17Z", "version": "8.13.0" } }, "data_stream": { "namespace": "default", "type": "logs", "dataset": "cloud_security_posture.findings" }, "host": { "name": "cloudbeatVM" }, "event": { "agent_id_status": "auth_metadata_missing", "sequence": 1710242361, "ingested": "2024-03-12T11:25:35Z", "created": "2024-03-12T11:20:19.451741982Z", "kind": "pipeline_error", "id": "907c27cc-a573-449e-9feb-f40c199dee61", "type": [ "info" ], "category": [ "configuration" ], "dataset": "cloud_security_posture.findings", "outcome": "success" } } ``` ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../components/no_findings_states.test.tsx | 245 ++++++++++++++++++ .../public/components/no_findings_states.tsx | 77 +++--- 2 files changed, 293 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/public/components/no_findings_states.test.tsx diff --git a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.test.tsx new file mode 100644 index 0000000000000..840aad9af2e94 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.test.tsx @@ -0,0 +1,245 @@ +/* + * 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 { render } from '@testing-library/react'; +import { createReactQueryResponse } from '../test/fixtures/react_query'; +import { TestProvider } from '../test/test_provider'; +import { NoFindingsStates } from './no_findings_states'; +import { useCspSetupStatusApi } from '../common/api/use_setup_status_api'; +import { useCspIntegrationLink } from '../common/navigation/use_csp_integration_link'; +import { useLicenseManagementLocatorApi } from '../common/api/use_license_management_locator_api'; +import { useSubscriptionStatus } from '../common/hooks/use_subscription_status'; + +jest.mock('../common/api/use_setup_status_api', () => ({ + useCspSetupStatusApi: jest.fn(), +})); + +jest.mock('../common/navigation/use_csp_integration_link', () => ({ + useCspIntegrationLink: jest.fn(), +})); + +jest.mock('../common/api/use_license_management_locator_api', () => ({ + useLicenseManagementLocatorApi: jest.fn(), +})); + +jest.mock('../common/hooks/use_subscription_status', () => ({ + useSubscriptionStatus: jest.fn(), +})); + +const customRederer = (postureType: 'cspm' | 'kspm') => { + return render( + + + + ); +}; + +describe('NoFindingsStates', () => { + beforeEach(() => { + jest.clearAllMocks(); + + (useSubscriptionStatus as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: true, + }) + ); + + (useLicenseManagementLocatorApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: true, + }) + ); + + (useCspIntegrationLink as jest.Mock).mockReturnValue('http://cspm-or-kspm-integration-link'); + }); + + it('should show the indexing notification when CSPM is not installed and KSPM is indexing', async () => { + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + cspm: { + status: 'not-deployed', + }, + kspm: { + status: 'indexing', + }, + indicesDetails: [ + { index: 'logs-cloud_security_posture.findings_latest-default', status: 'empty' }, + { index: 'logs-cloud_security_posture.findings-default*', status: 'empty' }, + ], + }, + }) + ); + + const { getByText } = customRederer('kspm'); + expect(getByText(/posture evaluation underway/i)).toBeInTheDocument(); + expect( + getByText( + /waiting for data to be collected and indexed. check back later to see your findings/i + ) + ).toBeInTheDocument(); + }); + + it('should show the indexing notification when KSPM is not installed and CSPM is indexing', async () => { + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + kspm: { + status: 'not-deployed', + }, + cspm: { + status: 'indexing', + }, + indicesDetails: [ + { index: 'logs-cloud_security_posture.findings_latest-default', status: 'empty' }, + { index: 'logs-cloud_security_posture.findings-default*', status: 'empty' }, + ], + }, + }) + ); + + const { getByText } = customRederer('cspm'); + expect(getByText(/posture evaluation underway/i)).toBeInTheDocument(); + expect( + getByText( + /waiting for data to be collected and indexed. Check back later to see your findings/i + ) + ).toBeInTheDocument(); + }); + + it('should show the indexing timout notification when CSPM is status is index-timeout', async () => { + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + kspm: { + status: 'installed', + }, + cspm: { + status: 'index-timeout', + }, + indicesDetails: [ + { index: 'logs-cloud_security_posture.findings_latest-default', status: 'empty' }, + { index: 'logs-cloud_security_posture.findings-default*', status: 'empty' }, + ], + }, + }) + ); + + const { getByText } = customRederer('cspm'); + expect(getByText(/waiting for findings data/i)).toBeInTheDocument(); + const indexTimeOutMessage = getByText(/collecting findings is taking longer than expected/i); + expect(indexTimeOutMessage).toBeInTheDocument(); + }); + + it('should show the indexing timout notification when KSPM is status is index-timeout', async () => { + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + kspm: { + status: 'index-timeout', + }, + cspm: { + status: 'installed', + }, + indicesDetails: [ + { index: 'logs-cloud_security_posture.findings_latest-default', status: 'empty' }, + { index: 'logs-cloud_security_posture.findings-default*', status: 'empty' }, + ], + }, + }) + ); + + const { getByText } = customRederer('kspm'); + expect(getByText(/waiting for findings data/i)).toBeInTheDocument(); + expect(getByText(/collecting findings is taking longer than expected/i)).toBeInTheDocument(); + }); + + it('should show the unprivileged notification when CSPM is status is index-timeout', async () => { + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + kspm: { + status: 'installed', + }, + cspm: { + status: 'unprivileged', + }, + indicesDetails: [ + { + index: 'logs-cloud_security_posture.findings_latest-default', + status: 'unprivileged', + }, + { index: 'logs-cloud_security_posture.findings-default*', status: 'unprivileged' }, + ], + }, + }) + ); + + const { getByText } = customRederer('cspm'); + expect(getByText(/privileges required/i)).toBeInTheDocument(); + }); + + it('should show the unprivileged notification when KSPM is status is index-timeout', async () => { + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + kspm: { + status: 'unprivileged', + }, + cspm: { + status: 'installed', + }, + indicesDetails: [ + { + index: 'logs-cloud_security_posture.findings_latest-default', + status: 'unprivileged', + }, + { index: 'logs-cloud_security_posture.findings-default*', status: 'unprivileged' }, + ], + }, + }) + ); + + const { getByText } = customRederer('kspm'); + expect(getByText(/privileges required/i)).toBeInTheDocument(); + }); + + it('should show the not-installed notification when CSPM and KSPM status is not-installed', async () => { + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + kspm: { + status: 'not-installed', + }, + cspm: { + status: 'not-installed', + }, + indicesDetails: [ + { + index: 'logs-cloud_security_posture.findings_latest-default', + status: 'success', + }, + { index: 'logs-cloud_security_posture.findings-default*', status: 'success' }, + ], + }, + }) + ); + + const { getByText } = customRederer('cspm'); + expect(getByText(/learn more about cloud security posture/i)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx index c41dfc9948086..8e20207719940 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx @@ -30,7 +30,7 @@ import { } from './test_subjects'; import { CloudPosturePage, PACKAGE_NOT_INSTALLED_TEST_SUBJECT } from './cloud_posture_page'; import { useCspSetupStatusApi } from '../common/api/use_setup_status_api'; -import type { IndexDetails, PostureTypes } from '../../common/types_old'; +import type { IndexDetails, PostureTypes, CspStatusCode } from '../../common/types_old'; import noDataIllustration from '../assets/illustrations/no_data_illustration.svg'; import { useCspIntegrationLink } from '../common/navigation/use_csp_integration_link'; import { NO_FINDINGS_STATUS_REFRESH_INTERVAL_MS } from '../common/constants'; @@ -169,7 +169,7 @@ const Unprivileged = ({ unprivilegedIndices }: { unprivilegedIndices: string[] } /> ); -const ConfigurationFindingsInstalledEmptyPrompt = ({ +const EmptySecurityFindingsPrompt = ({ kspmIntegrationLink, cspmIntegrationLink, }: { @@ -242,6 +242,43 @@ const ConfigurationFindingsInstalledEmptyPrompt = ({ ); }; +const NoFindingsStatesNotification = ({ + postureType, + status, + indicesStatus, + isNotInstalled, +}: { + postureType: PostureTypes; + status?: CspStatusCode; + indicesStatus?: IndexDetails[]; + isNotInstalled: boolean; +}) => { + const kspmIntegrationLink = useCspIntegrationLink(KSPM_POLICY_TEMPLATE); + const cspmIntegrationLink = useCspIntegrationLink(CSPM_POLICY_TEMPLATE); + + const unprivilegedIndices = + indicesStatus && + indicesStatus + .filter((idxDetails) => idxDetails.status === 'unprivileged') + .map((idxDetails: IndexDetails) => idxDetails.index) + .sort((a, b) => a.localeCompare(b)); + + if (status === 'unprivileged') + return ; + if (status === 'indexing' || status === 'waiting_for_results') return ; + if (status === 'index-timeout') return ; + if (isNotInstalled) + return ( + + ); + if (status === 'not-deployed') return ; + + return null; +}; + /** * This component will return the render states based on cloud posture setup status API * since 'not-installed' is being checked globally by CloudPosturePage and 'indexed' is the pass condition, those states won't be handled here @@ -254,36 +291,18 @@ export const NoFindingsStates = ({ postureType }: { postureType: PostureTypes }) const statusCspm = getSetupStatus.data?.cspm?.status; const indicesStatus = getSetupStatus.data?.indicesDetails; const status = postureType === 'cspm' ? statusCspm : statusKspm; - const showConfigurationInstallPrompt = - getSetupStatus.data?.kspm?.status === 'not-installed' && - getSetupStatus.data?.cspm?.status === 'not-installed'; - const kspmIntegrationLink = useCspIntegrationLink(KSPM_POLICY_TEMPLATE); - const cspmIntegrationLink = useCspIntegrationLink(CSPM_POLICY_TEMPLATE); - - const unprivilegedIndices = - indicesStatus && - indicesStatus - .filter((idxDetails) => idxDetails.status === 'unprivileged') - .map((idxDetails: IndexDetails) => idxDetails.index) - .sort((a, b) => a.localeCompare(b)); - const render = () => { - if (status === 'not-deployed') return ; // integration installed, but no agents added - if (status === 'indexing' || status === 'waiting_for_results') return ; // agent added, index timeout hasn't passed since installation - if (status === 'index-timeout') return ; // agent added, index timeout has passed - if (status === 'unprivileged') - return ; // user has no privileges for our indices - if (showConfigurationInstallPrompt) - return ( - - ); - }; + const isNotInstalled = statusKspm === 'not-installed' && statusCspm === 'not-installed'; return ( - {render()} + + + ); }; From 56a22661dd450f841c0aee0edeabb1005f0382d7 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 6 May 2024 14:51:27 -0500 Subject: [PATCH 3/4] [ironbank] Upgrade to ubi 9.4 (#182738) --- .../os_packages/docker_generator/templates/ironbank/Dockerfile | 2 +- .../docker_generator/templates/ironbank/hardening_manifest.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/Dockerfile index 5fe1a6695d06b..f7849bff06ead 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/Dockerfile @@ -4,7 +4,7 @@ ################################################################################ ARG BASE_REGISTRY=registry1.dso.mil ARG BASE_IMAGE=ironbank/redhat/ubi/ubi9 -ARG BASE_TAG=9.3 +ARG BASE_TAG=9.4 FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG} as prep_files diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/hardening_manifest.yaml b/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/hardening_manifest.yaml index bbfaee1e16202..6f0ab62dd1361 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/hardening_manifest.yaml +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/hardening_manifest.yaml @@ -14,7 +14,7 @@ tags: # Build args passed to Dockerfile ARGs args: BASE_IMAGE: 'redhat/ubi/ubi9' - BASE_TAG: '9.3' + BASE_TAG: '9.4' # Docker image labels labels: From 0c0cf0a8e0e9dde31f9c9872cde328984065dfc8 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Mon, 6 May 2024 16:20:20 -0400 Subject: [PATCH 4/4] Move panel placement register to dashboard start contract (#182571) Part of #182585 ## Summary Puts the `registerDashboardPanelPlacementSetting` function on the Dashboard start contract. Rather than importing the module directly, consumers will need to include `dashboard` in their plugin's start dependencies. --- .../public/app/register_embeddable.tsx | 9 +++---- examples/embeddable_examples/public/plugin.ts | 4 +++- .../register_field_list_embeddable.ts | 9 +++---- .../api/duplicate_dashboard_panel.ts | 2 +- .../embeddable/create/create_dashboard.ts | 2 +- .../embeddable/dashboard_container.tsx | 6 ++--- .../public/dashboard_container/index.ts | 2 +- .../{component => }/panel_placement/index.ts | 4 ++++ .../panel_placement_registry.ts} | 6 +---- .../place_clone_panel_strategy.ts | 6 ++--- .../place_new_panel_strategies.ts | 4 ++-- .../panel_placement/place_panel.test.ts | 24 +++++++++---------- .../panel_placement/place_panel.ts | 16 ++++++------- .../{component => }/panel_placement/types.ts | 14 +++++++---- src/plugins/dashboard/public/index.ts | 2 +- src/plugins/dashboard/public/plugin.tsx | 7 ++++++ .../dashboard_link_component.test.tsx | 2 +- .../dashboard_link_component.tsx | 2 +- .../dashboard_link_destination_picker.tsx | 2 +- .../components/editor/link_destination.tsx | 2 +- .../public/components/editor/link_editor.tsx | 2 +- .../public/components/editor/links_editor.tsx | 2 +- .../editor/links_editor_single_link.tsx | 2 +- .../public/editor/open_editor_flyout.tsx | 2 +- .../public/editor/open_link_editor_flyout.tsx | 2 +- .../public/embeddable/links_embeddable.tsx | 2 +- .../links_embeddable_factory.test.ts | 6 ++--- .../embeddable/links_embeddable_factory.ts | 12 ++++++---- src/plugins/links/public/plugin.ts | 2 +- .../observability_solution/slo/kibana.jsonc | 4 ++-- .../slo/public/plugin.ts | 8 ++----- .../slo/public/types.ts | 2 ++ 32 files changed, 91 insertions(+), 80 deletions(-) rename src/plugins/dashboard/public/dashboard_container/{component => }/panel_placement/index.ts (71%) rename src/plugins/dashboard/public/dashboard_container/{external_api/dashboard_panel_placement_registry.ts => panel_placement/panel_placement_registry.ts} (81%) rename src/plugins/dashboard/public/dashboard_container/{component => }/panel_placement/place_clone_panel_strategy.ts (95%) rename src/plugins/dashboard/public/dashboard_container/{component => }/panel_placement/place_new_panel_strategies.ts (97%) rename src/plugins/dashboard/public/dashboard_container/{component => }/panel_placement/place_panel.test.ts (85%) rename src/plugins/dashboard/public/dashboard_container/{component => }/panel_placement/place_panel.ts (75%) rename src/plugins/dashboard/public/dashboard_container/{component => }/panel_placement/types.ts (69%) diff --git a/examples/embeddable_examples/public/app/register_embeddable.tsx b/examples/embeddable_examples/public/app/register_embeddable.tsx index 95bac13ee0023..351828cc66c6b 100644 --- a/examples/embeddable_examples/public/app/register_embeddable.tsx +++ b/examples/embeddable_examples/public/app/register_embeddable.tsx @@ -64,10 +64,11 @@ export const RegisterEmbeddable = () => {

Configure initial dashboard placement (optional)

- Add an entry to registerDashboardPanelPlacementSetting to configure - initial dashboard placement. Panel placement lets you configure the width, height, and - placement strategy when panels get added to a dashboard. In the example below, the Field - List embeddable will be added to dashboards as a narrow and tall panel. + Add an entry to registerDashboardPanelPlacementSetting provided by the + Dashboard plugin start contract to configure initial dashboard placement. Panel placement + lets you configure the width, height, and placement strategy when panels get added to a + dashboard. In the example below, the Field List embeddable will be added to dashboards as + a narrow and tall panel.

diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts index 6939271e37341..312c9630fd91e 100644 --- a/examples/embeddable_examples/public/plugin.ts +++ b/examples/embeddable_examples/public/plugin.ts @@ -8,6 +8,7 @@ import { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { DashboardStart } from '@kbn/dashboard-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { DataViewFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; @@ -44,6 +45,7 @@ export interface StartDeps { data: DataPublicPluginStart; charts: ChartsPluginStart; fieldFormats: FieldFormatsStart; + dashboard: DashboardStart; } export class EmbeddableExamplesPlugin implements Plugin { @@ -59,7 +61,7 @@ export class EmbeddableExamplesPlugin implements Plugin { diff --git a/examples/embeddable_examples/public/react_embeddables/field_list/register_field_list_embeddable.ts b/examples/embeddable_examples/public/react_embeddables/field_list/register_field_list_embeddable.ts index e31df63b62f37..04bb93d7d052e 100644 --- a/examples/embeddable_examples/public/react_embeddables/field_list/register_field_list_embeddable.ts +++ b/examples/embeddable_examples/public/react_embeddables/field_list/register_field_list_embeddable.ts @@ -6,10 +6,7 @@ * Side Public License, v 1. */ -import { - registerDashboardPanelPlacementSetting, - PanelPlacementStrategy, -} from '@kbn/dashboard-plugin/public'; +import { DashboardStart, PanelPlacementStrategy } from '@kbn/dashboard-plugin/public'; import { FIELD_LIST_ID } from './constants'; import { FieldListSerializedStateState } from './types'; @@ -22,6 +19,6 @@ const getPanelPlacementSetting = (serializedState?: FieldListSerializedStateStat }; }; -export function registerFieldListPanelPlacementSetting() { - registerDashboardPanelPlacementSetting(FIELD_LIST_ID, getPanelPlacementSetting); +export function registerFieldListPanelPlacementSetting(dashboard: DashboardStart) { + dashboard.registerDashboardPanelPlacementSetting(FIELD_LIST_ID, getPanelPlacementSetting); } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.ts index 134f180db9a7b..012c0e720cb05 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.ts @@ -19,7 +19,7 @@ import { v4 as uuidv4 } from 'uuid'; import { DashboardPanelState } from '../../../../common'; import { dashboardClonePanelActionStrings } from '../../../dashboard_actions/_dashboard_actions_strings'; import { pluginServices } from '../../../services/plugin_services'; -import { placeClonePanel } from '../../component/panel_placement'; +import { placeClonePanel } from '../../panel_placement'; import { DashboardContainer } from '../dashboard_container'; const duplicateLegacyInput = async ( diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts index da96eeb8f54ce..ac585ce58d8cc 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts @@ -46,7 +46,7 @@ import { SavedDashboardInput, } from '../../../services/dashboard_content_management/types'; import { pluginServices } from '../../../services/plugin_services'; -import { runPanelPlacementStrategy } from '../../component/panel_placement/place_new_panel_strategies'; +import { runPanelPlacementStrategy } from '../../panel_placement/place_new_panel_strategies'; import { startDiffingDashboardState } from '../../state/diffing/dashboard_diffing_integration'; import { DashboardPublicState } from '../../types'; import { DashboardContainer } from '../dashboard_container'; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx index 69fdf28647c09..d3ba52c97edf2 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx @@ -62,11 +62,11 @@ import { import { DashboardAnalyticsService } from '../../services/analytics/types'; import { DashboardCapabilitiesService } from '../../services/dashboard_capabilities/types'; import { pluginServices } from '../../services/plugin_services'; -import { placePanel } from '../component/panel_placement'; -import { runPanelPlacementStrategy } from '../component/panel_placement/place_new_panel_strategies'; +import { placePanel } from '../panel_placement'; +import { runPanelPlacementStrategy } from '../panel_placement/place_new_panel_strategies'; import { DashboardViewport } from '../component/viewport/dashboard_viewport'; import { DashboardExternallyAccessibleApi } from '../external_api/dashboard_api'; -import { getDashboardPanelPlacementSetting } from '../external_api/dashboard_panel_placement_registry'; +import { getDashboardPanelPlacementSetting } from '../panel_placement/panel_placement_registry'; import { dashboardContainerReducers } from '../state/dashboard_container_reducers'; import { getDiffingMiddleware } from '../state/diffing/dashboard_diffing_integration'; import { diff --git a/src/plugins/dashboard/public/dashboard_container/index.ts b/src/plugins/dashboard/public/dashboard_container/index.ts index c8944968e6cb7..20b9966850e20 100644 --- a/src/plugins/dashboard/public/dashboard_container/index.ts +++ b/src/plugins/dashboard/public/dashboard_container/index.ts @@ -22,5 +22,5 @@ export { export { DashboardRenderer } from './external_api/dashboard_renderer'; export type { DashboardAPI, AwaitingDashboardAPI } from './external_api/dashboard_api'; -export { registerDashboardPanelPlacementSetting } from './external_api/dashboard_panel_placement_registry'; export type { DashboardLocatorParams } from './types'; +export type { IProvidesLegacyPanelPlacementSettings } from './panel_placement'; diff --git a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/index.ts b/src/plugins/dashboard/public/dashboard_container/panel_placement/index.ts similarity index 71% rename from src/plugins/dashboard/public/dashboard_container/component/panel_placement/index.ts rename to src/plugins/dashboard/public/dashboard_container/panel_placement/index.ts index 8e7444712c281..8ab73cf928277 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/index.ts +++ b/src/plugins/dashboard/public/dashboard_container/panel_placement/index.ts @@ -9,3 +9,7 @@ export { placePanel } from './place_panel'; export { placeClonePanel } from './place_clone_panel_strategy'; + +export { registerDashboardPanelPlacementSetting } from './panel_placement_registry'; + +export type { GetPanelPlacementSettings, IProvidesLegacyPanelPlacementSettings } from './types'; diff --git a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_panel_placement_registry.ts b/src/plugins/dashboard/public/dashboard_container/panel_placement/panel_placement_registry.ts similarity index 81% rename from src/plugins/dashboard/public/dashboard_container/external_api/dashboard_panel_placement_registry.ts rename to src/plugins/dashboard/public/dashboard_container/panel_placement/panel_placement_registry.ts index e21fedb9aabe1..98fab7c662506 100644 --- a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_panel_placement_registry.ts +++ b/src/plugins/dashboard/public/dashboard_container/panel_placement/panel_placement_registry.ts @@ -6,13 +6,9 @@ * Side Public License, v 1. */ -import { PanelPlacementSettings } from '../component/panel_placement/types'; +import { GetPanelPlacementSettings } from './types'; import { panelPlacementStrings } from '../_dashboard_container_strings'; -type GetPanelPlacementSettings = ( - serializedState?: SerializedState -) => PanelPlacementSettings; - const registry = new Map>(); export const registerDashboardPanelPlacementSetting = ( diff --git a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_clone_panel_strategy.ts b/src/plugins/dashboard/public/dashboard_container/panel_placement/place_clone_panel_strategy.ts similarity index 95% rename from src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_clone_panel_strategy.ts rename to src/plugins/dashboard/public/dashboard_container/panel_placement/place_clone_panel_strategy.ts index affe85dff5d26..cbd56ad26eca2 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_clone_panel_strategy.ts +++ b/src/plugins/dashboard/public/dashboard_container/panel_placement/place_clone_panel_strategy.ts @@ -9,10 +9,10 @@ import { cloneDeep, forOwn } from 'lodash'; import { PanelNotFoundError } from '@kbn/embeddable-plugin/public'; -import { DashboardPanelState } from '../../../../common'; -import { GridData } from '../../../../common/content_management'; +import { DashboardPanelState } from '../../../common'; +import { GridData } from '../../../common/content_management'; import { PanelPlacementProps, PanelPlacementReturn } from './types'; -import { DASHBOARD_GRID_COLUMN_COUNT } from '../../../dashboard_constants'; +import { DASHBOARD_GRID_COLUMN_COUNT } from '../../dashboard_constants'; interface IplacementDirection { grid: Omit; diff --git a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_new_panel_strategies.ts b/src/plugins/dashboard/public/dashboard_container/panel_placement/place_new_panel_strategies.ts similarity index 97% rename from src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_new_panel_strategies.ts rename to src/plugins/dashboard/public/dashboard_container/panel_placement/place_new_panel_strategies.ts index 168dcfa72a491..9c5d6646ea5ff 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_new_panel_strategies.ts +++ b/src/plugins/dashboard/public/dashboard_container/panel_placement/place_new_panel_strategies.ts @@ -7,8 +7,8 @@ */ import { cloneDeep } from 'lodash'; -import { DASHBOARD_GRID_COLUMN_COUNT, PanelPlacementStrategy } from '../../../dashboard_constants'; -import { panelPlacementStrings } from '../../_dashboard_container_strings'; +import { DASHBOARD_GRID_COLUMN_COUNT, PanelPlacementStrategy } from '../../dashboard_constants'; +import { panelPlacementStrings } from '../_dashboard_container_strings'; import { PanelPlacementProps, PanelPlacementReturn } from './types'; export const runPanelPlacementStrategy = ( diff --git a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_panel.test.ts b/src/plugins/dashboard/public/dashboard_container/panel_placement/place_panel.test.ts similarity index 85% rename from src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_panel.test.ts rename to src/plugins/dashboard/public/dashboard_container/panel_placement/place_panel.test.ts index 24023ba92dbce..2555419934e07 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_panel.test.ts +++ b/src/plugins/dashboard/public/dashboard_container/panel_placement/place_panel.test.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { DashboardPanelState } from '../../../../common'; +import { DashboardPanelState } from '../../../common'; import { EmbeddableFactory, EmbeddableInput } from '@kbn/embeddable-plugin/public'; import { CONTACT_CARD_EMBEDDABLE } from '@kbn/embeddable-plugin/public/lib/test_samples'; -import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../../../dashboard_constants'; +import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../../dashboard_constants'; import { placePanel } from './place_panel'; -import { IProvidesPanelPlacementSettings } from './types'; +import { IProvidesLegacyPanelPlacementSettings } from './types'; interface TestInput extends EmbeddableInput { test: string; @@ -92,8 +92,8 @@ test('adds a new panel state in the top most position when it is open', () => { }); test('adds a new panel state at the very top of the Dashboard with default sizing', () => { - const embeddableFactoryStub: IProvidesPanelPlacementSettings = { - getPanelPlacementSettings: jest.fn().mockImplementation(() => { + const embeddableFactoryStub: IProvidesLegacyPanelPlacementSettings = { + getLegacyPanelPlacementSettings: jest.fn().mockImplementation(() => { return { strategy: 'placeAtTop' }; }), }; @@ -111,15 +111,15 @@ test('adds a new panel state at the very top of the Dashboard with default sizin expect(panelState.gridData.h).toBe(DEFAULT_PANEL_HEIGHT); expect(panelState.gridData.w).toBe(DEFAULT_PANEL_WIDTH); - expect(embeddableFactoryStub.getPanelPlacementSettings).toHaveBeenCalledWith( + expect(embeddableFactoryStub.getLegacyPanelPlacementSettings).toHaveBeenCalledWith( { id: '9001', test: 'wowee' }, undefined ); }); test('adds a new panel state at the very top of the Dashboard with custom sizing', () => { - const embeddableFactoryStub: IProvidesPanelPlacementSettings = { - getPanelPlacementSettings: jest.fn().mockImplementation(() => { + const embeddableFactoryStub: IProvidesLegacyPanelPlacementSettings = { + getLegacyPanelPlacementSettings: jest.fn().mockImplementation(() => { return { strategy: 'placeAtTop', width: 10, height: 5 }; }), }; @@ -137,15 +137,15 @@ test('adds a new panel state at the very top of the Dashboard with custom sizing expect(panelState.gridData.h).toBe(5); expect(panelState.gridData.w).toBe(10); - expect(embeddableFactoryStub.getPanelPlacementSettings).toHaveBeenCalledWith( + expect(embeddableFactoryStub.getLegacyPanelPlacementSettings).toHaveBeenCalledWith( { id: '9002', test: 'woweee' }, undefined ); }); test('passes through given attributes', () => { - const embeddableFactoryStub: IProvidesPanelPlacementSettings = { - getPanelPlacementSettings: jest.fn().mockImplementation(() => { + const embeddableFactoryStub: IProvidesLegacyPanelPlacementSettings = { + getLegacyPanelPlacementSettings: jest.fn().mockImplementation(() => { return { strategy: 'placeAtTop', width: 10, height: 5 }; }), }; @@ -160,7 +160,7 @@ test('passes through given attributes', () => { { testAttr: 'hello' } ); - expect(embeddableFactoryStub.getPanelPlacementSettings).toHaveBeenCalledWith( + expect(embeddableFactoryStub.getLegacyPanelPlacementSettings).toHaveBeenCalledWith( { id: '9004', test: 'wow' }, { testAttr: 'hello' } ); diff --git a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_panel.ts b/src/plugins/dashboard/public/dashboard_container/panel_placement/place_panel.ts similarity index 75% rename from src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_panel.ts rename to src/plugins/dashboard/public/dashboard_container/panel_placement/place_panel.ts index c440c0fe93e10..86fe3c823e899 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/place_panel.ts +++ b/src/plugins/dashboard/public/dashboard_container/panel_placement/place_panel.ts @@ -8,19 +8,19 @@ import { PanelState, EmbeddableInput, EmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { DashboardPanelState } from '../../../../common'; -import { IProvidesPanelPlacementSettings } from './types'; +import { DashboardPanelState } from '../../../common'; +import { IProvidesLegacyPanelPlacementSettings } from './types'; import { runPanelPlacementStrategy } from './place_new_panel_strategies'; import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH, PanelPlacementStrategy, -} from '../../../dashboard_constants'; +} from '../../dashboard_constants'; -export const providesPanelPlacementSettings = ( +export const providesLegacyPanelPlacementSettings = ( value: unknown -): value is IProvidesPanelPlacementSettings => { - return Boolean((value as IProvidesPanelPlacementSettings).getPanelPlacementSettings); +): value is IProvidesLegacyPanelPlacementSettings => { + return Boolean((value as IProvidesLegacyPanelPlacementSettings).getLegacyPanelPlacementSettings); }; export function placePanel( @@ -37,10 +37,10 @@ export function placePanel( height: DEFAULT_PANEL_HEIGHT, strategy: PanelPlacementStrategy.findTopLeftMostOpenSpace, }; - if (providesPanelPlacementSettings(factory)) { + if (providesLegacyPanelPlacementSettings(factory)) { placementSettings = { ...placementSettings, - ...factory.getPanelPlacementSettings(newPanel.explicitInput, attributes), + ...factory.getLegacyPanelPlacementSettings(newPanel.explicitInput, attributes), }; } const { width, height, strategy } = placementSettings; diff --git a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/types.ts b/src/plugins/dashboard/public/dashboard_container/panel_placement/types.ts similarity index 69% rename from src/plugins/dashboard/public/dashboard_container/component/panel_placement/types.ts rename to src/plugins/dashboard/public/dashboard_container/panel_placement/types.ts index d5fdbf705f443..54b490e004fac 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/panel_placement/types.ts +++ b/src/plugins/dashboard/public/dashboard_container/panel_placement/types.ts @@ -7,9 +7,9 @@ */ import { EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import { DashboardPanelState } from '../../../../common'; -import { GridData } from '../../../../common/content_management'; -import { PanelPlacementStrategy } from '../../../dashboard_constants'; +import { DashboardPanelState } from '../../../common'; +import { GridData } from '../../../common/content_management'; +import { PanelPlacementStrategy } from '../../dashboard_constants'; export interface PanelPlacementSettings { strategy?: PanelPlacementStrategy; @@ -28,12 +28,16 @@ export interface PanelPlacementProps { currentPanels: { [key: string]: DashboardPanelState }; } -export interface IProvidesPanelPlacementSettings< +export interface IProvidesLegacyPanelPlacementSettings< InputType extends EmbeddableInput = EmbeddableInput, AttributesType = unknown > { - getPanelPlacementSettings: ( + getLegacyPanelPlacementSettings: ( input: InputType, attributes?: AttributesType ) => Partial; } + +export type GetPanelPlacementSettings = ( + serializedState?: SerializedState +) => PanelPlacementSettings; diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index 3b1bafe2e1fd4..91731174eff76 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -17,13 +17,13 @@ export { PanelPlacementStrategy, } from './dashboard_constants'; export { - registerDashboardPanelPlacementSetting, type DashboardAPI, type AwaitingDashboardAPI, DashboardRenderer, DASHBOARD_CONTAINER_TYPE, type DashboardCreationOptions, type DashboardLocatorParams, + type IProvidesLegacyPanelPlacementSettings, } from './dashboard_container'; export type { DashboardSetup, DashboardStart, DashboardFeatureFlagConfig } from './plugin'; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 16625f7b37006..0a231492d70b9 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -56,6 +56,7 @@ import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { DashboardContainerFactoryDefinition } from './dashboard_container/embeddable/dashboard_container_factory'; +import { registerDashboardPanelPlacementSetting } from './dashboard_container/panel_placement'; import { type DashboardAppLocator, DashboardAppLocatorDefinition, @@ -70,6 +71,7 @@ import { DashboardMountContextProps } from './dashboard_app/types'; import type { FindDashboardsService } from './services/dashboard_content_management/types'; import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; import { addPanelMenuTrigger } from './triggers'; +import { GetPanelPlacementSettings } from './dashboard_container/panel_placement'; export interface DashboardFeatureFlagConfig { allowByValueEmbeddables: boolean; @@ -119,6 +121,10 @@ export interface DashboardStart { locator?: DashboardAppLocator; dashboardFeatureFlagConfig: DashboardFeatureFlagConfig; findDashboardsService: () => Promise; + registerDashboardPanelPlacementSetting: ( + embeddableType: string, + getPanelPlacementSettings: GetPanelPlacementSettings + ) => void; } export let resolveServicesReady: () => void; @@ -344,6 +350,7 @@ export class DashboardPlugin return { locator: this.locator, dashboardFeatureFlagConfig: this.dashboardFeatureFlagConfig!, + registerDashboardPanelPlacementSetting, findDashboardsService: async () => { const { pluginServices } = await import('./services/plugin_services'); const { diff --git a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx index 4e6eb32319baf..84e358fdb381c 100644 --- a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx +++ b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { getDashboardLocatorParamsFromEmbeddable } from '@kbn/dashboard-plugin/public'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { DEFAULT_DASHBOARD_DRILLDOWN_OPTIONS } from '@kbn/presentation-util-plugin/public'; import { createEvent, fireEvent, render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; diff --git a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.tsx b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.tsx index 57d881cecd705..202a697cd7160 100644 --- a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.tsx +++ b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.tsx @@ -17,7 +17,7 @@ import { DashboardLocatorParams, getDashboardLocatorParamsFromEmbeddable, } from '@kbn/dashboard-plugin/public'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { DashboardDrilldownOptions, DEFAULT_DASHBOARD_DRILLDOWN_OPTIONS, diff --git a/src/plugins/links/public/components/dashboard_link/dashboard_link_destination_picker.tsx b/src/plugins/links/public/components/dashboard_link/dashboard_link_destination_picker.tsx index 137d604c2e01e..fea0a5239ba0d 100644 --- a/src/plugins/links/public/components/dashboard_link/dashboard_link_destination_picker.tsx +++ b/src/plugins/links/public/components/dashboard_link/dashboard_link_destination_picker.tsx @@ -20,7 +20,7 @@ import { EuiFlexGroup, EuiComboBoxOptionOption, } from '@elastic/eui'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { DashboardItem } from '../../embeddable/types'; import { DashboardLinkStrings } from './dashboard_link_strings'; diff --git a/src/plugins/links/public/components/editor/link_destination.tsx b/src/plugins/links/public/components/editor/link_destination.tsx index bd33b6245ab51..5eb2d67a0d882 100644 --- a/src/plugins/links/public/components/editor/link_destination.tsx +++ b/src/plugins/links/public/components/editor/link_destination.tsx @@ -8,7 +8,7 @@ import React, { useState } from 'react'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { EuiFormRow } from '@elastic/eui'; import { diff --git a/src/plugins/links/public/components/editor/link_editor.tsx b/src/plugins/links/public/components/editor/link_editor.tsx index 7af2ddf1c57d5..ca3aeda7224bb 100644 --- a/src/plugins/links/public/components/editor/link_editor.tsx +++ b/src/plugins/links/public/components/editor/link_editor.tsx @@ -26,7 +26,7 @@ import { EuiFlyoutHeader, EuiRadioGroupOption, } from '@elastic/eui'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { LinkType, diff --git a/src/plugins/links/public/components/editor/links_editor.tsx b/src/plugins/links/public/components/editor/links_editor.tsx index 2a146ab5430cd..bd2da0041499d 100644 --- a/src/plugins/links/public/components/editor/links_editor.tsx +++ b/src/plugins/links/public/components/editor/links_editor.tsx @@ -28,7 +28,7 @@ import { EuiSwitch, EuiTitle, } from '@elastic/eui'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { Link, diff --git a/src/plugins/links/public/components/editor/links_editor_single_link.tsx b/src/plugins/links/public/components/editor/links_editor_single_link.tsx index e13913f1e349d..c69c33662c014 100644 --- a/src/plugins/links/public/components/editor/links_editor_single_link.tsx +++ b/src/plugins/links/public/components/editor/links_editor_single_link.tsx @@ -21,7 +21,7 @@ import { EuiSkeletonTitle, DraggableProvidedDragHandleProps, } from '@elastic/eui'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { LinkInfo } from '../../embeddable/types'; import { validateUrl } from '../external_link/external_link_tools'; diff --git a/src/plugins/links/public/editor/open_editor_flyout.tsx b/src/plugins/links/public/editor/open_editor_flyout.tsx index 3fc187a05be1c..8455ca16e604b 100644 --- a/src/plugins/links/public/editor/open_editor_flyout.tsx +++ b/src/plugins/links/public/editor/open_editor_flyout.tsx @@ -11,7 +11,7 @@ import { skip, take } from 'rxjs'; import { v4 as uuidv4 } from 'uuid'; import { EuiLoadingSpinner, EuiPanel } from '@elastic/eui'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { toMountPoint } from '@kbn/react-kibana-mount'; import { withSuspense } from '@kbn/shared-ux-utility'; diff --git a/src/plugins/links/public/editor/open_link_editor_flyout.tsx b/src/plugins/links/public/editor/open_link_editor_flyout.tsx index f8176d6e7e245..d3406264db4ad 100644 --- a/src/plugins/links/public/editor/open_link_editor_flyout.tsx +++ b/src/plugins/links/public/editor/open_link_editor_flyout.tsx @@ -10,7 +10,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { coreServices } from '../services/kibana_services'; import { Link } from '../../common/content_management'; diff --git a/src/plugins/links/public/embeddable/links_embeddable.tsx b/src/plugins/links/public/embeddable/links_embeddable.tsx index d803b9df9e8c5..dcc49a7265a43 100644 --- a/src/plugins/links/public/embeddable/links_embeddable.tsx +++ b/src/plugins/links/public/embeddable/links_embeddable.tsx @@ -11,7 +11,7 @@ import React, { createContext } from 'react'; import { unmountComponentAtNode } from 'react-dom'; import { distinctUntilChanged, skip, Subject, Subscription, switchMap } from 'rxjs'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { AttributeService, Embeddable, diff --git a/src/plugins/links/public/embeddable/links_embeddable_factory.test.ts b/src/plugins/links/public/embeddable/links_embeddable_factory.test.ts index 427827a1ace4b..d575c975e0295 100644 --- a/src/plugins/links/public/embeddable/links_embeddable_factory.test.ts +++ b/src/plugins/links/public/embeddable/links_embeddable_factory.test.ts @@ -12,7 +12,7 @@ import { LinksInput } from './types'; describe('linksFactory', () => { test('returns an empty object when not given proper meta information', () => { const linksFactory = new LinksFactoryDefinition(); - const settings = linksFactory.getPanelPlacementSettings({} as unknown as LinksInput, {}); + const settings = linksFactory.getLegacyPanelPlacementSettings({} as unknown as LinksInput, {}); expect(settings.height).toBeUndefined(); expect(settings.width).toBeUndefined(); expect(settings.strategy).toBeUndefined(); @@ -20,7 +20,7 @@ describe('linksFactory', () => { test('returns a horizontal layout', () => { const linksFactory = new LinksFactoryDefinition(); - const settings = linksFactory.getPanelPlacementSettings({} as unknown as LinksInput, { + const settings = linksFactory.getLegacyPanelPlacementSettings({} as unknown as LinksInput, { layout: 'horizontal', links: [], }); @@ -31,7 +31,7 @@ describe('linksFactory', () => { test('returns a vertical layout with the appropriate height', () => { const linksFactory = new LinksFactoryDefinition(); - const settings = linksFactory.getPanelPlacementSettings({} as unknown as LinksInput, { + const settings = linksFactory.getLegacyPanelPlacementSettings({} as unknown as LinksInput, { layout: 'vertical', links: [ { type: 'dashboardLink', destination: 'superDashboard1' }, diff --git a/src/plugins/links/public/embeddable/links_embeddable_factory.ts b/src/plugins/links/public/embeddable/links_embeddable_factory.ts index df629c9b1d053..9ff3877b8a42e 100644 --- a/src/plugins/links/public/embeddable/links_embeddable_factory.ts +++ b/src/plugins/links/public/embeddable/links_embeddable_factory.ts @@ -7,8 +7,8 @@ */ import { DASHBOARD_GRID_COLUMN_COUNT, PanelPlacementStrategy } from '@kbn/dashboard-plugin/public'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; -import { IProvidesPanelPlacementSettings } from '@kbn/dashboard-plugin/public/dashboard_container/component/panel_placement/types'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import { IProvidesLegacyPanelPlacementSettings } from '@kbn/dashboard-plugin/public'; import { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common'; import { EmbeddableFactory, @@ -46,7 +46,9 @@ const isLinksAttributes = (attributes?: unknown): attributes is LinksAttributes }; export class LinksFactoryDefinition - implements EmbeddableFactoryDefinition, IProvidesPanelPlacementSettings + implements + EmbeddableFactoryDefinition, + IProvidesLegacyPanelPlacementSettings { latestVersion?: string | undefined; telemetry?: @@ -64,10 +66,10 @@ export class LinksFactoryDefinition getIconForSavedObject: () => APP_ICON, }; - public getPanelPlacementSettings: IProvidesPanelPlacementSettings< + public getLegacyPanelPlacementSettings: IProvidesLegacyPanelPlacementSettings< LinksInput, LinksAttributes | unknown - >['getPanelPlacementSettings'] = (input, attributes) => { + >['getLegacyPanelPlacementSettings'] = (input, attributes) => { if (!isLinksAttributes(attributes) || !attributes.layout) { // if we have no information about the layout of this links panel defer to default panel size and placement. return {}; diff --git a/src/plugins/links/public/plugin.ts b/src/plugins/links/public/plugin.ts index ca13281fd2512..32788a2a283c1 100644 --- a/src/plugins/links/public/plugin.ts +++ b/src/plugins/links/public/plugin.ts @@ -12,7 +12,7 @@ import { } from '@kbn/content-management-plugin/public'; import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { DashboardStart } from '@kbn/dashboard-plugin/public'; -import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import type { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; diff --git a/x-pack/plugins/observability_solution/slo/kibana.jsonc b/x-pack/plugins/observability_solution/slo/kibana.jsonc index 587a9abe5e33f..052eaaae9e1d6 100644 --- a/x-pack/plugins/observability_solution/slo/kibana.jsonc +++ b/x-pack/plugins/observability_solution/slo/kibana.jsonc @@ -17,6 +17,7 @@ "charts", "contentManagement", "controls", + "dashboard", "data", "dataViews", "lens", @@ -49,8 +50,7 @@ "kibanaUtils", "unifiedSearch", "embeddable", - "ingestPipelines", - "dashboard" + "ingestPipelines" ] } } diff --git a/x-pack/plugins/observability_solution/slo/public/plugin.ts b/x-pack/plugins/observability_solution/slo/public/plugin.ts index 417ac1fbf75f3..2f6dbaab39493 100644 --- a/x-pack/plugins/observability_solution/slo/public/plugin.ts +++ b/x-pack/plugins/observability_solution/slo/public/plugin.ts @@ -16,7 +16,6 @@ import { } from '@kbn/core/public'; import { BehaviorSubject, firstValueFrom } from 'rxjs'; import { registerReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { registerDashboardPanelPlacementSetting } from '@kbn/dashboard-plugin/public'; import { SloPublicPluginsSetup, SloPublicPluginsStart } from './types'; import { PLUGIN_NAME, sloAppId } from '../common'; import type { SloPublicSetup, SloPublicStart } from './types'; @@ -95,7 +94,8 @@ export class SloPlugin const hasPlatinumLicense = license.hasAtLeast('platinum'); if (hasPlatinumLicense) { - registerDashboardPanelPlacementSetting( + const [coreStart, pluginsStart] = await coreSetup.getStartServices(); + pluginsStart.dashboard.registerDashboardPanelPlacementSetting( SLO_OVERVIEW_EMBEDDABLE_ID, (serializedState: SloOverviewEmbeddableState | undefined) => { if (serializedState?.showAllGroupByInstances || serializedState?.groupFilters) { @@ -105,8 +105,6 @@ export class SloPlugin } ); registerReactEmbeddableFactory(SLO_OVERVIEW_EMBEDDABLE_ID, async () => { - const [coreStart, pluginsStart] = await coreSetup.getStartServices(); - const deps = { ...coreStart, ...pluginsStart }; const { getOverviewEmbeddableFactory } = await import( @@ -127,8 +125,6 @@ export class SloPlugin registerSloAlertsEmbeddableFactory(); registerReactEmbeddableFactory(SLO_ERROR_BUDGET_ID, async () => { - const [coreStart, pluginsStart] = await coreSetup.getStartServices(); - const deps = { ...coreStart, ...pluginsStart }; const { getErrorBudgetEmbeddableFactory } = await import( diff --git a/x-pack/plugins/observability_solution/slo/public/types.ts b/x-pack/plugins/observability_solution/slo/public/types.ts index c480f79b02e78..76e2bcad185ba 100644 --- a/x-pack/plugins/observability_solution/slo/public/types.ts +++ b/x-pack/plugins/observability_solution/slo/public/types.ts @@ -15,6 +15,7 @@ import type { } from '@kbn/observability-shared-plugin/public'; import { AiopsPluginStart } from '@kbn/aiops-plugin/public/types'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import { DashboardStart } from '@kbn/dashboard-plugin/public'; import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; import type { @@ -76,6 +77,7 @@ export interface SloPublicPluginsStart { aiops: AiopsPluginStart; cases: CasesPublicStart; cloud?: CloudStart; + dashboard: DashboardStart; dataViewEditor: DataViewEditorStart; fieldFormats: FieldFormatsStart; observability: ObservabilityPublicStart;