mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Graph] Switch to SavedObjectClient.resolve (#109617)
This commit is contained in:
parent
3c17704cfc
commit
7fff05f4e7
11 changed files with 325 additions and 57 deletions
|
@ -4,12 +4,29 @@
|
|||
"kibanaVersion": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["licensing", "data", "navigation", "savedObjects", "kibanaLegacy"],
|
||||
"optionalPlugins": ["home", "features"],
|
||||
"configPath": ["xpack", "graph"],
|
||||
"requiredBundles": ["kibanaUtils", "kibanaReact", "home"],
|
||||
"requiredPlugins": [
|
||||
"licensing",
|
||||
"data",
|
||||
"navigation",
|
||||
"savedObjects",
|
||||
"kibanaLegacy"
|
||||
],
|
||||
"optionalPlugins": [
|
||||
"home",
|
||||
"features",
|
||||
"spaces"
|
||||
],
|
||||
"configPath": [
|
||||
"xpack",
|
||||
"graph"
|
||||
],
|
||||
"requiredBundles": [
|
||||
"kibanaUtils",
|
||||
"kibanaReact",
|
||||
"home"
|
||||
],
|
||||
"owner": {
|
||||
"name": "Data Discovery",
|
||||
"githubTeam": "kibana-data-discovery"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import './index.scss';
|
|||
import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public';
|
||||
import { GraphSavePolicy } from './types';
|
||||
import { graphRouter } from './router';
|
||||
import { SpacesApi } from '../../spaces/public';
|
||||
|
||||
/**
|
||||
* These are dependencies of the Graph app besides the base dependencies
|
||||
|
@ -63,6 +64,7 @@ export interface GraphDependencies {
|
|||
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
|
||||
uiSettings: IUiSettingsClient;
|
||||
history: ScopedHistory<unknown>;
|
||||
spaces?: SpacesApi;
|
||||
}
|
||||
|
||||
export type GraphServices = Omit<GraphDependencies, 'kibanaLegacy' | 'element' | 'history'>;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { showSaveModal } from '../../../../../src/plugins/saved_objects/public';
|
||||
import { Workspace } from '../types';
|
||||
|
@ -40,6 +40,7 @@ export const WorkspaceRoute = ({
|
|||
getBasePath,
|
||||
addBasePath,
|
||||
setHeaderActionMenu,
|
||||
spaces,
|
||||
indexPatterns: getIndexPatternProvider,
|
||||
},
|
||||
}: WorkspaceRouteProps) => {
|
||||
|
@ -56,7 +57,6 @@ export const WorkspaceRoute = ({
|
|||
*/
|
||||
const [renderCounter, setRenderCounter] = useState(0);
|
||||
const history = useHistory();
|
||||
const urlQuery = new URLSearchParams(useLocation().search).get('query');
|
||||
|
||||
const indexPatternProvider = useMemo(
|
||||
() => createCachedIndexPatternProvider(getIndexPatternProvider.get),
|
||||
|
@ -114,22 +114,27 @@ export const WorkspaceRoute = ({
|
|||
})
|
||||
);
|
||||
|
||||
const { savedWorkspace, indexPatterns } = useWorkspaceLoader({
|
||||
const loaded = useWorkspaceLoader({
|
||||
workspaceRef,
|
||||
store,
|
||||
savedObjectsClient,
|
||||
toastNotifications,
|
||||
spaces,
|
||||
coreStart,
|
||||
});
|
||||
|
||||
if (!savedWorkspace || !indexPatterns) {
|
||||
if (!loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { savedWorkspace, indexPatterns, sharingSavedObjectProps } = loaded;
|
||||
|
||||
return (
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider services={services}>
|
||||
<Provider store={store}>
|
||||
<WorkspaceLayout
|
||||
spaces={spaces}
|
||||
sharingSavedObjectProps={sharingSavedObjectProps}
|
||||
renderCounter={renderCounter}
|
||||
workspace={workspaceRef.current}
|
||||
loading={loading}
|
||||
|
@ -143,7 +148,6 @@ export const WorkspaceRoute = ({
|
|||
indexPatterns={indexPatterns}
|
||||
savedWorkspace={savedWorkspace}
|
||||
indexPatternProvider={indexPatternProvider}
|
||||
urlQuery={urlQuery}
|
||||
/>
|
||||
</Provider>
|
||||
</KibanaContextProvider>
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { WorkspaceLayoutComponent } from '.';
|
||||
import { coreMock } from 'src/core/public/mocks';
|
||||
import { spacesPluginMock } from '../../../../spaces/public/mocks';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from '../../../../../../src/plugins/navigation/public';
|
||||
import { GraphSavePolicy, GraphWorkspaceSavedObject, IndexPatternProvider } from '../../types';
|
||||
import { OverlayStart, Capabilities } from 'kibana/public';
|
||||
import { SharingSavedObjectProps } from '../../helpers/use_workspace_loader';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const useLocation = () => ({
|
||||
search: '?query={}',
|
||||
});
|
||||
return {
|
||||
useLocation,
|
||||
};
|
||||
});
|
||||
|
||||
describe('workspace_layout', () => {
|
||||
const defaultProps = {
|
||||
renderCounter: 1,
|
||||
loading: false,
|
||||
savedWorkspace: { id: 'test' } as GraphWorkspaceSavedObject,
|
||||
hasFields: true,
|
||||
overlays: {} as OverlayStart,
|
||||
workspaceInitialized: true,
|
||||
indexPatterns: [],
|
||||
indexPatternProvider: {} as IndexPatternProvider,
|
||||
capabilities: {} as Capabilities,
|
||||
coreStart: coreMock.createStart(),
|
||||
graphSavePolicy: 'configAndDataWithConsent' as GraphSavePolicy,
|
||||
navigation: {} as NavigationStart,
|
||||
canEditDrillDownUrls: true,
|
||||
setHeaderActionMenu: jest.fn(),
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'exactMatch',
|
||||
aliasTargetId: '',
|
||||
} as SharingSavedObjectProps,
|
||||
spaces: spacesPluginMock.createStartContract(),
|
||||
};
|
||||
it('should display conflict notification if outcome is conflict', () => {
|
||||
shallow(
|
||||
<WorkspaceLayoutComponent
|
||||
{...defaultProps}
|
||||
sharingSavedObjectProps={{ outcome: 'conflict', aliasTargetId: 'conflictId' }}
|
||||
/>
|
||||
);
|
||||
expect(defaultProps.spaces.ui.components.getLegacyUrlConflict).toHaveBeenCalledWith({
|
||||
currentObjectId: 'test',
|
||||
objectNoun: 'Graph',
|
||||
otherObjectId: 'conflictId',
|
||||
otherObjectPath: '#/workspace/conflictId?query={}',
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,6 +9,7 @@ import React, { Fragment, memo, useCallback, useRef, useState } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { connect } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { SearchBar } from '../search_bar';
|
||||
import {
|
||||
GraphState,
|
||||
|
@ -33,6 +34,8 @@ import { GraphServices } from '../../application';
|
|||
import { ControlPanel } from '../control_panel';
|
||||
import { GraphVisualization } from '../graph_visualization';
|
||||
import { colorChoices } from '../../helpers/style_choices';
|
||||
import { SharingSavedObjectProps } from '../../helpers/use_workspace_loader';
|
||||
import { getEditUrl } from '../../services/url';
|
||||
|
||||
/**
|
||||
* Each component, which depends on `worksapce`
|
||||
|
@ -51,6 +54,7 @@ type WorkspaceLayoutProps = Pick<
|
|||
| 'coreStart'
|
||||
| 'canEditDrillDownUrls'
|
||||
| 'overlays'
|
||||
| 'spaces'
|
||||
> & {
|
||||
renderCounter: number;
|
||||
workspace?: Workspace;
|
||||
|
@ -58,7 +62,7 @@ type WorkspaceLayoutProps = Pick<
|
|||
indexPatterns: IndexPatternSavedObject[];
|
||||
savedWorkspace: GraphWorkspaceSavedObject;
|
||||
indexPatternProvider: IndexPatternProvider;
|
||||
urlQuery: string | null;
|
||||
sharingSavedObjectProps?: SharingSavedObjectProps;
|
||||
};
|
||||
|
||||
interface WorkspaceLayoutStateProps {
|
||||
|
@ -66,7 +70,7 @@ interface WorkspaceLayoutStateProps {
|
|||
hasFields: boolean;
|
||||
}
|
||||
|
||||
const WorkspaceLayoutComponent = ({
|
||||
export const WorkspaceLayoutComponent = ({
|
||||
renderCounter,
|
||||
workspace,
|
||||
loading,
|
||||
|
@ -81,8 +85,9 @@ const WorkspaceLayoutComponent = ({
|
|||
graphSavePolicy,
|
||||
navigation,
|
||||
canEditDrillDownUrls,
|
||||
urlQuery,
|
||||
setHeaderActionMenu,
|
||||
sharingSavedObjectProps,
|
||||
spaces,
|
||||
}: WorkspaceLayoutProps & WorkspaceLayoutStateProps) => {
|
||||
const [currentIndexPattern, setCurrentIndexPattern] = useState<IndexPattern>();
|
||||
const [showInspect, setShowInspect] = useState(false);
|
||||
|
@ -90,6 +95,10 @@ const WorkspaceLayoutComponent = ({
|
|||
const [mergeCandidates, setMergeCandidates] = useState<TermIntersect[]>([]);
|
||||
const [control, setControl] = useState<ControlType>('none');
|
||||
const selectedNode = useRef<WorkspaceNode | undefined>(undefined);
|
||||
|
||||
const search = useLocation().search;
|
||||
const urlQuery = new URLSearchParams(search).get('query');
|
||||
|
||||
const isInitialized = Boolean(workspaceInitialized || savedWorkspace.id);
|
||||
|
||||
const selectSelected = useCallback((node: WorkspaceNode) => {
|
||||
|
@ -154,6 +163,27 @@ const WorkspaceLayoutComponent = ({
|
|||
[]
|
||||
);
|
||||
|
||||
const getLegacyUrlConflictCallout = useCallback(() => {
|
||||
// This function returns a callout component *if* we have encountered a "legacy URL conflict" scenario
|
||||
const currentObjectId = savedWorkspace.id;
|
||||
if (spaces && sharingSavedObjectProps?.outcome === 'conflict' && currentObjectId) {
|
||||
// We have resolved to one object, but another object has a legacy URL alias associated with this ID/page. We should display a
|
||||
// callout with a warning for the user, and provide a way for them to navigate to the other object.
|
||||
const otherObjectId = sharingSavedObjectProps?.aliasTargetId!; // This is always defined if outcome === 'conflict'
|
||||
const otherObjectPath =
|
||||
getEditUrl(coreStart.http.basePath.prepend, { id: otherObjectId }) + search;
|
||||
return spaces.ui.components.getLegacyUrlConflict({
|
||||
objectNoun: i18n.translate('xpack.graph.legacyUrlConflict.objectNoun', {
|
||||
defaultMessage: 'Graph',
|
||||
}),
|
||||
currentObjectId,
|
||||
otherObjectId,
|
||||
otherObjectPath,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}, [savedWorkspace.id, sharingSavedObjectProps, spaces, coreStart.http, search]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<WorkspaceTopNavMenu
|
||||
|
@ -176,7 +206,6 @@ const WorkspaceLayoutComponent = ({
|
|||
lastResponse={workspace?.lastResponse}
|
||||
indexPattern={currentIndexPattern}
|
||||
/>
|
||||
|
||||
{isInitialized && <GraphTitle />}
|
||||
<div className="gphGraph__bar">
|
||||
<SearchBar
|
||||
|
@ -190,6 +219,7 @@ const WorkspaceLayoutComponent = ({
|
|||
<EuiSpacer size="s" />
|
||||
<FieldManagerMemoized pickerOpen={pickerOpen} setPickerOpen={setPickerOpen} />
|
||||
</div>
|
||||
{getLegacyUrlConflictCallout()}
|
||||
{!isInitialized && (
|
||||
<div>
|
||||
<GuidancePanelMemoized
|
||||
|
|
|
@ -82,28 +82,38 @@ export function findSavedWorkspace(
|
|||
});
|
||||
}
|
||||
|
||||
export function getEmptyWorkspace() {
|
||||
return {
|
||||
savedObject: {
|
||||
displayName: 'graph workspace',
|
||||
getEsType: () => savedWorkspaceType,
|
||||
...defaultsProps,
|
||||
} as GraphWorkspaceSavedObject,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSavedWorkspace(
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
id?: string
|
||||
id: string
|
||||
) {
|
||||
const savedObject = {
|
||||
id,
|
||||
displayName: 'graph workspace',
|
||||
getEsType: () => savedWorkspaceType,
|
||||
} as { [key: string]: any };
|
||||
const resolveResult = await savedObjectsClient.resolve<Record<string, unknown>>(
|
||||
savedWorkspaceType,
|
||||
id
|
||||
);
|
||||
|
||||
if (!id) {
|
||||
assign(savedObject, defaultsProps);
|
||||
return Promise.resolve(savedObject);
|
||||
}
|
||||
|
||||
const resp = await savedObjectsClient.get<Record<string, unknown>>(savedWorkspaceType, id);
|
||||
savedObject._source = cloneDeep(resp.attributes);
|
||||
const resp = resolveResult.saved_object;
|
||||
|
||||
if (!resp._version) {
|
||||
throw new SavedObjectNotFound(savedWorkspaceType, id || '');
|
||||
}
|
||||
|
||||
const savedObject = {
|
||||
id,
|
||||
displayName: 'graph workspace',
|
||||
getEsType: () => savedWorkspaceType,
|
||||
_source: cloneDeep(resp.attributes),
|
||||
} as GraphWorkspaceSavedObject;
|
||||
|
||||
// assign the defaults to the response
|
||||
defaults(savedObject._source, defaultsProps);
|
||||
|
||||
|
@ -120,7 +130,15 @@ export async function getSavedWorkspace(
|
|||
injectReferences(savedObject, resp.references);
|
||||
}
|
||||
|
||||
return savedObject as GraphWorkspaceSavedObject;
|
||||
const sharingSavedObjectProps = {
|
||||
outcome: resolveResult.outcome,
|
||||
aliasTargetId: resolveResult.alias_target_id,
|
||||
};
|
||||
|
||||
return {
|
||||
savedObject,
|
||||
sharingSavedObjectProps,
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteSavedWorkspace(
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { useWorkspaceLoader, UseWorkspaceLoaderProps } from './use_workspace_loader';
|
||||
import { coreMock } from 'src/core/public/mocks';
|
||||
import { spacesPluginMock } from '../../../spaces/public/mocks';
|
||||
import { createMockGraphStore } from '../state_management/mocks';
|
||||
import { Workspace } from '../types';
|
||||
import { SavedObjectsClientCommon } from 'src/plugins/data/common';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const useLocation = () => ({
|
||||
search: '?query={}',
|
||||
});
|
||||
|
||||
const replaceFn = jest.fn();
|
||||
|
||||
const useHistory = () => ({
|
||||
replace: replaceFn,
|
||||
});
|
||||
return {
|
||||
useHistory,
|
||||
useLocation,
|
||||
useParams: () => ({
|
||||
id: '1',
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const mockSavedObjectsClient = ({
|
||||
resolve: jest.fn().mockResolvedValue({
|
||||
saved_object: { id: 10, _version: '7.15.0', attributes: { wsState: '{}' } },
|
||||
outcome: 'exactMatch',
|
||||
}),
|
||||
find: jest.fn().mockResolvedValue({ title: 'test' }),
|
||||
} as unknown) as SavedObjectsClientCommon;
|
||||
|
||||
async function setup(props: UseWorkspaceLoaderProps) {
|
||||
const returnVal = {};
|
||||
function TestComponent() {
|
||||
Object.assign(returnVal, useWorkspaceLoader(props));
|
||||
return null;
|
||||
}
|
||||
await act(async () => {
|
||||
const promise = Promise.resolve();
|
||||
mount(<TestComponent />);
|
||||
await act(() => promise);
|
||||
});
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
describe('use_workspace_loader', () => {
|
||||
const defaultProps = {
|
||||
workspaceRef: { current: {} as Workspace },
|
||||
store: createMockGraphStore({}).store,
|
||||
savedObjectsClient: mockSavedObjectsClient,
|
||||
coreStart: coreMock.createStart(),
|
||||
spaces: spacesPluginMock.createStartContract(),
|
||||
};
|
||||
|
||||
it('should not redirect if outcome is exactMatch', async () => {
|
||||
await act(async () => {
|
||||
await setup((defaultProps as unknown) as UseWorkspaceLoaderProps);
|
||||
});
|
||||
expect(defaultProps.spaces.ui.redirectLegacyUrl).not.toHaveBeenCalled();
|
||||
expect(defaultProps.store.dispatch).toHaveBeenCalled();
|
||||
});
|
||||
it('should redirect if outcome is aliasMatch', async () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
spaces: spacesPluginMock.createStartContract(),
|
||||
savedObjectsClient: {
|
||||
...mockSavedObjectsClient,
|
||||
resolve: jest.fn().mockResolvedValue({
|
||||
saved_object: { id: 10, _version: '7.15.0', attributes: { wsState: '{}' } },
|
||||
outcome: 'aliasMatch',
|
||||
alias_target_id: 'aliasTargetId',
|
||||
}),
|
||||
},
|
||||
};
|
||||
await act(async () => {
|
||||
await setup((props as unknown) as UseWorkspaceLoaderProps);
|
||||
});
|
||||
expect(props.spaces.ui.redirectLegacyUrl).toHaveBeenCalledWith(
|
||||
'#/workspace/aliasTargetId?query={}',
|
||||
'Graph'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -5,35 +5,48 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientContract, ToastsStart } from 'kibana/public';
|
||||
import { SavedObjectsClientContract } from 'kibana/public';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useHistory, useLocation, useParams } from 'react-router-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { GraphStore } from '../state_management';
|
||||
import { GraphWorkspaceSavedObject, IndexPatternSavedObject, Workspace } from '../types';
|
||||
import { getSavedWorkspace } from './saved_workspace_utils';
|
||||
|
||||
interface UseWorkspaceLoaderProps {
|
||||
import { getEmptyWorkspace, getSavedWorkspace } from './saved_workspace_utils';
|
||||
import { getEditUrl } from '../services/url';
|
||||
import { SpacesApi } from '../../../spaces/public';
|
||||
export interface UseWorkspaceLoaderProps {
|
||||
store: GraphStore;
|
||||
workspaceRef: React.MutableRefObject<Workspace | undefined>;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
toastNotifications: ToastsStart;
|
||||
coreStart: CoreStart;
|
||||
spaces?: SpacesApi;
|
||||
}
|
||||
|
||||
interface WorkspaceUrlParams {
|
||||
id?: string;
|
||||
}
|
||||
export interface SharingSavedObjectProps {
|
||||
outcome?: 'aliasMatch' | 'exactMatch' | 'conflict';
|
||||
aliasTargetId?: string;
|
||||
}
|
||||
|
||||
interface WorkspaceLoadedState {
|
||||
savedWorkspace: GraphWorkspaceSavedObject;
|
||||
indexPatterns: IndexPatternSavedObject[];
|
||||
sharingSavedObjectProps?: SharingSavedObjectProps;
|
||||
}
|
||||
|
||||
export const useWorkspaceLoader = ({
|
||||
coreStart,
|
||||
spaces,
|
||||
workspaceRef,
|
||||
store,
|
||||
savedObjectsClient,
|
||||
toastNotifications,
|
||||
}: UseWorkspaceLoaderProps) => {
|
||||
const [indexPatterns, setIndexPatterns] = useState<IndexPatternSavedObject[]>();
|
||||
const [savedWorkspace, setSavedWorkspace] = useState<GraphWorkspaceSavedObject>();
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const [state, setState] = useState<WorkspaceLoadedState>();
|
||||
const { replace: historyReplace } = useHistory();
|
||||
const { search } = useLocation();
|
||||
const { id } = useParams<WorkspaceUrlParams>();
|
||||
|
||||
/**
|
||||
|
@ -41,7 +54,7 @@ export const useWorkspaceLoader = ({
|
|||
* on changes in id parameter and URL query only.
|
||||
*/
|
||||
useEffect(() => {
|
||||
const urlQuery = new URLSearchParams(location.search).get('query');
|
||||
const urlQuery = new URLSearchParams(search).get('query');
|
||||
|
||||
function loadWorkspace(
|
||||
fetchedSavedWorkspace: GraphWorkspaceSavedObject,
|
||||
|
@ -71,24 +84,43 @@ export const useWorkspaceLoader = ({
|
|||
.then((response) => response.savedObjects);
|
||||
}
|
||||
|
||||
async function fetchSavedWorkspace() {
|
||||
return (id
|
||||
async function fetchSavedWorkspace(): Promise<{
|
||||
savedObject: GraphWorkspaceSavedObject;
|
||||
sharingSavedObjectProps?: SharingSavedObjectProps;
|
||||
}> {
|
||||
return id
|
||||
? await getSavedWorkspace(savedObjectsClient, id).catch(function (e) {
|
||||
toastNotifications.addError(e, {
|
||||
coreStart.notifications.toasts.addError(e, {
|
||||
title: i18n.translate('xpack.graph.missingWorkspaceErrorMessage', {
|
||||
defaultMessage: "Couldn't load graph with ID",
|
||||
}),
|
||||
});
|
||||
history.replace('/home');
|
||||
historyReplace('/home');
|
||||
// return promise that never returns to prevent the controller from loading
|
||||
return new Promise(() => {});
|
||||
})
|
||||
: await getSavedWorkspace(savedObjectsClient)) as GraphWorkspaceSavedObject;
|
||||
: getEmptyWorkspace();
|
||||
}
|
||||
|
||||
async function initializeWorkspace() {
|
||||
const fetchedIndexPatterns = await fetchIndexPatterns();
|
||||
const fetchedSavedWorkspace = await fetchSavedWorkspace();
|
||||
const {
|
||||
savedObject: fetchedSavedWorkspace,
|
||||
sharingSavedObjectProps: fetchedSharingSavedObjectProps,
|
||||
} = await fetchSavedWorkspace();
|
||||
|
||||
if (spaces && fetchedSharingSavedObjectProps?.outcome === 'aliasMatch') {
|
||||
// We found this object by a legacy URL alias from its old ID; redirect the user to the page with its new ID, preserving any URL hash
|
||||
const newObjectId = fetchedSharingSavedObjectProps?.aliasTargetId!; // This is always defined if outcome === 'aliasMatch'
|
||||
const newPath = getEditUrl(coreStart.http.basePath.prepend, { id: newObjectId }) + search;
|
||||
spaces.ui.redirectLegacyUrl(
|
||||
newPath,
|
||||
i18n.translate('xpack.graph.legacyUrlConflict.objectNoun', {
|
||||
defaultMessage: 'Graph',
|
||||
})
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deal with situation of request to open saved workspace. Otherwise clean up store,
|
||||
|
@ -99,22 +131,25 @@ export const useWorkspaceLoader = ({
|
|||
} else if (workspaceRef.current) {
|
||||
clearStore();
|
||||
}
|
||||
|
||||
setIndexPatterns(fetchedIndexPatterns);
|
||||
setSavedWorkspace(fetchedSavedWorkspace);
|
||||
setState({
|
||||
savedWorkspace: fetchedSavedWorkspace,
|
||||
indexPatterns: fetchedIndexPatterns,
|
||||
sharingSavedObjectProps: fetchedSharingSavedObjectProps,
|
||||
});
|
||||
}
|
||||
|
||||
initializeWorkspace();
|
||||
}, [
|
||||
id,
|
||||
location,
|
||||
search,
|
||||
store,
|
||||
history,
|
||||
historyReplace,
|
||||
savedObjectsClient,
|
||||
setSavedWorkspace,
|
||||
toastNotifications,
|
||||
setState,
|
||||
coreStart,
|
||||
workspaceRef,
|
||||
spaces,
|
||||
]);
|
||||
|
||||
return { savedWorkspace, indexPatterns };
|
||||
return state;
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SpacesApi } from '../../spaces/public';
|
||||
import {
|
||||
AppNavLinkStatus,
|
||||
AppUpdater,
|
||||
|
@ -44,6 +45,7 @@ export interface GraphPluginStartDependencies {
|
|||
savedObjects: SavedObjectsStart;
|
||||
kibanaLegacy: KibanaLegacyStart;
|
||||
home?: HomePublicPluginStart;
|
||||
spaces?: SpacesApi;
|
||||
}
|
||||
|
||||
export class GraphPlugin
|
||||
|
@ -110,6 +112,7 @@ export class GraphPlugin
|
|||
overlays: coreStart.overlays,
|
||||
savedObjects: pluginsStart.savedObjects,
|
||||
uiSettings: core.uiSettings,
|
||||
spaces: pluginsStart.spaces,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -18,13 +18,13 @@ export function getNewPath() {
|
|||
return '/workspace';
|
||||
}
|
||||
|
||||
export function getEditPath({ id }: GraphWorkspaceSavedObject) {
|
||||
export function getEditPath({ id }: Pick<GraphWorkspaceSavedObject, 'id'>) {
|
||||
return `/workspace/${id}`;
|
||||
}
|
||||
|
||||
export function getEditUrl(
|
||||
addBasePath: (url: string) => string,
|
||||
workspace: GraphWorkspaceSavedObject
|
||||
workspace: Pick<GraphWorkspaceSavedObject, 'id'>
|
||||
) {
|
||||
return addBasePath(`#${getEditPath(workspace)}`);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
{ "path": "../../../src/plugins/kibana_legacy/tsconfig.json"},
|
||||
{ "path": "../../../src/plugins/home/tsconfig.json"},
|
||||
{ "path": "../../../src/plugins/kibana_utils/tsconfig.json" },
|
||||
{ "path": "../../../src/plugins/kibana_react/tsconfig.json" }
|
||||
{ "path": "../../../src/plugins/kibana_react/tsconfig.json" },
|
||||
{ "path": "../spaces/tsconfig.json" }
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue