Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Serverless Elasticsearch] Fix user is blocked from moving forward when opening Discover, Dashboard, or Visualize Library #164709

Merged
merged 17 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ x-pack/plugins/monitoring @elastic/infra-monitoring-ui
src/plugins/navigation @elastic/appex-sharedux
src/plugins/newsfeed @elastic/kibana-core
test/common/plugins/newsfeed @elastic/kibana-core
src/plugins/no_data_page @elastic/appex-sharedux
x-pack/plugins/notifications @elastic/appex-sharedux
packages/kbn-object-versioning @elastic/appex-sharedux
x-pack/plugins/observability_ai_assistant @elastic/obs-ai-assistant
Expand Down
3 changes: 3 additions & 0 deletions config/serverless.es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ telemetry.labels.serverless: search

# Alerts config
xpack.actions.enabledActionTypes: ['.email', '.index', '.slack', '.jira', '.webhook', '.teams']

# Customize empty page state for analytics apps
no_data_page.analyticsNoDataPageFlavor: 'serverless_search'
4 changes: 4 additions & 0 deletions docs/developer/plugin-list.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ It also provides a stateful version of it on the start contract.
Content is fetched from the remote (https://feeds.elastic.co) once a day, with periodic checks if the content needs to be refreshed. All newsfeed content is hosted remotely.


|{kib-repo}blob/{branch}/src/plugins/no_data_page/README.md[noDataPage]
|Helps to globally configure the no data page components


|{kib-repo}blob/{branch}/src/plugins/presentation_util/README.mdx[presentationUtil]
|The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas).

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@
"@kbn/navigation-plugin": "link:src/plugins/navigation",
"@kbn/newsfeed-plugin": "link:src/plugins/newsfeed",
"@kbn/newsfeed-test-plugin": "link:test/common/plugins/newsfeed",
"@kbn/no-data-page-plugin": "link:src/plugins/no_data_page",
"@kbn/notifications-plugin": "link:x-pack/plugins/notifications",
"@kbn/object-versioning": "link:packages/kbn-object-versioning",
"@kbn/observability-ai-assistant-plugin": "link:x-pack/plugins/observability_ai_assistant",
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ pageLoadAssetSize:
monitoring: 80000
navigation: 37269
newsfeed: 42228
noDataPage: 5000
observability: 115443
observabilityAIAssistant: 25000
observabilityOnboarding: 19573
Expand Down
4 changes: 3 additions & 1 deletion packages/shared-ux/card/no_data/impl/src/no_data_card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export const NoDataCard = ({ href: srcHref, category, description, ...props }: P

return (
<RedirectAppLinksContainer>
<Component {...{ ...props, href, canAccessFleet, description }} />
<Component
{...{ ...props, href, canAccessFleet: props.canAccessFleet ?? canAccessFleet, description }}
/>
</RedirectAppLinksContainer>
);
};
2 changes: 1 addition & 1 deletion packages/shared-ux/card/no_data/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@ export type NoDataCardComponentProps = Partial<
/**
* Props for the `NoDataCard` sevice-connected component.
*/
export type NoDataCardProps = Omit<NoDataCardComponentProps, 'canAccessFleet'>;
export type NoDataCardProps = NoDataCardComponentProps;
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import React from 'react';
import { act } from 'react-dom/test-utils';

import { mountWithIntl } from '@kbn/test-jest-helpers';
import { I18nProvider } from '@kbn/i18n-react';

import { KibanaNoDataPage } from '@kbn/shared-ux-page-kibana-no-data';
import { render, screen } from '@testing-library/react';

