Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
2534188
feat(ingest-hub): navigate to [Metrics AWS] Overview dashboard from S…
Supplementing Sep 10, 2026
1a75c6b
Merge branch 'main' into aws-overview-dashboard-navigation
Supplementing Sep 11, 2026
e395e9f
Changes from node scripts/check
kibanamachine Sep 11, 2026
09686e8
fix(ingest-hub): replace useInstalledContent with dedicated useAwsOve…
Supplementing Sep 11, 2026
22106dd
Merge branch 'main' into aws-overview-dashboard-navigation
Supplementing Sep 11, 2026
4d6fa31
Changes from node scripts/check
kibanamachine Sep 11, 2026
9dad9d5
refactor(ingest-hub): match AWS overview dashboard by ID not title
Supplementing Sep 11, 2026
eb43ca8
fix(ingest-hub): handle non-primary spaces in useAwsOverviewDashboardUrl
Supplementing Sep 14, 2026
562d5b1
Merge branch 'main' into aws-overview-dashboard-navigation
Supplementing Sep 14, 2026
7735c6b
Changes from node scripts/check
kibanamachine Sep 14, 2026
23f5436
fix(ingest-hub): cast test refs as KibanaAssetReference to satisfy ts…
Supplementing Sep 14, 2026
0a6e0df
Changes from node scripts/check
kibanamachine Sep 14, 2026
59225bc
Update x-pack/platform/plugins/shared/ingest_hub/public/onboarding/st…
Supplementing Sep 14, 2026
720bbb3
test(ingest-hub): assert no href on first render when spaces resolves…
Supplementing Sep 15, 2026
5a7bfe1
nit(ingest-hub): use full GitHub link for AWS overview dashboard source
Supplementing Sep 15, 2026
56dcad1
Merge branch 'main' into aws-overview-dashboard-navigation
Supplementing Sep 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ jest.mock('./installed_content', () => ({
InstalledContent: () => <div data-test-subj="mock-installed-content" />,
}));

jest.mock('./use_aws_overview_dashboard_url', () => ({
useAwsOverviewDashboardUrl: jest.fn(),
}));

