mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [🌊 Streams: Selectors for derived samples (#213638)](https://github.com/elastic/kibana/pull/213638) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Joe Reuter","email":"johannes.reuter@elastic.co"},"sourceCommit":{"committedDate":"2025-04-01T09:47:31Z","message":"🌊 Streams: Selectors for derived samples (#213638)\n\nSimplified massively from first state and just plugging in reselect in\nplaces where that's suitable (here to calculate the currently relevant\nsample documents).\n\nAlso does a drive-by layout fix.\n\n~Introduces a new xstate helper for derived data.~\n\n~In most cases, the actor and state machine model of xstate is great,\nbut for derived data using pure functions, the semantics of the\n`useMemo` hook with defined dependencies is often easier to understand\nand eliminates the risk of forgetting to update the derived data\ncorrectly in some cases.~\n\n~It's about using the right tool for the right job - you don't need to\nchoose between the dependency list of useMemo and the actor model of\nxstate, you can use what fits the case, without compromising\nperformance.~\n\n~This is the API:~\n```ts\nconst myActorContext = withMemoizedSelectors(\n createActorContext(myMachine),\n {\n derivedView: createSelector(\n [\n (ctx: MyContextType) => {\n return ctx.dependency1;\n },\n (ctx: MyContextType) =>\n ctx.dependency2,\n ],\n (dependency1, dependency2) => {\n return // expensive calculation only running when necessary\n }\n ),\n },\n (context) => (context.subMachine ? [context.subMachine] : []) // optional subscribe to changes of submachines as well\n);\n\n\n// in react use useMemoizedSelector hook\n// this will cause the component to rerender if the selector is returning a new value\nmyActorContext.useMemoizedSelector('derivedView')\n```\n\n~This is using reselect to declare the dependencies similar to a react\nuseMemo hook - the actual selector will only run if the dependencies\nchange, leading to similar semantics as useMemo, with the additional\nbenefit that if the value is used in multiple places, it's still just\ncalculated once. The component calling `withMemoizedSelectors` only\nre-renders if the value returned by the selector changes. The selector\nitself only re-runs if one of the declared dependencies changes.~\n\n~Everything is type-safe by capturing the types of the reselect selector\nobject via inferred type param and using it in the `useMemoizedSelector`\ntype.~","sha":"c5e0b05454b124949de754556ec8ba5289445ab3","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:obs-ux-logs","backport:version","Feature:Streams","v9.1.0","v8.19.0"],"title":"🌊 Streams: Selectors for derived samples","number":213638,"url":"https://github.com/elastic/kibana/pull/213638","mergeCommit":{"message":"🌊 Streams: Selectors for derived samples (#213638)\n\nSimplified massively from first state and just plugging in reselect in\nplaces where that's suitable (here to calculate the currently relevant\nsample documents).\n\nAlso does a drive-by layout fix.\n\n~Introduces a new xstate helper for derived data.~\n\n~In most cases, the actor and state machine model of xstate is great,\nbut for derived data using pure functions, the semantics of the\n`useMemo` hook with defined dependencies is often easier to understand\nand eliminates the risk of forgetting to update the derived data\ncorrectly in some cases.~\n\n~It's about using the right tool for the right job - you don't need to\nchoose between the dependency list of useMemo and the actor model of\nxstate, you can use what fits the case, without compromising\nperformance.~\n\n~This is the API:~\n```ts\nconst myActorContext = withMemoizedSelectors(\n createActorContext(myMachine),\n {\n derivedView: createSelector(\n [\n (ctx: MyContextType) => {\n return ctx.dependency1;\n },\n (ctx: MyContextType) =>\n ctx.dependency2,\n ],\n (dependency1, dependency2) => {\n return // expensive calculation only running when necessary\n }\n ),\n },\n (context) => (context.subMachine ? [context.subMachine] : []) // optional subscribe to changes of submachines as well\n);\n\n\n// in react use useMemoizedSelector hook\n// this will cause the component to rerender if the selector is returning a new value\nmyActorContext.useMemoizedSelector('derivedView')\n```\n\n~This is using reselect to declare the dependencies similar to a react\nuseMemo hook - the actual selector will only run if the dependencies\nchange, leading to similar semantics as useMemo, with the additional\nbenefit that if the value is used in multiple places, it's still just\ncalculated once. The component calling `withMemoizedSelectors` only\nre-renders if the value returned by the selector changes. The selector\nitself only re-runs if one of the declared dependencies changes.~\n\n~Everything is type-safe by capturing the types of the reselect selector\nobject via inferred type param and using it in the `useMemoizedSelector`\ntype.~","sha":"c5e0b05454b124949de754556ec8ba5289445ab3"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/213638","number":213638,"mergeCommit":{"message":"🌊 Streams: Selectors for derived samples (#213638)\n\nSimplified massively from first state and just plugging in reselect in\nplaces where that's suitable (here to calculate the currently relevant\nsample documents).\n\nAlso does a drive-by layout fix.\n\n~Introduces a new xstate helper for derived data.~\n\n~In most cases, the actor and state machine model of xstate is great,\nbut for derived data using pure functions, the semantics of the\n`useMemo` hook with defined dependencies is often easier to understand\nand eliminates the risk of forgetting to update the derived data\ncorrectly in some cases.~\n\n~It's about using the right tool for the right job - you don't need to\nchoose between the dependency list of useMemo and the actor model of\nxstate, you can use what fits the case, without compromising\nperformance.~\n\n~This is the API:~\n```ts\nconst myActorContext = withMemoizedSelectors(\n createActorContext(myMachine),\n {\n derivedView: createSelector(\n [\n (ctx: MyContextType) => {\n return ctx.dependency1;\n },\n (ctx: MyContextType) =>\n ctx.dependency2,\n ],\n (dependency1, dependency2) => {\n return // expensive calculation only running when necessary\n }\n ),\n },\n (context) => (context.subMachine ? [context.subMachine] : []) // optional subscribe to changes of submachines as well\n);\n\n\n// in react use useMemoizedSelector hook\n// this will cause the component to rerender if the selector is returning a new value\nmyActorContext.useMemoizedSelector('derivedView')\n```\n\n~This is using reselect to declare the dependencies similar to a react\nuseMemo hook - the actual selector will only run if the dependencies\nchange, leading to similar semantics as useMemo, with the additional\nbenefit that if the value is used in multiple places, it's still just\ncalculated once. The component calling `withMemoizedSelectors` only\nre-renders if the value returned by the selector changes. The selector\nitself only re-runs if one of the declared dependencies changes.~\n\n~Everything is type-safe by capturing the types of the reselect selector\nobject via inferred type param and using it in the `useMemoizedSelector`\ntype.~","sha":"c5e0b05454b124949de754556ec8ba5289445ab3"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Joe Reuter <johannes.reuter@elastic.co>
This commit is contained in:
parent
189a1eb14a
commit
442330ad94
7 changed files with 55 additions and 28 deletions
|
@ -30,6 +30,7 @@ import {
|
|||
getTableColumns,
|
||||
previewDocsFilterOptions,
|
||||
} from './state_management/simulation_state_machine';
|
||||
import { selectPreviewDocuments } from './state_management/simulation_state_machine/selectors';
|
||||
|
||||
export const ProcessorOutcomePreview = () => {
|
||||
const isLoading = useSimulatorSelector(
|
||||
|
@ -140,7 +141,9 @@ const OutcomePreviewTable = () => {
|
|||
const processors = useSimulatorSelector((state) => state.context.processors);
|
||||
const detectedFields = useSimulatorSelector((state) => state.context.simulation?.detected_fields);
|
||||
const previewDocsFilter = useSimulatorSelector((state) => state.context.previewDocsFilter);
|
||||
const previewDocuments = useSimulatorSelector((state) => state.context.previewDocuments);
|
||||
const previewDocuments = useSimulatorSelector((snapshot) =>
|
||||
selectPreviewDocuments(snapshot.context)
|
||||
);
|
||||
|
||||
const previewColumns = useMemo(
|
||||
() => getTableColumns(processors, detectedFields ?? [], previewDocsFilter),
|
||||
|
|
|
@ -29,10 +29,12 @@ import type { FindActionResult } from '@kbn/actions-plugin/server';
|
|||
import { UseGenAIConnectorsResult } from '@kbn/observability-ai-assistant-plugin/public/hooks/use_genai_connectors';
|
||||
import { useAbortController, useBoolean } from '@kbn/react-hooks';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { css } from '@emotion/css';
|
||||
import { useStreamDetail } from '../../../../../hooks/use_stream_detail';
|
||||
import { useKibana } from '../../../../../hooks/use_kibana';
|
||||
import { GrokFormState, ProcessorFormState } from '../../types';
|
||||
import { useSimulatorSelector } from '../../state_management/stream_enrichment_state_machine';
|
||||
import { selectPreviewDocuments } from '../../state_management/simulation_state_machine/selectors';
|
||||
|
||||
const RefreshButton = ({
|
||||
generatePatterns,
|
||||
|
@ -353,7 +355,15 @@ function InnerGrokAiSuggestions({
|
|||
return (
|
||||
<>
|
||||
{content != null && (
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
gutterSize="m"
|
||||
// make sure the content is always filling the full width so the
|
||||
// refresh button is rendered below in all cases
|
||||
className={css`
|
||||
width: 100%;
|
||||
`}
|
||||
>
|
||||
{content}
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
|
@ -379,7 +389,9 @@ export function GrokAiSuggestions() {
|
|||
} = useKibana();
|
||||
const { enabled: isAiEnabled, couldBeEnabled } = useAiEnabled();
|
||||
const { definition } = useStreamDetail();
|
||||
const previewDocuments = useSimulatorSelector((state) => state.context.previewDocuments);
|
||||
const previewDocuments = useSimulatorSelector((snapshot) =>
|
||||
selectPreviewDocuments(snapshot.context)
|
||||
);
|
||||
|
||||
if (!isAiEnabled && couldBeEnabled) {
|
||||
return (
|
||||
|
|
|
@ -44,7 +44,7 @@ import {
|
|||
useStreamEnrichmentEvents,
|
||||
useStreamsEnrichmentSelector,
|
||||
useSimulatorSelector,
|
||||
StreamEnrichmentContext,
|
||||
StreamEnrichmentContextType,
|
||||
} from '../state_management/stream_enrichment_state_machine';
|
||||
import { ProcessorMetrics } from '../state_management/simulation_state_machine';
|
||||
import { DateProcessorForm } from './date';
|
||||
|
@ -194,7 +194,7 @@ const createDraftProcessorFromForm = (
|
|||
};
|
||||
|
||||
export interface EditProcessorPanelProps {
|
||||
processorRef: StreamEnrichmentContext['processorsRefs'][number];
|
||||
processorRef: StreamEnrichmentContextType['processorsRefs'][number];
|
||||
processorMetrics?: ProcessorMetrics;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 { createSelector } from 'reselect';
|
||||
import { filterSimulationDocuments } from './utils';
|
||||
import { SimulationActorSnapshot } from './simulation_state_machine';
|
||||
|
||||
const EMPTY_ARRAY: [] = [];
|
||||
|
||||
export const selectPreviewDocuments = createSelector(
|
||||
[
|
||||
(snapshot: SimulationActorSnapshot['context']) => snapshot.samples,
|
||||
(snapshot: SimulationActorSnapshot['context']) => snapshot.previewDocsFilter,
|
||||
(snapshot: SimulationActorSnapshot['context']) => snapshot.simulation?.documents,
|
||||
],
|
||||
(samples, previewDocsFilter, documents) => {
|
||||
return (
|
||||
(previewDocsFilter && documents
|
||||
? filterSimulationDocuments(documents, previewDocsFilter)
|
||||
: samples) || EMPTY_ARRAY
|
||||
);
|
||||
}
|
||||
);
|
|
@ -30,7 +30,7 @@ import {
|
|||
createSimulationRunnerActor,
|
||||
createSimulationRunFailureNofitier,
|
||||
} from './simulation_runner_actor';
|
||||
import { filterSimulationDocuments, composeSamplingCondition } from './utils';
|
||||
import { composeSamplingCondition } from './utils';
|
||||
|
||||
export type SimulationActorRef = ActorRefFrom<typeof simulationMachine>;
|
||||
export type SimulationActorSnapshot = SnapshotFrom<typeof simulationMachine>;
|
||||
|
@ -72,13 +72,6 @@ export const simulationMachine = setup({
|
|||
storeSimulation: assign((_, params: { simulation: Simulation | undefined }) => ({
|
||||
simulation: params.simulation,
|
||||
})),
|
||||
derivePreviewDocuments: assign(({ context }) => {
|
||||
return {
|
||||
previewDocuments: context.simulation
|
||||
? filterSimulationDocuments(context.simulation.documents, context.previewDocsFilter)
|
||||
: context.samples,
|
||||
};
|
||||
}),
|
||||
deriveSamplingCondition: assign(({ context }) => ({
|
||||
samplingCondition: composeSamplingCondition(context.processors),
|
||||
})),
|
||||
|
@ -125,14 +118,11 @@ export const simulationMachine = setup({
|
|||
on: {
|
||||
'dateRange.update': '.loadingSamples',
|
||||
'simulation.changePreviewDocsFilter': {
|
||||
actions: [
|
||||
{ type: 'storePreviewDocsFilter', params: ({ event }) => event },
|
||||
{ type: 'derivePreviewDocuments' },
|
||||
],
|
||||
actions: [{ type: 'storePreviewDocsFilter', params: ({ event }) => event }],
|
||||
},
|
||||
'simulation.reset': {
|
||||
target: '.idle',
|
||||
actions: [{ type: 'resetSimulation' }, { type: 'derivePreviewDocuments' }],
|
||||
actions: [{ type: 'resetSimulation' }],
|
||||
},
|
||||
// Handle adding/reordering processors
|
||||
'processors.*': {
|
||||
|
@ -158,7 +148,7 @@ export const simulationMachine = setup({
|
|||
},
|
||||
{
|
||||
target: '.idle',
|
||||
actions: [{ type: 'resetSimulation' }, { type: 'derivePreviewDocuments' }],
|
||||
actions: [{ type: 'resetSimulation' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -210,10 +200,7 @@ export const simulationMachine = setup({
|
|||
}),
|
||||
onDone: {
|
||||
target: 'assertingSimulationRequirements',
|
||||
actions: [
|
||||
{ type: 'storeSamples', params: ({ event }) => ({ samples: event.output }) },
|
||||
{ type: 'derivePreviewDocuments' },
|
||||
],
|
||||
actions: [{ type: 'storeSamples', params: ({ event }) => ({ samples: event.output }) }],
|
||||
},
|
||||
onError: {
|
||||
target: 'idle',
|
||||
|
@ -251,7 +238,6 @@ export const simulationMachine = setup({
|
|||
target: 'idle',
|
||||
actions: [
|
||||
{ type: 'storeSimulation', params: ({ event }) => ({ simulation: event.output }) },
|
||||
{ type: 'derivePreviewDocuments' },
|
||||
],
|
||||
},
|
||||
onError: {
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
} from '@kbn/streams-schema';
|
||||
import { htmlIdGenerator } from '@elastic/eui';
|
||||
import {
|
||||
StreamEnrichmentContext,
|
||||
StreamEnrichmentContextType,
|
||||
StreamEnrichmentEvent,
|
||||
StreamEnrichmentInput,
|
||||
StreamEnrichmentServiceDependencies,
|
||||
|
@ -49,7 +49,7 @@ export type StreamEnrichmentActorRef = ActorRefFrom<typeof streamEnrichmentMachi
|
|||
export const streamEnrichmentMachine = setup({
|
||||
types: {
|
||||
input: {} as StreamEnrichmentInput,
|
||||
context: {} as StreamEnrichmentContext,
|
||||
context: {} as StreamEnrichmentContextType,
|
||||
events: {} as StreamEnrichmentEvent,
|
||||
},
|
||||
actors: {
|
||||
|
@ -346,7 +346,7 @@ export const createStreamEnrichmentMachineImplementations = ({
|
|||
},
|
||||
});
|
||||
|
||||
function getStagedProcessors(context: StreamEnrichmentContext) {
|
||||
function getStagedProcessors(context: StreamEnrichmentContextType) {
|
||||
return context.processorsRefs
|
||||
.map((proc) => proc.getSnapshot())
|
||||
.filter((proc) => proc.context.isNew)
|
||||
|
|
|
@ -24,7 +24,7 @@ export interface StreamEnrichmentInput {
|
|||
definition: IngestStreamGetResponse;
|
||||
}
|
||||
|
||||
export interface StreamEnrichmentContext {
|
||||
export interface StreamEnrichmentContextType {
|
||||
definition: IngestStreamGetResponse;
|
||||
initialProcessorsRefs: ProcessorActorRef[];
|
||||
processorsRefs: ProcessorActorRef[];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue