Skip to content

Commit

Permalink
[ES|QL] Bypass no data views screen (elastic#174316)
Browse files Browse the repository at this point in the history
> Derived from elastic#173068
> Further addresses elastic#169873

## Summary

- We're adding a link to the No Data Prompt to send a person to explore
their data using ES|QL if there exists any ingested data but no data
view.
- This PR populates the query bar with the first available index (some
special handling for logs index.
elastic@2a9edec)
- All consumers of the prompt/no data view are updated in this PR.
- [x] ~There's an issue where, if you're in Discover, clicking the link
won't refresh the page. .~ This is fixed on Discover side by
reinitializing the state container when user clicks the "try es|ql" link
and URL state updates.
- [x] ~There is an issue that you can save the es|ql chart from
Discover, but Dashboard's empty screen blocks the navigation because
data views don't exist
elastic#174316 (comment).
This is fixed by allowing the dashboard to work without the default data
view. Hopefully, this won't lead to major issues
- [x] ~ES|QL panels can't be created without the default data views~
this is fixed by trying to fallback to an ad-hoc dataview, plan to move
that code to the utils introduced here
elastic#174736
- [x] fix circular deps
- [x] Add functional tests


## Visuals



https://github.com/elastic/kibana/assets/7784120/af3592c1-f4c8-43bb-a128-3268b7761367




### Storybook Stories

#### Can access ES|QL

![Screenshot 2024-01-31 at 17 05
47](https://github.com/elastic/kibana/assets/7784120/370d0351-198e-4dc3-b22e-86f497ad4df5)


#### Cannot access (e.g. preview is unavailable - _not implemented_)

![Screenshot 2024-01-31 at 17 05
59](https://github.com/elastic/kibana/assets/7784120/c2bf52ab-9fa8-4f25-9e5d-512d4f4342fa)


---------

Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
Co-authored-by: Rachel Shen <rshen@elastic.co>
Co-authored-by: Anton Dosov <anton.dosov@elastic.co>
  • Loading branch information
4 people authored and fkanout committed Mar 4, 2024
1 parent d06f087 commit 43f134f
Show file tree
Hide file tree
Showing 52 changed files with 553 additions and 106 deletions.
2 changes: 2 additions & 0 deletions packages/deeplinks/analytics/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export const DISCOVER_APP_ID = 'discover';
export const DASHBOARD_APP_ID = 'dashboards';

export const VISUALIZE_APP_ID = 'visualize';

export const DISCOVER_ESQL_LOCATOR = 'DISCOVER_ESQL_LOCATOR';
9 changes: 8 additions & 1 deletion packages/deeplinks/analytics/deep_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@
* Side Public License, v 1.
*/

import { DASHBOARD_APP_ID, DISCOVER_APP_ID, VISUALIZE_APP_ID } from './constants';
import {
DASHBOARD_APP_ID,
DISCOVER_APP_ID,
DISCOVER_ESQL_LOCATOR,
VISUALIZE_APP_ID,
} from './constants';

export type AppId = typeof DISCOVER_APP_ID | typeof DASHBOARD_APP_ID | typeof VISUALIZE_APP_ID;

export type DeepLinkId = AppId;

export type LocatorId = typeof DISCOVER_ESQL_LOCATOR;
7 changes: 6 additions & 1 deletion packages/deeplinks/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
* Side Public License, v 1.
*/

export { DASHBOARD_APP_ID, DISCOVER_APP_ID, VISUALIZE_APP_ID } from './constants';
export {
DASHBOARD_APP_ID,
DISCOVER_APP_ID,
VISUALIZE_APP_ID,
DISCOVER_ESQL_LOCATOR,
} from './constants';

export type { AppId, DeepLinkId } from './deep_links';
1 change: 1 addition & 0 deletions packages/kbn-es-query/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export {
migrateFilter,
fromCombinedFilter,
isOfQueryType,
isOfEsqlQueryType,
isOfAggregateQueryType,
getAggregateQueryMode,
getLanguageDisplayName,
Expand Down
9 changes: 9 additions & 0 deletions packages/kbn-es-query/src/es_query/es_aggregate_query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ export function isOfAggregateQueryType(
return Boolean(query && ('sql' in query || 'esql' in query));
}

/**
* True if the query is of type AggregateQuery and is of type esql, false otherwise.
*/
export function isOfEsqlQueryType(
query?: AggregateQuery | Query | { [key: string]: any }
): query is { esql: string } {
return Boolean(query && 'esql' in query && !('sql' in query));
}

// returns the language of the aggregate Query, sql, esql etc
export function getAggregateQueryMode(query: AggregateQuery): Language {
return Object.keys(query)[0] as Language;
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-es-query/src/es_query/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { decorateQuery } from './decorate_query';
export {
isOfQueryType,
isOfAggregateQueryType,
isOfEsqlQueryType,
getAggregateQueryMode,
getLanguageDisplayName,
} from './es_aggregate_query';
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-esql-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export {
getIndexPatternFromESQLQuery,
getLimitFromESQLQuery,
removeDropCommandsFromESQLQuery,
getIndexForESQLQuery,
} from './src';
2 changes: 1 addition & 1 deletion packages/kbn-esql-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

export { getESQLAdHocDataview } from './utils/get_esql_adhoc_dataview';
export { getESQLAdHocDataview, getIndexForESQLQuery } from './utils/get_esql_adhoc_dataview';
export {
getIndexPatternFromSQLQuery,
getIndexPatternFromESQLQuery,
Expand Down
30 changes: 29 additions & 1 deletion packages/kbn-esql-utils/src/utils/get_esql_adhoc_dataview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function sha256(str: string) {
// This is a helper to create one. The id is constructed from the indexpattern.
// As there are no runtime fields or field formatters or default time fields
// the same adhoc dataview can be constructed/used. This comes with great advantages such
// as solving the problem descibed here https://github.com/elastic/kibana/issues/168131
// as solving the problem described here https://github.com/elastic/kibana/issues/168131
export async function getESQLAdHocDataview(
indexPattern: string,
dataViewsService: DataViewsPublicPluginStart
Expand All @@ -35,3 +35,31 @@ export async function getESQLAdHocDataview(
id: await sha256(`esql-${indexPattern}`),
});
}

/**
* This can be used to get an initial index for a default ES|QL query.
* Could be used during onboarding when data views to get a better index are not yet available.
* Can be used in combination with {@link getESQLAdHocDataview} to create a dataview for the index.
*/
export async function getIndexForESQLQuery(deps: {
dataViews: { getIndices: DataViewsPublicPluginStart['getIndices'] };
}): Promise<string | null> {
const indices = (
await deps.dataViews.getIndices({
showAllIndices: false,
pattern: '*',
isRollupIndex: () => false,
})
)
.filter((index) => !index.name.startsWith('.'))
.map((index) => index.name);

let indexName = indices[0];
if (indices.length > 0) {
if (indices.find((index) => index.startsWith('logs'))) {
indexName = 'logs*';
}
}

return indexName ?? null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { getHasApiKeys$ } from '../lib/get_has_api_keys';
export interface Props {
/** Handler for successfully creating a new data view. */
onDataViewCreated: (dataView: unknown) => void;
/** Handler for when try ES|QL is clicked and user has been navigated to try ES|QL in discover. */
onESQLNavigationComplete?: () => void;
/** if set to true allows creation of an ad-hoc dataview from data view editor */
allowAdHocDataView?: boolean;
/** if the kibana instance is customly branded */
Expand Down Expand Up @@ -116,6 +118,7 @@ const flavors: {
*/
export const AnalyticsNoDataPage: React.FC<AnalyticsNoDataPageProps> = ({
onDataViewCreated,
onESQLNavigationComplete,
allowAdHocDataView,
showPlainSpinner,
...services
Expand All @@ -131,7 +134,13 @@ export const AnalyticsNoDataPage: React.FC<AnalyticsNoDataPageProps> = ({

return (
<KibanaNoDataPage
{...{ noDataConfig, onDataViewCreated, allowAdHocDataView, showPlainSpinner }}
{...{
noDataConfig,
onDataViewCreated,
onESQLNavigationComplete,
allowAdHocDataView,
showPlainSpinner,
}}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.compo
*/
export const AnalyticsNoDataPage = ({
onDataViewCreated,
onESQLNavigationComplete,
allowAdHocDataView,
}: AnalyticsNoDataPageProps) => {
const { customBranding, ...services } = useServices();
Expand All @@ -29,6 +30,7 @@ export const AnalyticsNoDataPage = ({
showPlainSpinner={showPlainSpinner}
allowAdHocDataView={allowAdHocDataView}
onDataViewCreated={onDataViewCreated}
onESQLNavigationComplete={onESQLNavigationComplete}
/>
);
};
2 changes: 2 additions & 0 deletions packages/shared-ux/page/analytics_no_data/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ export interface AnalyticsNoDataPageProps {
onDataViewCreated: (dataView: unknown) => void;
/** if set to true allows creation of an ad-hoc data view from data view editor */
allowAdHocDataView?: boolean;
/** Handler for when try ES|QL is clicked and user has been navigated to try ES|QL in discover. */
onESQLNavigationComplete?: () => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useServices } from './services';
*/
export const KibanaNoDataPage = ({
onDataViewCreated,
onESQLNavigationComplete,
noDataConfig,
allowAdHocDataView,
showPlainSpinner,
Expand Down Expand Up @@ -55,6 +56,7 @@ export const KibanaNoDataPage = ({
return (
<NoDataViewsPrompt
onDataViewCreated={onDataViewCreated}
onESQLNavigationComplete={onESQLNavigationComplete}
allowAdHocDataView={allowAdHocDataView}
/>
);
Expand Down
2 changes: 2 additions & 0 deletions packages/shared-ux/page/kibana_no_data/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ export interface KibanaNoDataPageProps {
allowAdHocDataView?: boolean;
/** Set to true if the kibana is customly branded */
showPlainSpinner: boolean;
/** Handler for when try ES|QL is clicked and user has been navigated to try ES|QL in discover. */
onESQLNavigationComplete?: () => void;
}
1 change: 1 addition & 0 deletions packages/shared-ux/prompt/no_data_views/impl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export { NoDataViewsPrompt } from './src/no_data_views';
export { NoDataViewsPrompt as NoDataViewsPromptComponent } from './src/no_data_views.component';
export { NoDataViewsPromptKibanaProvider, NoDataViewsPromptProvider } from './src/services';
export { DataViewIllustration } from './src/data_view_illustration';
export { useOnTryESQL } from './src/hooks';
75 changes: 75 additions & 0 deletions packages/shared-ux/prompt/no_data_views/impl/src/actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { EuiButton, EuiLink, EuiSpacer, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';

interface NoDataButtonProps {
onClickCreate: (() => void) | undefined;
canCreateNewDataView: boolean;
onTryESQL?: () => void;
esqlDocLink?: string;
}

const createDataViewText = i18n.translate('sharedUXPackages.noDataViewsPrompt.addDataViewText', {
defaultMessage: 'Create data view',
});

export const NoDataButtonLink = ({
onClickCreate,
canCreateNewDataView,
onTryESQL,
esqlDocLink,
}: NoDataButtonProps) => {
if (!onTryESQL && !canCreateNewDataView) {
return null;
}

return (
<>
{canCreateNewDataView && (
<EuiButton
onClick={onClickCreate}
iconType="plusInCircle"
fill={true}
data-test-subj="createDataViewButton"
>
{createDataViewText}
</EuiButton>
)}
{canCreateNewDataView && onTryESQL && <EuiSpacer />}
{onTryESQL && (
<EuiText size="xs" color={'subdued'}>
<FormattedMessage
id="sharedUXPackages.no_data_views.esqlMessage"
defaultMessage="Alternatively, you can query your data directly using ES|QL (technical preview). {docsLink}"
values={{
docsLink: esqlDocLink && (
<EuiLink href={esqlDocLink} target="_blank">
<FormattedMessage
id="sharedUXPackages.no_data_views.esqlDocsLink"
defaultMessage="Learn more."
/>
</EuiLink>
),
}}
/>
<EuiSpacer size={'s'} />
<EuiButton color="success" onClick={onTryESQL} size="s" data-test-subj="tryESQLLink">
<FormattedMessage
id="sharedUXPackages.no_data_views.esqlButtonLabel"
defaultMessage="Try ES|QL"
/>
</EuiButton>
</EuiText>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export { useOnTryESQL, type UseOnTryEsqlParams } from './use_on_try_esql';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { useEffect, useState } from 'react';
import { DISCOVER_ESQL_LOCATOR } from '@kbn/deeplinks-analytics';

import { NavigateToAppFn, LocatorClient } from '@kbn/shared-ux-prompt-no-data-views-types';

export interface UseOnTryEsqlParams {
locatorClient?: LocatorClient;
navigateToApp: NavigateToAppFn;
}

export const useOnTryESQL = ({ locatorClient, navigateToApp }: UseOnTryEsqlParams) => {
const [onTryESQL, setOnTryEsql] = useState<(() => void) | undefined>();

useEffect(() => {
(async () => {
const location = await locatorClient?.get(DISCOVER_ESQL_LOCATOR)?.getLocation({});

if (!location) {
return;
}

const { app, path, state } = location;

setOnTryEsql(() => () => {
navigateToApp(app, { path, state });
});
})();
}, [locatorClient, navigateToApp]);

return onTryESQL;
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,13 @@
import React from 'react';
import { css } from '@emotion/react';

import { EuiButton, EuiEmptyPrompt, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiEmptyPrompt, EuiPanel } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { withSuspense } from '@kbn/shared-ux-utility';
import { NoDataViewsPromptComponentProps } from '@kbn/shared-ux-prompt-no-data-views-types';

import { DocumentationLink } from './documentation_link';

const createDataViewText = i18n.translate('sharedUXPackages.noDataViewsPrompt.addDataViewText', {
defaultMessage: 'Create data view',
});
import { NoDataButtonLink } from './actions';

// Using raw value because it is content dependent
const MAX_WIDTH = 830;
Expand All @@ -31,19 +27,10 @@ export const NoDataViewsPrompt = ({
onClickCreate,
canCreateNewDataView,
dataViewsDocLink,
onTryESQL,
esqlDocLink,
emptyPromptColor = 'plain',
}: NoDataViewsPromptComponentProps) => {
const actions = canCreateNewDataView && (
<EuiButton
onClick={onClickCreate}
iconType="plusInCircle"
fill={true}
data-test-subj="createDataViewButton"
>
{createDataViewText}
</EuiButton>
);

const title = canCreateNewDataView ? (
<h2>
<FormattedMessage
Expand Down Expand Up @@ -94,6 +81,9 @@ export const NoDataViewsPrompt = ({
);

const icon = <Illustration />;
const actions = (
<NoDataButtonLink {...{ onClickCreate, canCreateNewDataView, onTryESQL, esqlDocLink }} />
);

return (
<EuiEmptyPrompt
Expand Down
Loading

0 comments on commit 43f134f

Please sign in to comment.