diff --git a/x-pack/plugins/notes_test/kibana.json b/x-pack/plugins/notes_test/kibana.json index 9799a48b35ef3..15ac81f703784 100644 --- a/x-pack/plugins/notes_test/kibana.json +++ b/x-pack/plugins/notes_test/kibana.json @@ -6,5 +6,5 @@ "server": true, "ui": true, "requiredPlugins": [], - "optionalPlugins": [] + "optionalPlugins": ["spaces"] } diff --git a/x-pack/plugins/notes_test/public/app.tsx b/x-pack/plugins/notes_test/public/app.tsx index 0f80dacfecf25..f01ab7e0c12f9 100644 --- a/x-pack/plugins/notes_test/public/app.tsx +++ b/x-pack/plugins/notes_test/public/app.tsx @@ -9,7 +9,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Router, Switch, Route } from 'react-router-dom'; -import type { AppMountParameters } from 'src/core/public'; +import type { AppMountParameters, HttpStart } from 'src/core/public'; +import type { SpacesPluginStart } from '../../spaces/public'; import { VIEW_NOTE_PATH } from '../common'; import { NotesList } from './notes_list'; import type { Services } from './services'; @@ -18,16 +19,18 @@ import { ViewNote } from './view_note'; interface RenderParams { services: Services; appMountParams: AppMountParameters; + http: HttpStart; + spacesApi?: SpacesPluginStart; } -export const renderApp = ({ services, appMountParams }: RenderParams) => { +export const renderApp = ({ services, appMountParams, http, spacesApi }: RenderParams) => { const { element, history } = appMountParams; ReactDOM.render( - + diff --git a/x-pack/plugins/notes_test/public/plugin.tsx b/x-pack/plugins/notes_test/public/plugin.tsx index 0223121d6c50d..392edfee6c004 100644 --- a/x-pack/plugins/notes_test/public/plugin.tsx +++ b/x-pack/plugins/notes_test/public/plugin.tsx @@ -6,18 +6,25 @@ */ import type { CoreStart, Plugin, CoreSetup, AppMountParameters } from 'src/core/public'; +import type { SpacesPluginStart } from '../../spaces/public'; import { getServices } from './services'; -export class NotesTestPlugin implements Plugin<{}, {}, {}, {}> { - public setup(core: CoreSetup) { +interface PluginStartDeps { + spaces?: SpacesPluginStart; +} + +export class NotesTestPlugin implements Plugin<{}, {}, {}, PluginStartDeps> { + public setup(core: CoreSetup) { core.application.register({ id: 'notesTest', title: 'Notes test', async mount(appMountParams: AppMountParameters) { - const [coreStart] = await core.getStartServices(); + const [coreStart, pluginStartDeps] = await core.getStartServices(); const services = getServices(coreStart); + const { http } = coreStart; + const { spaces: spacesApi } = pluginStartDeps; const { renderApp } = await import('./app'); - return renderApp({ services, appMountParams }); + return renderApp({ services, appMountParams, http, spacesApi }); }, }); diff --git a/x-pack/plugins/notes_test/public/view_note.tsx b/x-pack/plugins/notes_test/public/view_note.tsx index 96f10716d9117..a1d86c88463bf 100644 --- a/x-pack/plugins/notes_test/public/view_note.tsx +++ b/x-pack/plugins/notes_test/public/view_note.tsx @@ -17,8 +17,10 @@ import { } from '@elastic/eui'; import { Link, useParams } from 'react-router-dom'; -import type { ResolvedSimpleSavedObject } from 'src/core/public'; +import type { HttpStart, ResolvedSimpleSavedObject } from 'src/core/public'; +import type { SpacesPluginStart } from '../../spaces/public'; import type { NoteAttributes } from '../common'; +import { VIEW_NOTE_PATH } from '../common'; import type { Services } from './services'; interface Params { @@ -26,11 +28,14 @@ interface Params { } interface Props { services: Services; + http: HttpStart; + spacesApi?: SpacesPluginStart; } type ResolvedNote = ResolvedSimpleSavedObject; +const OBJECT_NOUN = 'note'; -export function ViewNote({ services }: Props) { +export function ViewNote({ services, http, spacesApi }: Props) { const { noteId } = useParams(); const [resolvedNote, setResolvedNote] = useState(null); const note = resolvedNote?.saved_object; @@ -38,6 +43,14 @@ export function ViewNote({ services }: Props) { const fetchNote = async () => { const resolveResult = await services.getNoteById(noteId); + if (spacesApi && resolveResult.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 = resolveResult.alias_target_id!; // This is always defined if outcome === 'aliasMatch' + const newPath = `/${VIEW_NOTE_PATH}/${newObjectId}${window.location.hash}`; // Use the *local* path within this app (do not include the "/app/appId" prefix) + await spacesApi.ui.redirectLegacyUrl(newPath, OBJECT_NOUN); + return; + } + setResolvedNote(resolveResult); }; @@ -46,10 +59,37 @@ export function ViewNote({ services }: Props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [noteId]); + const getLegacyUrlConflictCallout = () => { + // This function returns a callout component *if* we have encountered a "legacy URL conflict" scenario + if (spacesApi && resolvedNote) { + if (resolvedNote.outcome === 'conflict') { + // 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 currentObjectId = resolvedNote.saved_object.id; + const otherObjectId = resolvedNote.alias_target_id!; // This is always defined if outcome === 'conflict' + const otherObjectPath = `/${VIEW_NOTE_PATH}/${otherObjectId}${window.location.hash}`; // Use the *local* path within this app (do not include the "/app/appId" prefix) + return ( + <> + {spacesApi.ui.components.getLegacyUrlConflict({ + objectNoun: OBJECT_NOUN, + currentObjectId, + otherObjectId, + otherObjectPath, + })} + + + ); + } + } + return null; + }; + return ( + {/* If we have a legacy URL conflict callout to display, show it at the top of the page */} + {getLegacyUrlConflictCallout()}

View note

diff --git a/x-pack/plugins/notes_test/tsconfig.json b/x-pack/plugins/notes_test/tsconfig.json index 2ebf144dc584a..0edd5023c4e3a 100644 --- a/x-pack/plugins/notes_test/tsconfig.json +++ b/x-pack/plugins/notes_test/tsconfig.json @@ -10,5 +10,6 @@ "include": ["common/**/*", "public/**/*", "server/**/*"], "references": [ { "path": "../../../src/core/tsconfig.json" }, + { "path": "../spaces/tsconfig.json" }, ] }