[8.18] [Lens][Embeddable] Restore show missing dataView error message in case of missing datasource (#208363) (#209556)

# Backport

This will backport the following commits from `main` to `8.18`:
- [[Lens][Embeddable] Restore show missing dataView error message in
case of missing datasource
(#208363)](https://github.com/elastic/kibana/pull/208363)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Marco
Liberati","email":"dej611@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-02-04T14:34:13Z","message":"[Lens][Embeddable]
Restore show missing dataView error message in case of missing
datasource (#208363)\n\n## Summary\r\n\r\nFixes #207428 \r\n\r\nThis PR
restores the `Could not find data view xxxx` message when a\r\ndataView
referenced by the visualization is missing.\r\n<img width=\"764\"
alt=\"Screenshot 2025-01-27 at 14 18
19\"\r\nsrc=\"https://github.com/user-attachments/assets/14ed86fc-f6db-4056-8517-2a14fe491541\"\r\n/>\r\n\r\n###
Checklist\r\n\r\nCheck the PR satisfies following conditions. \r\n\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"f3326446986c6a1cd4f472505d0c2968ef3605cd","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Visualizations","Feature:Lens","backport:prev-minor","Feature:Embeddables","v8.18.0","v9.1.0"],"title":"[Lens][Embeddable]
Restore show missing dataView error message in case of missing
datasource","number":208363,"url":"https://github.com/elastic/kibana/pull/208363","mergeCommit":{"message":"[Lens][Embeddable]
Restore show missing dataView error message in case of missing
datasource (#208363)\n\n## Summary\r\n\r\nFixes #207428 \r\n\r\nThis PR
restores the `Could not find data view xxxx` message when a\r\ndataView
referenced by the visualization is missing.\r\n<img width=\"764\"
alt=\"Screenshot 2025-01-27 at 14 18
19\"\r\nsrc=\"https://github.com/user-attachments/assets/14ed86fc-f6db-4056-8517-2a14fe491541\"\r\n/>\r\n\r\n###
Checklist\r\n\r\nCheck the PR satisfies following conditions. \r\n\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"f3326446986c6a1cd4f472505d0c2968ef3605cd"}},"sourceBranch":"main","suggestedTargetBranches":["8.18"],"targetPullRequestStates":[{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/208363","number":208363,"mergeCommit":{"message":"[Lens][Embeddable]
Restore show missing dataView error message in case of missing
datasource (#208363)\n\n## Summary\r\n\r\nFixes #207428 \r\n\r\nThis PR
restores the `Could not find data view xxxx` message when a\r\ndataView
referenced by the visualization is missing.\r\n<img width=\"764\"
alt=\"Screenshot 2025-01-27 at 14 18
19\"\r\nsrc=\"https://github.com/user-attachments/assets/14ed86fc-f6db-4056-8517-2a14fe491541\"\r\n/>\r\n\r\n###
Checklist\r\n\r\nCheck the PR satisfies following conditions. \r\n\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"f3326446986c6a1cd4f472505d0c2968ef3605cd"}}]}]
BACKPORT-->

Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2025-02-05 03:49:33 +11:00 committed by GitHub
parent 97cd9adc4a
commit 425b5bee28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 90 additions and 20 deletions

View file

