[Logs UI] Make URL syncing optional in the Log View state machine (#154061)

## Summary

This fixes https://github.com/elastic/kibana/issues/154030 (and other
uses of the Log Stream embeddable component).

The embeddable component calls `useLogView()` directly, and this causes
issues with context dependencies for URL syncing from the consumers.

This PR makes the URL actions / services optional within the machine. 

Uses:

- `<LogViewProvider />` used for all main Logs pages (stream, anomalies,
categories) has full URL syncing 
- `<LogViewProvider />` used within our Logs alert editor does not have
URL syncing (not needed) 
- `useLogView()` as used by the embeddable component does not have URL
syncing 
- `useLogView()` as used by `RedirectToNodeLogs` does not have URL
syncing (not needed, URL syncing kicks in after redirect) 

The default / pure implementation of `initializeFromUrl` just does a
`send({ type: 'INITIALIZED_FROM_URL', logViewReference: null })` as the
state machine needs to transition to it's `initialized` state and is
already set up to use the initial context reference if there's no
reference obtained from the URL.

Examples:

![Screenshot 2023-03-30 at 15 23
48](https://user-images.githubusercontent.com/471693/228867868-b526c4b2-bec8-47cb-8e7c-c3da2dd6c803.png)

![Screenshot 2023-03-30 at 15 24
39](https://user-images.githubusercontent.com/471693/228867889-c7451f84-415c-45f9-ae96-e6908d60409c.png)
This commit is contained in:
Kerry Gallagher 2023-03-31 08:47:44 -04:00 committed by GitHub
parent a77ece24f5
commit 684203944c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 27 deletions

View file

@ -10,6 +10,11 @@ import createContainer from 'constate';
import { useCallback, useState } from 'react';
import { waitFor } from 'xstate/lib/waitFor';
import { LogViewAttributes, LogViewReference } from '../../common/log_views';
import {
InitializeFromUrl,
UpdateContextInUrl,
ListenForUrlChanges,
} from '../observability_logs/log_view_state/src/url_state_storage_service';
import {
createLogViewNotificationChannel,
createLogViewStateMachine,
@ -17,26 +22,22 @@ import {
} from '../observability_logs/log_view_state';
import type { ILogViewsClient } from '../services/log_views';
import { isDevMode } from '../utils/dev_mode';
import { useKbnUrlStateStorageFromRouterContext } from '../utils/kbn_url_state_context';
import { useKibanaContextForPlugin } from './use_kibana';
export const useLogView = ({
initialLogViewReference,
logViews,
useDevTools = isDevMode(),
initializeFromUrl,
updateContextInUrl,
listenForUrlChanges,
}: {
initialLogViewReference?: LogViewReference;
logViews: ILogViewsClient;
useDevTools?: boolean;
initializeFromUrl?: InitializeFromUrl;
updateContextInUrl?: UpdateContextInUrl;
listenForUrlChanges?: ListenForUrlChanges;
}) => {
const {
services: {
notifications: { toasts: toastsService },
},
} = useKibanaContextForPlugin();
const urlStateStorage = useKbnUrlStateStorageFromRouterContext();
const [logViewStateNotifications] = useState(() => createLogViewNotificationChannel());
const logViewStateService = useInterpret(
@ -47,8 +48,9 @@ export const useLogView = ({
},
logViews,
notificationChannel: logViewStateNotifications,
toastsService,
urlStateStorage,
initializeFromUrl,
updateContextInUrl,
listenForUrlChanges,
}),
{
devTools: useDevTools,

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { IToasts } from '@kbn/core/public';
import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public';
import { catchError, from, map, of, throwError } from 'rxjs';
import { createMachine, actions, assign } from 'xstate';
import { ILogViewsClient } from '../../../services/log_views';
@ -23,9 +21,9 @@ import {
LogViewTypestate,
} from './types';
import {
initializeFromUrl,
updateContextInUrl,
listenForUrlChanges,
InitializeFromUrl,
UpdateContextInUrl,
ListenForUrlChanges,
} from './url_state_storage_service';
export const createPureLogViewStateMachine = (initialContext: LogViewContextWithReference) =>
@ -223,6 +221,7 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith
notifyLoadingStarted: actions.pure(() => undefined),
notifyLoadingSucceeded: actions.pure(() => undefined),
notifyLoadingFailed: actions.pure(() => undefined),
updateContextInUrl: actions.pure(() => undefined),
storeLogViewReference: assign((context, event) =>
'logViewReference' in event && event.logViewReference !== null
? ({
@ -282,6 +281,11 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith
: {}
),
},
services: {
initializeFromUrl: (_context, _event) => (send) =>
send({ type: 'INITIALIZED_FROM_URL', logViewReference: null }),
listenForUrlChanges: (_context, _event) => (send) => {},
},
guards: {
isPersistedLogView: (context, event) =>
context.logViewReference.type === 'log-view-reference',
@ -293,16 +297,18 @@ export interface LogViewStateMachineDependencies {
initialContext: LogViewContextWithReference;
logViews: ILogViewsClient;
notificationChannel?: NotificationChannel<LogViewContext, LogViewEvent, LogViewNotificationEvent>;
toastsService: IToasts;
urlStateStorage: IKbnUrlStateStorage;
initializeFromUrl?: InitializeFromUrl;
updateContextInUrl?: UpdateContextInUrl;
listenForUrlChanges?: ListenForUrlChanges;
}
export const createLogViewStateMachine = ({
initialContext,
logViews,
notificationChannel,
toastsService,
urlStateStorage,
initializeFromUrl,
updateContextInUrl,
listenForUrlChanges,
}: LogViewStateMachineDependencies) =>
createPureLogViewStateMachine(initialContext).withConfig({
actions: {
@ -319,11 +325,11 @@ export const createLogViewStateMachine = ({
),
}
: {}),
updateContextInUrl: updateContextInUrl({ toastsService, urlStateStorage }),
...(updateContextInUrl ? { updateContextInUrl } : {}),
},
services: {
initializeFromUrl: initializeFromUrl({ toastsService, urlStateStorage }),
listenForUrlChanges: listenForUrlChanges({ urlStateStorage }),
...(initializeFromUrl ? { initializeFromUrl } : {}),
...(listenForUrlChanges ? { listenForUrlChanges } : {}),
loadLogView: (context) =>
from(
'logViewReference' in context

View file

@ -133,3 +133,7 @@ const convertSourceIdToReference = (sourceId: string): PersistedLogViewReference
// NOTE: Used by link-to components
export const replaceLogViewInQueryString = (logViewReference: LogViewReference) =>
replaceStateKeyInQueryString(defaultLogViewKey, logViewReference);
export type InitializeFromUrl = ReturnType<typeof initializeFromUrl>;
export type UpdateContextInUrl = ReturnType<typeof updateContextInUrl>;
export type ListenForUrlChanges = ReturnType<typeof listenForUrlChanges>;

View file

@ -5,16 +5,44 @@
* 2.0.
*/
import React from 'react';
import React, { useState } from 'react';
import { LogAnalysisCapabilitiesProvider } from '../../containers/logs/log_analysis';
import { useKibanaContextForPlugin } from '../../hooks/use_kibana';
import { LogViewProvider } from '../../hooks/use_log_view';
import {
initializeFromUrl as createInitializeFromUrl,
updateContextInUrl as createUpdateContextInUrl,
listenForUrlChanges as createListenForUrlChanges,
} from '../../observability_logs/log_view_state/src/url_state_storage_service';
import { useKbnUrlStateStorageFromRouterContext } from '../../utils/kbn_url_state_context';
export const LogsPageProviders: React.FunctionComponent = ({ children }) => {
const { services } = useKibanaContextForPlugin();
const {
services: {
notifications: { toasts: toastsService },
logViews: { client },
},
} = useKibanaContextForPlugin();
const urlStateStorage = useKbnUrlStateStorageFromRouterContext();
const [initializeFromUrl] = useState(() => {
return createInitializeFromUrl({ toastsService, urlStateStorage });
});
const [updateContextInUrl] = useState(() => {
return createUpdateContextInUrl({ toastsService, urlStateStorage });
});
const [listenForUrlChanges] = useState(() => {
return createListenForUrlChanges({ urlStateStorage });
});
return (
<LogViewProvider logViews={services.logViews.client}>
<LogViewProvider
logViews={client}
initializeFromUrl={initializeFromUrl}
updateContextInUrl={updateContextInUrl}
listenForUrlChanges={listenForUrlChanges}
>
<LogAnalysisCapabilitiesProvider>{children}</LogAnalysisCapabilitiesProvider>
</LogViewProvider>
);