Skip to content

Commit

Permalink
Fix profile data view fields loading, and clean up resolve_data_view
Browse files Browse the repository at this point in the history
  • Loading branch information
davismcphee committed Jan 21, 2025
1 parent bd630c3 commit 58862da
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,13 @@ export const buildStateSubscribe =
? nextState.dataSource.dataViewId
: undefined;

const { dataView: nextDataView, fallback } = await loadAndResolveDataView(
{ id: dataViewId, savedSearch, isEsqlMode },
{ internalStateContainer: internalState, services }
);
const { dataView: nextDataView, fallback } = await loadAndResolveDataView({
dataViewId,
savedSearch,
isEsqlMode,
internalStateContainer: internalState,
services,
});

// If the requested data view is not found, don't try to load it,
// and instead reset the app state to the fallback data view
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export async function changeDataView(
try {
nextDataView = typeof id === 'string' ? await dataViews.get(id, false) : id;
} catch (e) {
//
// Swallow the error and keep the current data view
}

if (nextDataView && dataView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,14 @@ const getStateDataView = async (
return await getEsqlDataView(query, dataView, services);
}

const result = await loadAndResolveDataView(
{
id: dataViewId,
dataViewSpec,
savedSearch,
isEsqlMode: isEsqlQuery,
},
{ services, internalStateContainer }
);
const result = await loadAndResolveDataView({
dataViewId,
dataViewSpec,
savedSearch,
isEsqlMode: isEsqlQuery,
services,
internalStateContainer,
});

return result.dataView;
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@ import { discoverServiceMock as services } from '../../../../__mocks__/services'

describe('Resolve data view tests', () => {
test('returns valid data for an existing data view', async () => {
const id = 'the-data-view-id';
const dataViewId = 'the-data-view-id';
const result = await loadDataView({
id,
dataViewId,
services,
dataViewList: [],
savedDataViews: [],
adHocDataViews: [],
});
expect(result.loaded).toEqual(dataViewMock);
expect(result.stateVal).toEqual(id);
expect(result.stateValFound).toEqual(true);
expect(result.loadedDataView).toEqual(dataViewMock);
expect(result.requestedDataViewId).toEqual(dataViewId);
expect(result.requestedDataViewFound).toEqual(true);
});
test('returns fallback data for an invalid data view', async () => {
const id = 'invalid-id';
const dataViewId = 'invalid-id';
const result = await loadDataView({
id,
dataViewId,
services,
dataViewList: [],
savedDataViews: [],
adHocDataViews: [],
});
expect(result.loaded).toEqual(dataViewMock);
expect(result.stateValFound).toBe(false);
expect(result.stateVal).toBe(id);
expect(result.loadedDataView).toEqual(dataViewMock);
expect(result.requestedDataViewFound).toBe(false);
expect(result.requestedDataViewId).toBe(dataViewId);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,80 +15,64 @@ import { DiscoverInternalStateContainer } from '../discover_internal_state_conta
import { DiscoverServices } from '../../../../build_services';

interface DataViewData {
/**
* List of existing data views
*/
list: DataViewListItem[];
/**
* Loaded data view (might be default data view if requested was not found)
*/
loaded: DataView;
loadedDataView: DataView;
/**
* Id of the requested data view
*/
stateVal?: string;
requestedDataViewId?: string;
/**
* Determines if requested data view was found
*/
stateValFound: boolean;
requestedDataViewFound: boolean;
}

/**
* Function to load the given data view by id, providing a fallback if it doesn't exist
*/
export async function loadDataView({
id,
dataViewId,
dataViewSpec,
services,
dataViewList,
services: { dataViews },
savedDataViews,
adHocDataViews,
}: {
id?: string;
dataViewId?: string;
dataViewSpec?: DataViewSpec;
services: DiscoverServices;
dataViewList: DataViewListItem[];
savedDataViews: DataViewListItem[];
adHocDataViews: DataView[];
}): Promise<DataViewData> {
const { dataViews } = services;

let fetchId: string | undefined = id;
let fetchId: string | undefined = dataViewId;

/**
* Handle redirect with data view spec provided via history location state
*/
// Handle redirect with data view spec provided via history location state
if (dataViewSpec) {
const isPersisted = dataViewList.find(({ id: currentId }) => currentId === dataViewSpec.id);
if (!isPersisted) {
const isPersisted = savedDataViews.find(({ id: currentId }) => currentId === dataViewSpec.id);
if (isPersisted) {
// If passed a spec for a persisted data view, reassign the fetchId
fetchId = dataViewSpec.id!;
} else {
// If passed an ad hoc data view spec, create and return the data view
const createdAdHocDataView = await dataViews.create(dataViewSpec);
return {
list: dataViewList || [],
loaded: createdAdHocDataView,
stateVal: createdAdHocDataView.id,
stateValFound: true,
loadedDataView: createdAdHocDataView,
requestedDataViewId: createdAdHocDataView.id,
requestedDataViewFound: true,
};
}
// reassign fetchId in case of persisted data view spec provided
fetchId = dataViewSpec.id!;
}

// First try to fetch the data view by ID
let fetchedDataView: DataView | null = null;
// try to fetch adhoc data view first
try {
fetchedDataView = fetchId ? await dataViews.get(fetchId) : null;
if (fetchedDataView && !fetchedDataView.isPersisted()) {
return {
list: dataViewList || [],
loaded: fetchedDataView,
stateVal: id,
stateValFound: true,
};
}
// Skipping error handling, since 'get' call trying to fetch
// adhoc data view which only created using Promise.resolve(dataView),
// Any other error will be handled by the next 'get' call below.
// eslint-disable-next-line no-empty
} catch (e) {}
} catch (e) {
// Swallow the error and fall back to the default data view
}

// If there is no fetched data view, try to fetch the default data view
let defaultDataView: DataView | null = null;
if (!fetchedDataView) {
try {
Expand All @@ -97,25 +81,29 @@ export async function loadDataView({
refreshFields: true,
});
} catch (e) {
//
// Swallow the error and fall back to the first ad hoc data view
}
}

// fetch persisted data view
// If nothing else is available, use the first ad hoc data view as a fallback
let defaultAdHocDataView: DataView | null = null;
if (!fetchedDataView && !defaultDataView && adHocDataViews.length) {
defaultAdHocDataView = adHocDataViews[0];
}

return {
list: dataViewList || [],
// we can be certain that the data view exists due to an earlier hasData check
loaded: fetchedDataView || defaultDataView || adHocDataViews[0],
stateVal: fetchId,
stateValFound: Boolean(fetchId) && Boolean(fetchedDataView),
// We can be certain that a data view exists due to an earlier hasData check
loadedDataView: (fetchedDataView || defaultDataView || defaultAdHocDataView)!,
requestedDataViewId: fetchId,
requestedDataViewFound: Boolean(fetchId) && Boolean(fetchedDataView),
};
}

/**
* Check if the given data view is valid, provide a fallback if it doesn't exist
* And message the user in this case with toast notifications
*/
export function resolveDataView({
function resolveDataView({
dataViewData,
savedSearch,
toastNotifications,
Expand All @@ -126,21 +114,20 @@ export function resolveDataView({
toastNotifications: ToastsStart;
isEsqlMode?: boolean;
}) {
const { loaded: loadedDataView, stateVal, stateValFound } = dataViewData;

const { loadedDataView, requestedDataViewId, requestedDataViewFound } = dataViewData;
const ownDataView = savedSearch?.searchSource.getField('index');

if (ownDataView && !stateVal) {
if (ownDataView && !requestedDataViewId) {
// the given saved search has its own data view, and no data view was specified in the URL
return ownDataView;
}

// no warnings for ES|QL mode
if (stateVal && !stateValFound && !Boolean(isEsqlMode)) {
if (requestedDataViewId && !requestedDataViewFound && !Boolean(isEsqlMode)) {
const warningTitle = i18n.translate('discover.valueIsNotConfiguredDataViewIDWarningTitle', {
defaultMessage: '{stateVal} is not a configured data view ID',
values: {
stateVal: `"${stateVal}"`,
stateVal: `"${requestedDataViewId}"`,
},
});

Expand Down Expand Up @@ -178,43 +165,52 @@ export function resolveDataView({
return loadedDataView;
}

export const loadAndResolveDataView = async (
{
id,
dataViewSpec,
savedSearch,
isEsqlMode,
}: {
id?: string;
dataViewSpec?: DataViewSpec;
savedSearch?: SavedSearch;
isEsqlMode?: boolean;
},
{
internalStateContainer,
services,
}: { internalStateContainer: DiscoverInternalStateContainer; services: DiscoverServices }
) => {
export const loadAndResolveDataView = async ({
dataViewId,
dataViewSpec,
savedSearch,
isEsqlMode,
internalStateContainer,
services,
}: {
dataViewId?: string;
dataViewSpec?: DataViewSpec;
savedSearch?: SavedSearch;
isEsqlMode?: boolean;
internalStateContainer: DiscoverInternalStateContainer;
services: DiscoverServices;
}) => {
const { dataViews, toastNotifications } = services;
const { adHocDataViews, savedDataViews } = internalStateContainer.getState();
const adHocDataView = adHocDataViews.find((dataView) => dataView.id === id);

if (adHocDataView) {
return { fallback: false, dataView: adHocDataView };
// Check ad hoc data views first, then attempt to load one if none is found
let fallback = false;
let dataView = adHocDataViews.find((dv) => dv.id === dataViewId);

if (!dataView) {
const dataViewData = await loadDataView({
dataViewId,
services,
dataViewSpec,
savedDataViews,
adHocDataViews,
});

fallback = !dataViewData.requestedDataViewFound;
dataView = resolveDataView({
dataViewData,
savedSearch,
toastNotifications,
isEsqlMode,
});
}

// If dataView is an ad hoc data view with no fields, refresh its field list.
// This can happen when default profile data views are created without fields
// to avoid unnecessary requests on startup.
if (!dataView.isPersisted() && !dataView.fields.length) {
await dataViews.refreshFields(dataView);
}

const nextDataViewData = await loadDataView({
services,
id,
dataViewSpec,
dataViewList: savedDataViews,
adHocDataViews,
});
const nextDataView = resolveDataView({
dataViewData: nextDataViewData,
savedSearch,
toastNotifications: services.toastNotifications,
isEsqlMode,
});

return { fallback: !nextDataViewData.stateValFound, dataView: nextDataView };
return { fallback, dataView };
};

0 comments on commit 58862da

Please sign in to comment.