@ -141,7 +141,7 @@ function getMissingIndexPatternsErrors(
// Check for access to both Management app && specific indexPattern section
const { management: isManagementEnabled } = core.application.capabilities.navLinks;
const isIndexPatternManagementEnabled =
core.application.capabilities.management.kibana.indexPatterns;
core.application.capabilities.management?.kibana?.indexPatterns;
const canFix = isManagementEnabled && isIndexPatternManagementEnabled;
return [
{

View file

@ -34,7 +34,7 @@ import {
} from '@kbn/presentation-publishing';
import { PublishesSearchSession } from '@kbn/presentation-publishing/interfaces/fetch/publishes_search_session';
import { isObject } from 'lodash';
import { defaultDoc } from '../mocks';
import { createMockDatasource, defaultDoc } from '../mocks';
jest.mock('@kbn/interpreter', () => ({
toExpression: jest.fn().mockReturnValue('expression'),
@ -87,13 +87,21 @@ type ChangeFnType = ({
async function expectRerenderOnDataLoder(
changeFn: ChangeFnType,
runtimeState: LensRuntimeState = { attributes: getLensAttributesMock() },
parentApiOverrides?: Partial<
{
filters$: BehaviorSubject<Filter[] | undefined>;
query$: BehaviorSubject<Query | AggregateQuery | undefined>;
timeRange$: BehaviorSubject<TimeRange | undefined>;
} & LensOverrides
>
{
parentApiOverrides,
servicesOverrides,
internalApiOverrides,
}: {
parentApiOverrides?: Partial<
{
filters$: BehaviorSubject<Filter[] | undefined>;
query$: BehaviorSubject<Query | AggregateQuery | undefined>;
timeRange$: BehaviorSubject<TimeRange | undefined>;
} & LensOverrides
>;
internalApiOverrides?: Partial<LensInternalApi>;
servicesOverrides?: Partial<LensEmbeddableStartServices>;
} = {}
): Promise<void> {
const parentApi = {
...createUnifiedSearchApi(),
@ -116,12 +124,15 @@ async function expectRerenderOnDataLoder(
parentApi,
};
const getState = jest.fn(() => runtimeState);
const internalApi = getLensInternalApiMock();
const services = makeEmbeddableServices(new BehaviorSubject<string>(''), undefined, {
visOverrides: { id: 'lnsXY' },
dataOverrides: { id: 'form_based' },
});
services.documentToExpression = jest.fn().mockResolvedValue({ ast: 'expression_string' });
const internalApi = getLensInternalApiMock(internalApiOverrides);
const services = {
...makeEmbeddableServices(new BehaviorSubject<string>(''), undefined, {
visOverrides: { id: 'lnsXY' },
dataOverrides: { id: 'form_based' },
}),
documentToExpression: jest.fn().mockResolvedValue({ ast: 'expression_string' }),
...servicesOverrides,
};
const { cleanup } = loadEmbeddableData(
faker.string.uuid(),
getState,
@ -242,7 +253,7 @@ describe('Data Loader', () => {
return false;
},
undefined, // use default attributes
createUnifiedSearchApi(query, filters) // customize parentApi
{ parentApiOverrides: createUnifiedSearchApi(query, filters) } // customize parentApi
);
});
@ -305,7 +316,13 @@ describe('Data Loader', () => {
return false;
},
{ attributes },
createUnifiedSearchApi(parentApiQuery, parentApiFilters, parentApiTimeRange)
{
parentApiOverrides: createUnifiedSearchApi(
parentApiQuery,
parentApiFilters,
parentApiTimeRange
),
}
);
});
@ -411,4 +428,54 @@ describe('Data Loader', () => {
}
);
});
it('should catch missing dataView errors correctly', async () => {
await expectRerenderOnDataLoder(
async ({ internalApi }) => {
// wait for the error to appear
await waitForValue(internalApi.blockingError$);
const error = internalApi.blockingError$.getValue()!;
expect(error.message).toEqual(
'Could not find the data view: 90943e30-9a47-11e8-b64d-95841ca0b247'
);
return false;
},
undefined,
// Unfortuantely some mocks are required here to make the test work
{
// Mock the testing datasource to return an error when asked for checkIntegrity
servicesOverrides: {
datasourceMap: {
form_based: {
...createMockDatasource('form_based'),
checkIntegrity: jest.fn().mockReturnValue(['90943e30-9a47-11e8-b64d-95841ca0b247']),
},
},
},
// Mock the visualization context to fully load the datasource state
internalApiOverrides: {
getVisualizationContext: jest.fn().mockReturnValue({
activeAttributes: {
...defaultDoc,
visualizationType: 'lnsXY',
state: { ...defaultDoc.state, datasourceStates: { form_based: {} } },
},
mergedSearchContext: {
now: Date.now(),
timeRange: { from: 'now-15m', to: 'now' },
query: [defaultDoc.state.query],
filters: [],
disableWarningToasts: true,
},
indexPatterns: [],
indexPatternRefs: {},
activeVisualizationState: { activeId: 'lnsXY' },
activeDatasourceState: {},
activeData: {},
}),
},
}
);
});
});

View file

@ -202,7 +202,7 @@ export function loadEmbeddableData(
);
// Go concurrently: build the expression and fetch the dataViews
const [{ params, abortController, ...rest }, dataViews] = await Promise.all([
const [{ params, abortController, ...rest }, dataViewIds] = await Promise.all([
getExpressionRendererParams(currentState, {
searchContext,
api,
@ -241,9 +241,12 @@ export function loadEmbeddableData(
});
// Publish the used dataViews on the Lens API
internalApi.updateDataViews(dataViews);
internalApi.updateDataViews(dataViewIds);
if (params?.expression != null && !dispatchBlockingErrorIfAny()) {
// This will catch also failed loaded dataViews
const hasBlockingErrors = dispatchBlockingErrorIfAny();
if (params?.expression != null && !hasBlockingErrors) {
internalApi.updateExpressionParams(params);
}