import { AnalyticsNoDataPage } from './analytics_no_data_page.component';
import { AnalyticsNoDataPageProvider } from './services';
Expand All @@ -28,6 +31,7 @@ describe('AnalyticsNoDataPageComponent', () => {
onDataViewCreated={onDataViewCreated}
kibanaGuideDocLink={'http://www.test.com'}
showPlainSpinner={false}
prependBasePath={(path: string) => path}
/>
</AnalyticsNoDataPageProvider>
);
Expand All @@ -52,6 +56,7 @@ describe('AnalyticsNoDataPageComponent', () => {
kibanaGuideDocLink={'http://www.test.com'}
allowAdHocDataView={true}
showPlainSpinner={false}
prependBasePath={(path: string) => path}
/>
</AnalyticsNoDataPageProvider>
);
Expand All @@ -61,4 +66,86 @@ describe('AnalyticsNoDataPageComponent', () => {
expect(component.find(KibanaNoDataPage).length).toBe(1);
expect(component.find(KibanaNoDataPage).props().allowAdHocDataView).toBe(true);
});

describe('no data state', () => {
describe('kibana flavor', () => {
it('renders add integrations card', async () => {
render(
<I18nProvider>
<AnalyticsNoDataPageProvider {...{ ...services, hasESData: async () => false }}>
<AnalyticsNoDataPage
onDataViewCreated={onDataViewCreated}
kibanaGuideDocLink={'http://www.test.com'}
showPlainSpinner={false}
prependBasePath={(path: string) => path}
/>
</AnalyticsNoDataPageProvider>
</I18nProvider>
);

await screen.findByTestId('kbnOverviewAddIntegrations');
await screen.getAllByText('Add integrations');
});

it('renders disabled add integrations card when fleet is not available', async () => {
render(
<I18nProvider>
<AnalyticsNoDataPageProvider
{...{ ...services, hasESData: async () => false, canAccessFleet: false }}
>
<AnalyticsNoDataPage
onDataViewCreated={onDataViewCreated}
kibanaGuideDocLink={'http://www.test.com'}
showPlainSpinner={false}
prependBasePath={(path: string) => path}
/>
</AnalyticsNoDataPageProvider>
</I18nProvider>
);

await screen.findByTestId('kbnOverviewAddIntegrations');
await screen.getByText('Contact your administrator');
});
});

describe('serverless_search flavor', () => {
it('renders getting started card', async () => {
render(
<I18nProvider>
<AnalyticsNoDataPageProvider {...{ ...services, hasESData: async () => false }}>
<AnalyticsNoDataPage
pageFlavor={'serverless_search'}
onDataViewCreated={onDataViewCreated}
kibanaGuideDocLink={'http://www.test.com'}
showPlainSpinner={false}
prependBasePath={(path: string) => path}
/>
</AnalyticsNoDataPageProvider>
</I18nProvider>
);

await screen.findByTestId('kbnOverviewElasticsearchGettingStarted');
});

it('renders the same getting started card when fleet is not available', async () => {
render(
<I18nProvider>
<AnalyticsNoDataPageProvider
{...{ ...services, hasESData: async () => false, canAccessFleet: false }}
>
<AnalyticsNoDataPage
onDataViewCreated={onDataViewCreated}
kibanaGuideDocLink={'http://www.test.com'}
showPlainSpinner={false}
prependBasePath={(path: string) => path}
pageFlavor={'serverless_search'}
/>
</AnalyticsNoDataPageProvider>
</I18nProvider>
);

await screen.findByTestId('kbnOverviewElasticsearchGettingStarted');
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { KibanaNoDataPage } from '@kbn/shared-ux-page-kibana-no-data';
import { KibanaNoDataPageProps } from '@kbn/shared-ux-page-kibana-no-data-types';
import { AnalyticsNoDataPageFlavor } from '@kbn/shared-ux-page-analytics-no-data-types';

/**
* Props for the pure component.
Expand All @@ -21,26 +23,63 @@ export interface Props {
allowAdHocDataView?: boolean;
/** if the kibana instance is customly branded */
showPlainSpinner: boolean;
/** The flavor of the empty page to use. */
pageFlavor?: AnalyticsNoDataPageFlavor;
prependBasePath: (path: string) => string;
}

const solution = i18n.translate('sharedUXPackages.noDataConfig.analytics', {
defaultMessage: 'Analytics',
});

const pageTitle = i18n.translate('sharedUXPackages.noDataConfig.analyticsPageTitle', {
defaultMessage: 'Welcome to Analytics!',
});

const addIntegrationsTitle = i18n.translate('sharedUXPackages.noDataConfig.addIntegrationsTitle', {
defaultMessage: 'Add integrations',
});

const addIntegrationsDescription = i18n.translate(
'sharedUXPackages.noDataConfig.addIntegrationsDescription',
{
defaultMessage: 'Use Elastic Agent to collect data and build out Analytics solutions.',
}
);
const flavors: {
[K in AnalyticsNoDataPageFlavor]: (deps: {
kibanaGuideDocLink: string;
prependBasePath: (path: string) => string;
}) => KibanaNoDataPageProps['noDataConfig'];
} = {
kibana: ({ kibanaGuideDocLink }) => ({
solution: i18n.translate('sharedUXPackages.noDataConfig.analytics', {
defaultMessage: 'Analytics',
}),
pageTitle: i18n.translate('sharedUXPackages.noDataConfig.analyticsPageTitle', {
defaultMessage: 'Welcome to Analytics!',
}),
logo: 'logoKibana',
action: {
elasticAgent: {
title: i18n.translate('sharedUXPackages.noDataConfig.addIntegrationsTitle', {
defaultMessage: 'Add integrations',
}),
description: i18n.translate('sharedUXPackages.noDataConfig.addIntegrationsDescription', {
defaultMessage: 'Use Elastic Agent to collect data and build out Analytics solutions.',
}),
'data-test-subj': 'kbnOverviewAddIntegrations',
},
},
docsLink: kibanaGuideDocLink,
}),
serverless_search: ({ prependBasePath }) => ({
solution: i18n.translate('sharedUXPackages.noDataConfig.elasticsearch', {
defaultMessage: 'Elasticsearch',
}),
pageTitle: i18n.translate('sharedUXPackages.noDataConfig.elasticsearchPageTitle', {
defaultMessage: 'Welcome to Elasticsearch!',
}),
logo: 'logoElasticsearch',
action: {
elasticsearch: {
title: i18n.translate('sharedUXPackages.noDataConfig.elasticsearchTitle', {
defaultMessage: 'Get started',
}),
description: i18n.translate('sharedUXPackages.noDataConfig.elasticsearchDescription', {
defaultMessage:
'Set up your programming language client, ingest some data, and start searching.',
}),
'data-test-subj': 'kbnOverviewElasticsearchGettingStarted',
href: prependBasePath('/app/elasticsearch/'),
Copy link
Contributor Author

@Dosant Dosant Aug 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

href on this level is added only for "serverless_search" flavor, but not for "kibana" flavor because the integrations page is already hardcoded inside the component. The NoDataCard must be refactored to be fully configurable and don't hardcode fleet stuff.

/** force the no data card to be shown **/
canAccessFleet: true,
Copy link
Contributor Author

@Dosant Dosant Aug 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing/faking this explicitly so that NoDataCard doesn't fallback to the "no access to fleet" page. This needs to be refactored.

Currently, it is ugly: even though the NoDataCard seems to be generic and text and links can be configured through props, some "fleet" logic parts are still hardcoded inside the component. For example, the component checks the access to fleet through context.

},
},
}),
};

/**
* A pure component of an entire page that can be displayed when Kibana "has no data", specifically for Analytics.
Expand All @@ -50,20 +89,13 @@ export const AnalyticsNoDataPage = ({
onDataViewCreated,
allowAdHocDataView,
showPlainSpinner,
prependBasePath,
pageFlavor = 'kibana',
}: Props) => {
const noDataConfig = {
solution,
pageTitle,
logo: 'logoKibana',
action: {
elasticAgent: {
title: addIntegrationsTitle,
description: addIntegrationsDescription,
'data-test-subj': 'kbnOverviewAddIntegrations',
},
},
docsLink: kibanaGuideDocLink,
};
const noDataConfig: KibanaNoDataPageProps['noDataConfig'] = flavors[pageFlavor]({
kibanaGuideDocLink,
prependBasePath,
});

return (
<KibanaNoDataPage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ describe('AnalyticsNoDataPage', () => {
expect(component.find(Component).props().kibanaGuideDocLink).toBe(services.kibanaGuideDocLink);
expect(component.find(Component).props().onDataViewCreated).toBe(onDataViewCreated);
expect(component.find(Component).props().allowAdHocDataView).toBe(true);
expect(component.find(Component).props().prependBasePath).toBe(services.prependBasePath);
expect(component.find(Component).props().pageFlavor).toBe(services.pageFlavor);
});

it('passes correct boolean value to showPlainSpinner', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const AnalyticsNoDataPage = ({
allowAdHocDataView,
}: AnalyticsNoDataPageProps) => {
const services = useServices();
const { kibanaGuideDocLink, customBranding } = services;
const { kibanaGuideDocLink, customBranding, prependBasePath, pageFlavor } = services;
const { hasCustomBranding$ } = customBranding;
const showPlainSpinner = useObservable(hasCustomBranding$) ?? false;

Expand All @@ -32,6 +32,8 @@ export const AnalyticsNoDataPage = ({
allowAdHocDataView,
kibanaGuideDocLink,
showPlainSpinner,
prependBasePath,
pageFlavor,
}}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ export const AnalyticsNoDataPageProvider: FC<AnalyticsNoDataPageServices> = ({
children,
...services
}) => {
const { kibanaGuideDocLink, customBranding } = services;
const { kibanaGuideDocLink, customBranding, prependBasePath, pageFlavor } = services;

return (
<Context.Provider value={{ kibanaGuideDocLink, customBranding }}>
<Context.Provider value={{ kibanaGuideDocLink, customBranding, prependBasePath, pageFlavor }}>
<KibanaNoDataPageProvider {...services}>{children}</KibanaNoDataPageProvider>
</Context.Provider>
);
Expand All @@ -48,6 +48,8 @@ export const AnalyticsNoDataPageKibanaProvider: FC<AnalyticsNoDataPageKibanaDepe
customBranding: {
hasCustomBranding$: dependencies.coreStart.customBranding.hasCustomBranding$,
},
prependBasePath: dependencies.coreStart.http.basePath.prepend,
pageFlavor: dependencies.noDataPage?.getAnalyticsNoDataPageFlavor() ?? 'kibana',
};
return (
<Context.Provider {...{ value }}>
Expand Down
2 changes: 2 additions & 0 deletions packages/shared-ux/page/analytics_no_data/impl/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"@kbn/shared-ux-page-analytics-no-data-types",
"@kbn/test-jest-helpers",
"@kbn/shared-ux-page-analytics-no-data-mocks",
"@kbn/shared-ux-page-kibana-no-data-types",
"@kbn/i18n-react",
],
"exclude": [
"target/**/*",
Expand Down
4 changes: 4 additions & 0 deletions packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const getServicesMock = () => {
...getKibanaNoDataPageServicesMock(),
kibanaGuideDocLink: 'Kibana guide',
customBranding: { hasCustomBranding$: of(false) },
prependBasePath: (path) => path,
pageFlavor: 'kibana',
};

return services;
Expand All @@ -26,6 +28,8 @@ export const getServicesMockCustomBranding = () => {
// this mock will have custom branding set to true
customBranding: { hasCustomBranding$: of(true) },
kibanaGuideDocLink: 'Kibana guide',
prependBasePath: (path) => path,
pageFlavor: 'kibana',
};

return services;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export class StorybookMock extends AbstractStorybookMock<
customBranding: {
hasCustomBranding$: of(false),
},
pageFlavor: 'kibana',
prependBasePath: (path) => path,
...kibanaNoDataMock.getServices(params),
};
}
Expand Down
Loading