jest.mock('./agent_setup_callout', () => ({
AgentSetupCallout: () => (
<div data-test-subj="mock-agent-callout">
Expand All @@ -51,17 +55,20 @@ import { useOnboardingFlow } from '../../onboarding_flow_context';
import useSessionStorage from 'react-use/lib/useSessionStorage';
import { useGetPackageInfoByKeyQuery } from '@kbn/fleet-plugin/public';
import { useServiceDataDetection } from './use_service_data_detection';
import { useAwsOverviewDashboardUrl } from './use_aws_overview_dashboard_url';
import { DetectAndReviewStep } from '.';

const mockUseOnboardingFlow = useOnboardingFlow as jest.Mock;
const mockUseSessionStorage = useSessionStorage as jest.Mock;
const mockUseGetPackageInfoByKeyQuery = useGetPackageInfoByKeyQuery as jest.Mock;
const mockUseServiceDataDetection = useServiceDataDetection as jest.Mock;
const mockUseAwsOverviewDashboardUrl = useAwsOverviewDashboardUrl as jest.Mock;

function setupMocks({
deploymentMethod = 'managed_integration' as 'managed_integration' | 'agent_based' | 'ecf',
selectedServiceIds = [] as string[],
packageData = undefined as object | undefined,
overviewHref = undefined as string | undefined,
} = {}) {
mockUseOnboardingFlow.mockReturnValue({
servicesStep: { selectedServiceIds },
Expand All @@ -86,6 +93,7 @@ function setupMocks({
totalCount: 0,
isTimedOut: false,
});
mockUseAwsOverviewDashboardUrl.mockReturnValue(overviewHref);
}

function renderStep(props: { onContinue?: () => void; onBack?: () => void } = {}) {
Expand Down Expand Up @@ -145,6 +153,20 @@ describe('DetectAndReviewStep', () => {
fireEvent.click(screen.getByText('Back'));
expect(onBack).toHaveBeenCalledTimes(1);
});

it('has href to the [Metrics AWS] Overview dashboard when the hook resolves one', () => {
setupMocks({ overviewHref: '/base/app/dashboards#/view/aws-overview-id' });
renderStep();
const btn = screen.getByTestId('detectAndReviewStep-continueButton');
expect(btn).toHaveAttribute('href', '/base/app/dashboards#/view/aws-overview-id');
});

it('has no href when the hook returns undefined', () => {
setupMocks({ overviewHref: undefined });
renderStep();
const btn = screen.getByTestId('detectAndReviewStep-continueButton');
expect(btn).not.toHaveAttribute('href');
});
});

describe('deployment summary', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { useServiceDataDetection } from './use_service_data_detection';
import { DeploymentSummary } from './deployment_summary';
import { AgentSetupCallout } from './agent_setup_callout';
import { InstalledContent } from './installed_content';
import { useAwsOverviewDashboardUrl } from './use_aws_overview_dashboard_url';

const DEFAULT_SERVICE_SETTINGS: ServiceSettingsPersistedState = {
globalRegion: '',
Expand Down Expand Up @@ -77,6 +78,9 @@ export function DetectAndReviewStep({ onContinue, onBack }: DetectAndReviewStepP
const installedKibana: KibanaAssetReference[] = installationInfo?.installed_kibana ?? [];
const installedEs: EsAssetReference[] = installationInfo?.installed_es ?? [];

// Resolve the href to [Metrics AWS] Overview for the "Take me to my data" button.
const overviewHref = useAwsOverviewDashboardUrl(installationInfo);

const hasDeployedServices = selectedServiceIds.length > 0;

return (
Expand Down Expand Up @@ -150,6 +154,7 @@ export function DetectAndReviewStep({ onContinue, onBack }: DetectAndReviewStepP
fill
iconType="sortRight"
iconSide="right"
href={overviewHref}
onClick={onContinue}
data-test-subj="detectAndReviewStep-continueButton"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* 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 { act, renderHook } from '@testing-library/react';
import type { KibanaAssetReference } from '@kbn/fleet-plugin/common';

jest.mock('@kbn/kibana-react-plugin/public', () => ({
useKibana: jest.fn(),
}));

import { useKibana } from '@kbn/kibana-react-plugin/public';
import {
useAwsOverviewDashboardUrl,
type InstallationSnapshot,
} from './use_aws_overview_dashboard_url';

const mockUseKibana = useKibana as jest.Mock;
const mockPrepend = jest.fn((path: string) => `/base${path}`);
const mockGetActiveSpace = jest.fn();

function setupKibana(spaceId?: string) {
mockGetActiveSpace.mockResolvedValue(spaceId ? { id: spaceId } : undefined);
mockUseKibana.mockReturnValue({
services: {
http: { basePath: { prepend: mockPrepend } },
spaces: spaceId !== undefined ? { getActiveSpace: mockGetActiveSpace } : undefined,
},
});
}

beforeEach(() => {
jest.clearAllMocks();
setupKibana('default');
});

// The canonical package ID for [Metrics AWS] Overview, as shipped in elastic/integrations.
const OVERVIEW_ID = 'aws-fac28650-7349-11e9-816b-07687310a99a';

// Cast as KibanaAssetReference — avoids assigning the string literal 'dashboard' to the
// KibanaSavedObjectType enum under CI's tsconfig.type_check.json compiled resolution.
const primaryRef = { id: OVERVIEW_ID, type: 'dashboard' } as KibanaAssetReference;
const otherRef = { id: 'aws-ec2-id', type: 'dashboard' } as KibanaAssetReference;

describe('useAwsOverviewDashboardUrl', () => {
it('returns undefined when installationInfo is undefined', async () => {
const { result } = renderHook(() => useAwsOverviewDashboardUrl(undefined));
await act(async () => {});
expect(result.current).toBeUndefined();
});

describe('primary space (installed_kibana_space_id === currentSpaceId)', () => {
it('returns the basePath-prefixed URL when the overview ref is in installed_kibana', async () => {
const info: InstallationSnapshot = {
installed_kibana: [otherRef, primaryRef],
installed_kibana_space_id: 'default',
};
const { result } = renderHook(() => useAwsOverviewDashboardUrl(info));
await act(async () => {});
expect(result.current).toBe(`/base/app/dashboards#/view/${OVERVIEW_ID}`);
});

it('returns undefined when the overview dashboard is not in installed_kibana', async () => {
const info: InstallationSnapshot = {
installed_kibana: [otherRef],
installed_kibana_space_id: 'default',
};
const { result } = renderHook(() => useAwsOverviewDashboardUrl(info));
await act(async () => {});
expect(result.current).toBeUndefined();
});

it('treats missing installed_kibana_space_id as primary space', async () => {
const info: InstallationSnapshot = {
installed_kibana: [primaryRef],
};
const { result } = renderHook(() => useAwsOverviewDashboardUrl(info));
await act(async () => {});
expect(result.current).toBe(`/base/app/dashboards#/view/${OVERVIEW_ID}`);
});
});

describe('non-primary space (installed_kibana_space_id !== currentSpaceId)', () => {
const SPACE_LOCAL_ID = 'some-space-specific-uuid';

beforeEach(() => setupKibana('my-space'));

it('returns the URL using the space-local id matched by originId', async () => {
const info: InstallationSnapshot = {
installed_kibana: [primaryRef], // primary space refs (different space)
installed_kibana_space_id: 'default',
additional_spaces_installed_kibana: {
'my-space': [
{ id: SPACE_LOCAL_ID, originId: OVERVIEW_ID, type: 'dashboard' } as KibanaAssetReference,
],
},
};
const { result } = renderHook(() => useAwsOverviewDashboardUrl(info));
await act(async () => {});
expect(result.current).toBe(`/base/app/dashboards#/view/${SPACE_LOCAL_ID}`);
});

it('returns undefined when the current space has no additional_spaces entry', async () => {
const info: InstallationSnapshot = {
installed_kibana: [primaryRef],
installed_kibana_space_id: 'default',
additional_spaces_installed_kibana: {},
};
const { result } = renderHook(() => useAwsOverviewDashboardUrl(info));
await act(async () => {});
expect(result.current).toBeUndefined();
});

it('returns undefined when the space entry exists but has no overview dashboard', async () => {
const info: InstallationSnapshot = {
installed_kibana: [primaryRef],
installed_kibana_space_id: 'default',
additional_spaces_installed_kibana: {
'my-space': [{ id: 'some-other-uuid', originId: 'aws-ec2-id', type: 'dashboard' } as KibanaAssetReference],
},
};
const { result } = renderHook(() => useAwsOverviewDashboardUrl(info));
await act(async () => {});
expect(result.current).toBeUndefined();
});
});

describe('spaces service unavailable', () => {
beforeEach(() => setupKibana(undefined));

it('falls back to primary space logic when spaces service is absent', async () => {
const info: InstallationSnapshot = {
installed_kibana: [primaryRef],
installed_kibana_space_id: 'default',
};
const { result } = renderHook(() => useAwsOverviewDashboardUrl(info));
await act(async () => {});
expect(result.current).toBe(`/base/app/dashboards#/view/${OVERVIEW_ID}`);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useEffect, useState } from 'react';
import type { CoreStart } from '@kbn/core/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import type { KibanaAssetReference } from '@kbn/fleet-plugin/common';
import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';

/**
* Canonical saved-object ID of the `[Metrics AWS] Overview` dashboard shipped with
* the `aws` integration package (elastic/integrations). Stable across renames.
*
* Source: packages/aws/kibana/dashboard/aws-fac28650-7349-11e9-816b-07687310a99a.json
Comment thread
Supplementing marked this conversation as resolved.
Outdated
*/
const AWS_METRICS_OVERVIEW_DASHBOARD_ID = 'aws-fac28650-7349-11e9-816b-07687310a99a';

/**
* Minimal slice of Fleet's InstallationInfo needed for space-aware dashboard resolution.
* InstallationInfo is not exported from @kbn/fleet-plugin/common so we define the subset.
*/
export interface InstallationSnapshot {
installed_kibana: KibanaAssetReference[];
/** The space where the package's Kibana assets were originally installed. */
installed_kibana_space_id?: string;
/** Space-local asset refs for any spaces beyond the primary installation space. */
additional_spaces_installed_kibana?: Record<string, KibanaAssetReference[]>;
}

/**
* Resolves the basePath-prefixed href to the `[Metrics AWS] Overview` dashboard,
* matched by dashboard ID (not title) following Fleet's `getDashboardIdForSpace` pattern
* (x-pack/platform/plugins/shared/fleet/public/.../dashboard_helpers.ts):
*
* - Primary space (`installed_kibana_space_id === currentSpaceId`): the canonical package
* ID is the saved-object ID directly — look in `installed_kibana`.
* - Any other space: Fleet re-keys saved objects; look in `additional_spaces_installed_kibana`
* for a ref where `originId === canonicalId` and use that ref's space-local `id`.
*
* Returns `undefined` while the current space is still resolving, or if the dashboard is
* not installed in the current space.
*/
export function useAwsOverviewDashboardUrl(
installationInfo: InstallationSnapshot | undefined
): string | undefined {
const { services } = useKibana<CoreStart & { spaces?: SpacesPluginStart }>();

// Resolve the current space ID. Defaults to 'default' so the primary-space path works
// immediately on first render in the common case. A non-default space triggers a
// re-render once getActiveSpace() resolves.
const [currentSpaceId, setCurrentSpaceId] = useState<string>('default');
useEffect(() => {
if (!services.spaces) return;
services.spaces.getActiveSpace().then((space) => setCurrentSpaceId(space.id));
}, [services.spaces]);

if (!installationInfo) return undefined;
Comment thread
Supplementing marked this conversation as resolved.
Outdated

const { installed_kibana, installed_kibana_space_id, additional_spaces_installed_kibana } =
installationInfo;

let dashboardId: string | undefined;

if (!installed_kibana_space_id || installed_kibana_space_id === currentSpaceId) {
// Primary space: the canonical package ID is the saved-object id directly.
// KibanaSavedObjectType.dashboard === 'dashboard' — literal avoids runtime enum import.
const ref = installed_kibana.find(
(k) => k.type === 'dashboard' && k.id === AWS_METRICS_OVERVIEW_DASHBOARD_ID
);
dashboardId = ref?.id;
} else {
// Non-primary space: Fleet re-keys the saved object; match by originId.
const spaceRefs = additional_spaces_installed_kibana?.[currentSpaceId];
const ref = spaceRefs?.find(
(k) => k.type === 'dashboard' && k.originId === AWS_METRICS_OVERVIEW_DASHBOARD_ID
);
dashboardId = ref?.id;
}

return dashboardId
? services.http.basePath.prepend(`/app/dashboards#/view/${dashboardId}`)
: undefined;
}
Loading