Skip to content

Commit

Permalink
[Serverless Elasticsearch] Fix user is blocked from moving forward wh…
Browse files Browse the repository at this point in the history
…en opening Discover, Dashboard, or Visualize Library
  • Loading branch information
Dosant authored Aug 28, 2023
1 parent 84b683b commit 243142d
Show file tree
Hide file tree
Showing 52 changed files with 496 additions and 56 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
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/'),
/** force the no data card to be shown **/
canAccessFleet: true,
},
},
}),
};

/**
* 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

0 comments on commit 243142d

Please sign in to comment.