[Ingest Node Pipelines] Refactor pipeline simulator code (#72328)

This commit is contained in:
Alison Goryachev 2020-08-05 09:21:57 -04:00 committed by GitHub
parent f9fc83fb1d
commit bf22fe54e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 323 additions and 366 deletions

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { PipelineFormProvider as PipelineForm } from './pipeline_form_provider';
export { PipelineForm } from './pipeline_form';

View file

@ -16,7 +16,6 @@ import './pipeline_form.scss';
import { OnUpdateHandlerArg, OnUpdateHandler } from '../pipeline_processors_editor';
import { PipelineRequestFlyout } from './pipeline_request_flyout';
import { PipelineTestFlyout } from './pipeline_test_flyout';
import { PipelineFormFields } from './pipeline_form_fields';
import { PipelineFormError } from './pipeline_form_error';
import { pipelineFormSchema } from './schema';
@ -48,8 +47,6 @@ export const PipelineForm: React.FunctionComponent<PipelineFormProps> = ({
}) => {
const [isRequestVisible, setIsRequestVisible] = useState<boolean>(false);
const [isTestingPipeline, setIsTestingPipeline] = useState<boolean>(false);
const {
processors: initialProcessors,
on_failure: initialOnFailureProcessors,
@ -79,10 +76,6 @@ export const PipelineForm: React.FunctionComponent<PipelineFormProps> = ({
}
};
const handleTestPipelineClick = () => {
setIsTestingPipeline(true);
};
const { form } = useForm<IPipelineForm>({
schema: pipelineFormSchema,
defaultValue: defaultFormValues,
@ -90,7 +83,6 @@ export const PipelineForm: React.FunctionComponent<PipelineFormProps> = ({
});
const onEditorFlyoutOpen = useCallback(() => {
setIsTestingPipeline(false);
setIsRequestVisible(false);
}, [setIsRequestVisible]);
@ -137,8 +129,6 @@ export const PipelineForm: React.FunctionComponent<PipelineFormProps> = ({
onFailure={processorsState.onFailure}
onProcessorsUpdate={onProcessorsChangeHandler}
hasVersion={Boolean(defaultValue.version)}
isTestButtonDisabled={isTestingPipeline || form.isValid === false}
onTestPipelineClick={handleTestPipelineClick}
isEditing={isEditing}
/>
@ -198,18 +188,6 @@ export const PipelineForm: React.FunctionComponent<PipelineFormProps> = ({
closeFlyout={() => setIsRequestVisible((prevIsRequestVisible) => !prevIsRequestVisible)}
/>
) : null}
{/* Test pipeline flyout */}
{isTestingPipeline ? (
<PipelineTestFlyout
readProcessors={() =>
processorStateRef.current?.getData() || { processors: [], on_failure: [] }
}
closeFlyout={() => {
setIsTestingPipeline((prevIsTestingPipeline) => !prevIsTestingPipeline);
}}
/>
) : null}
</Form>
<EuiSpacer size="m" />

View file

@ -13,7 +13,7 @@ import { Processor } from '../../../../common/types';
import { getUseField, getFormRow, Field, useKibana } from '../../../shared_imports';
import {
PipelineProcessorsContextProvider,
ProcessorsEditorContextProvider,
GlobalOnFailureProcessorsEditor,
ProcessorsEditor,
OnUpdateHandler,
@ -29,8 +29,6 @@ interface Props {
onLoadJson: OnDoneLoadJsonHandler;
onProcessorsUpdate: OnUpdateHandler;
hasVersion: boolean;
isTestButtonDisabled: boolean;
onTestPipelineClick: () => void;
onEditorFlyoutOpen: () => void;
isEditing?: boolean;
}
@ -45,8 +43,6 @@ export const PipelineFormFields: React.FunctionComponent<Props> = ({
onProcessorsUpdate,
isEditing,
hasVersion,
isTestButtonDisabled,
onTestPipelineClick,
onEditorFlyoutOpen,
}) => {
const { services } = useKibana();
@ -125,20 +121,18 @@ export const PipelineFormFields: React.FunctionComponent<Props> = ({
{/* Pipeline Processors Editor */}
<PipelineProcessorsContextProvider
<ProcessorsEditorContextProvider
onFlyoutOpen={onEditorFlyoutOpen}
links={{ esDocsBasePath: services.documentation.getEsDocsBasePath() }}
api={services.api}
toasts={services.notifications.toasts}
onUpdate={onProcessorsUpdate}
value={{ processors, onFailure }}
>
<div className="pipelineProcessorsEditor">
<EuiFlexGroup gutterSize="m" responsive={false} direction="column">
<EuiFlexItem grow={false}>
<ProcessorsHeader
onLoadJson={onLoadJson}
onTestPipelineClick={onTestPipelineClick}
isTestButtonDisabled={isTestButtonDisabled}
/>
<ProcessorsHeader onLoadJson={onLoadJson} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ProcessorsEditor />
@ -154,7 +148,7 @@ export const PipelineFormFields: React.FunctionComponent<Props> = ({
</EuiFlexItem>
</EuiFlexGroup>
</div>
</PipelineProcessorsContextProvider>
</ProcessorsEditorContextProvider>
</>
);
};

View file

@ -1,20 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { PipelineForm as PipelineFormUI, PipelineFormProps } from './pipeline_form';
import { TestConfigContextProvider } from './test_config_context';
export const PipelineFormProvider: React.FunctionComponent<PipelineFormProps> = (
passThroughProps
) => {
return (
<TestConfigContextProvider>
<PipelineFormUI {...passThroughProps} />
</TestConfigContextProvider>
);
};

View file

@ -1,203 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState, useEffect, useCallback } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import {
EuiFlyout,
EuiFlyoutBody,
EuiFlyoutHeader,
EuiSpacer,
EuiTitle,
EuiCallOut,
} from '@elastic/eui';
import { useKibana } from '../../../../shared_imports';
import { Pipeline } from '../../../../../common/types';
import { Tabs, Tab, OutputTab, DocumentsTab } from './tabs';
import { useTestConfigContext } from '../test_config_context';
export interface PipelineTestFlyoutProps {
closeFlyout: () => void;
pipeline: Pipeline;
isPipelineValid: boolean;
}
export const PipelineTestFlyout: React.FunctionComponent<PipelineTestFlyoutProps> = ({
closeFlyout,
pipeline,
isPipelineValid,
}) => {
const { services } = useKibana();
const { testConfig } = useTestConfigContext();
const { documents: cachedDocuments, verbose: cachedVerbose } = testConfig;
const initialSelectedTab = cachedDocuments ? 'output' : 'documents';
const [selectedTab, setSelectedTab] = useState<Tab>(initialSelectedTab);
const [shouldExecuteImmediately, setShouldExecuteImmediately] = useState<boolean>(false);
const [isExecuting, setIsExecuting] = useState<boolean>(false);
const [executeError, setExecuteError] = useState<any>(null);
const [executeOutput, setExecuteOutput] = useState<any>(undefined);
const handleExecute = useCallback(
async (documents: object[], verbose?: boolean) => {
const { name: pipelineName, ...pipelineDefinition } = pipeline;
setIsExecuting(true);
setExecuteError(null);
const { error, data: output } = await services.api.simulatePipeline({
documents,
verbose,
pipeline: pipelineDefinition,
});
setIsExecuting(false);
if (error) {
setExecuteError(error);
return;
}
setExecuteOutput(output);
services.notifications.toasts.addSuccess(
i18n.translate('xpack.ingestPipelines.testPipelineFlyout.successNotificationText', {
defaultMessage: 'Pipeline executed',
}),
{
toastLifeTimeMs: 1000,
}
);
setSelectedTab('output');
},
[pipeline, services.api, services.notifications.toasts]
);
useEffect(() => {
if (cachedDocuments) {
setShouldExecuteImmediately(true);
}
// We only want to know on initial mount if there are cached documents
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
// If the user has already tested the pipeline once,
// use the cached test config and automatically execute the pipeline
if (shouldExecuteImmediately && Object.entries(pipeline).length > 0) {
setShouldExecuteImmediately(false);
handleExecute(cachedDocuments!, cachedVerbose);
}
}, [
pipeline,
handleExecute,
cachedDocuments,
cachedVerbose,
isExecuting,
shouldExecuteImmediately,
]);
let tabContent;
if (selectedTab === 'output') {
tabContent = (
<OutputTab
executeOutput={executeOutput}
handleExecute={handleExecute}
isExecuting={isExecuting}
/>
);
} else {
// default to "documents" tab
tabContent = (
<DocumentsTab
isExecuting={isExecuting}
isPipelineValid={isPipelineValid}
handleExecute={handleExecute}
/>
);
}
return (
<EuiFlyout maxWidth={550} onClose={closeFlyout} data-test-subj="testPipelineFlyout">
<EuiFlyoutHeader>
<EuiTitle>
<h2 data-test-subj="title">
{pipeline.name ? (
<FormattedMessage
id="xpack.ingestPipelines.testPipelineFlyout.withPipelineNameTitle"
defaultMessage="Test pipeline '{pipelineName}'"
values={{
pipelineName: pipeline.name,
}}
/>
) : (
<FormattedMessage
id="xpack.ingestPipelines.testPipelineFlyout.title"
defaultMessage="Test pipeline"
/>
)}
</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<Tabs
onTabChange={setSelectedTab}
selectedTab={selectedTab}
getIsDisabled={(tabId) => !executeOutput && tabId === 'output'}
/>
<EuiSpacer />
{/* Execute error */}
{executeError ? (
<>
<EuiCallOut
title={
<FormattedMessage
id="xpack.ingestPipelines.testPipelineFlyout.executePipelineError"
defaultMessage="Unable to execute pipeline"
/>
}
color="danger"
iconType="alert"
>
<p>{executeError.message}</p>
</EuiCallOut>
<EuiSpacer size="m" />
</>
) : null}
{/* Invalid pipeline error */}
{!isPipelineValid ? (
<>
<EuiCallOut
title={
<FormattedMessage
id="xpack.ingestPipelines.testPipelineFlyout.invalidPipelineErrorMessage"
defaultMessage="The pipeline to execute is invalid."
/>
}
color="danger"
iconType="alert"
/>
<EuiSpacer size="m" />
</>
) : null}
{/* Documents or output tab content */}
{tabContent}
</EuiFlyoutBody>
</EuiFlyout>
);
};

View file

@ -1,47 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState, useEffect } from 'react';
import { Pipeline } from '../../../../../common/types';
import { useFormContext } from '../../../../shared_imports';
import { ReadProcessorsFunction } from '../types';
import { PipelineTestFlyout, PipelineTestFlyoutProps } from './pipeline_test_flyout';
interface Props extends Omit<PipelineTestFlyoutProps, 'pipeline' | 'isPipelineValid'> {
readProcessors: ReadProcessorsFunction;
}
export const PipelineTestFlyoutProvider: React.FunctionComponent<Props> = ({
closeFlyout,
readProcessors,
}) => {
const form = useFormContext();
const [formData, setFormData] = useState<Pipeline>({} as Pipeline);
const [isFormDataValid, setIsFormDataValid] = useState<boolean>(false);
useEffect(() => {
const subscription = form.subscribe(async ({ isValid, validate, data }) => {
const isFormValid = isValid ?? (await validate());
if (isFormValid) {
setFormData(data.format() as Pipeline);
}
setIsFormDataValid(isFormValid);
});
return subscription.unsubscribe;
}, [form]);
return (
<PipelineTestFlyout
pipeline={{ ...formData, ...readProcessors() }}
closeFlyout={closeFlyout}
isPipelineValid={isFormDataValid}
/>
);
};

View file

@ -5,25 +5,23 @@
*/
import React, { FunctionComponent } from 'react';
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiTitle } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { usePipelineProcessorsContext } from '../pipeline_processors_editor/context';
import { LoadFromJsonButton, OnDoneLoadJsonHandler } from '../pipeline_processors_editor';
import {
LoadFromJsonButton,
OnDoneLoadJsonHandler,
TestPipelineButton,
} from '../pipeline_processors_editor';
export interface Props {
onTestPipelineClick: () => void;
isTestButtonDisabled: boolean;
onLoadJson: OnDoneLoadJsonHandler;
}
export const ProcessorsHeader: FunctionComponent<Props> = ({
onTestPipelineClick,
isTestButtonDisabled,
onLoadJson,
}) => {
export const ProcessorsHeader: FunctionComponent<Props> = ({ onLoadJson }) => {
const { links } = usePipelineProcessorsContext();
return (
<EuiFlexGroup
@ -63,17 +61,7 @@ export const ProcessorsHeader: FunctionComponent<Props> = ({
<LoadFromJsonButton onDone={onLoadJson} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="testPipelineButton"
size="s"
onClick={onTestPipelineClick}
disabled={isTestButtonDisabled}
>
<FormattedMessage
id="xpack.ingestPipelines.pipelineEditor.testPipelineButtonLabel"
defaultMessage="Test pipeline"
/>
</EuiButton>
<TestPipelineButton />
</EuiFlexItem>
</EuiFlexGroup>
);

View file

@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils';
import React from 'react';
import { registerTestBed, TestBed } from '../../../../../../../test_utils';
import {
PipelineProcessorsContextProvider,
ProcessorsEditorContextProvider,
Props,
ProcessorsEditor,
GlobalOnFailureProcessorsEditor,
@ -62,9 +62,9 @@ jest.mock('react-virtualized', () => {
const testBedSetup = registerTestBed<TestSubject>(
(props: Props) => (
<PipelineProcessorsContextProvider {...props}>
<ProcessorsEditorContextProvider {...props}>
<ProcessorsEditor /> <GlobalOnFailureProcessorsEditor />
</PipelineProcessorsContextProvider>
</ProcessorsEditorContextProvider>
),
{
doMountAsync: false,

View file

@ -3,8 +3,11 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { notificationServiceMock } from 'src/core/public/mocks';
import { setup, SetupResult } from './pipeline_processors_editor.helpers';
import { Pipeline } from '../../../../../common/types';
import { apiService } from '../../../services';
const testProcessors: Pick<Pipeline, 'processors'> = {
processors: [
@ -46,6 +49,8 @@ describe('Pipeline Editor', () => {
links: {
esDocsBasePath: 'test',
},
toasts: notificationServiceMock.createSetupContract().toasts,
api: apiService,
});
});

View file

@ -20,4 +20,6 @@ export { ProcessorRemoveModal } from './processor_remove_modal';
export { OnDoneLoadJsonHandler, LoadFromJsonButton } from './load_from_json';
export { TestPipelineButton } from './test_pipeline';
export { PipelineProcessorsItemTooltip, Position } from './pipeline_processors_editor_item_tooltip';

View file

@ -21,6 +21,7 @@ export const PipelineProcessorsEditor: FunctionComponent<Props> = memo(
state: { editor, processors },
} = usePipelineProcessorsContext();
const baseSelector = useMemo(() => [stateSlice], [stateSlice]);
return (
<ProcessorsTree
baseSelector={baseSelector}

View file

@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import React, { FunctionComponent } from 'react';
import { EuiButton } from '@elastic/eui';
import { FlyoutProvider } from './flyout_provider';
const i18nTexts = {
buttonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.testPipeline.buttonLabel', {
defaultMessage: 'Test pipeline',
}),
};
export const TestPipelineButton: FunctionComponent = () => {
return (
<FlyoutProvider>
{(openFlyout) => {
return (
<EuiButton size="s" onClick={openFlyout} data-test-subj="testPipelineButton">
{i18nTexts.buttonLabel}
</EuiButton>
);
}}
</FlyoutProvider>
);
};

View file

@ -0,0 +1,171 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState, useEffect, useCallback } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import {
EuiFlyout,
EuiFlyoutBody,
EuiFlyoutHeader,
EuiSpacer,
EuiTitle,
EuiCallOut,
} from '@elastic/eui';
import { usePipelineProcessorsContext, useTestConfigContext } from '../../context';
import { serialize } from '../../serialize';
import { Tabs, Tab, OutputTab, DocumentsTab } from './flyout_tabs';
export interface Props {
children: (openFlyout: () => void) => React.ReactNode;
}
export const FlyoutProvider: React.FunctionComponent<Props> = ({ children }) => {
const {
state: { processors },
api,
toasts,
} = usePipelineProcessorsContext();
const serializedProcessors = serialize(processors.state);
const { testConfig } = useTestConfigContext();
const { documents: cachedDocuments, verbose: cachedVerbose } = testConfig;
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
const initialSelectedTab = cachedDocuments ? 'output' : 'documents';
const [selectedTab, setSelectedTab] = useState<Tab>(initialSelectedTab);
const [shouldExecuteImmediately, setShouldExecuteImmediately] = useState<boolean>(false);
const [isExecuting, setIsExecuting] = useState<boolean>(false);
const [executeError, setExecuteError] = useState<any>(null);
const [executeOutput, setExecuteOutput] = useState<any>(undefined);
const handleExecute = useCallback(
async (documents: object[], verbose?: boolean) => {
setIsExecuting(true);
setExecuteError(null);
const { error, data: output } = await api.simulatePipeline({
documents,
verbose,
pipeline: { ...serializedProcessors },
});
setIsExecuting(false);
if (error) {
setExecuteError(error);
return;
}
setExecuteOutput(output);
toasts.addSuccess(
i18n.translate('xpack.ingestPipelines.testPipelineFlyout.successNotificationText', {
defaultMessage: 'Pipeline executed',
}),
{
toastLifeTimeMs: 1000,
}
);
setSelectedTab('output');
},
[serializedProcessors, api, toasts]
);
useEffect(() => {
if (isFlyoutVisible === false && cachedDocuments) {
setShouldExecuteImmediately(true);
}
}, [isFlyoutVisible, cachedDocuments]);
useEffect(() => {
// If the user has already tested the pipeline once,
// use the cached test config and automatically execute the pipeline
if (isFlyoutVisible && shouldExecuteImmediately && cachedDocuments) {
setShouldExecuteImmediately(false);
handleExecute(cachedDocuments!, cachedVerbose);
}
}, [handleExecute, cachedDocuments, cachedVerbose, isFlyoutVisible, shouldExecuteImmediately]);
let tabContent;
if (selectedTab === 'output') {
tabContent = (
<OutputTab
executeOutput={executeOutput}
handleExecute={handleExecute}
isExecuting={isExecuting}
/>
);
} else {
// default to "Documents" tab
tabContent = <DocumentsTab isExecuting={isExecuting} handleExecute={handleExecute} />;
}
return (
<>
{children(() => setIsFlyoutVisible(true))}
{isFlyoutVisible && (
<EuiFlyout
maxWidth={550}
onClose={() => setIsFlyoutVisible(false)}
data-test-subj="testPipelineFlyout"
>
<EuiFlyoutHeader>
<EuiTitle>
<h2 data-test-subj="title">
<FormattedMessage
id="xpack.ingestPipelines.testPipelineFlyout.title"
defaultMessage="Test pipeline"
/>
</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<Tabs
onTabChange={setSelectedTab}
selectedTab={selectedTab}
getIsDisabled={(tabId) => !executeOutput && tabId === 'output'}
/>
<EuiSpacer />
{/* Execute error */}
{executeError ? (
<>
<EuiCallOut
title={
<FormattedMessage
id="xpack.ingestPipelines.testPipelineFlyout.executePipelineError"
defaultMessage="Unable to execute pipeline"
/>
}
color="danger"
iconType="alert"
>
<p>{executeError.message}</p>
</EuiCallOut>
<EuiSpacer size="m" />
</>
) : null}
{/* Documents or output tab content */}
{tabContent}
</EuiFlyoutBody>
</EuiFlyout>
)}
</>
);
};

View file

@ -9,8 +9,8 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { EuiCode } from '@elastic/eui';
import { FormSchema, fieldValidators, ValidationFuncArg } from '../../../../../shared_imports';
import { parseJson, stringifyJson } from '../../../../lib';
import { FormSchema, fieldValidators, ValidationFuncArg } from '../../../../../../shared_imports';
import { parseJson, stringifyJson } from '../../../../../lib';
const { emptyField, isJsonField } = fieldValidators;

View file

@ -17,32 +17,27 @@ import {
Form,
useForm,
FormConfig,
useKibana,
} from '../../../../../shared_imports';
} from '../../../../../../shared_imports';
import { usePipelineProcessorsContext, useTestConfigContext, TestConfig } from '../../../context';
import { documentsSchema } from './schema';
import { useTestConfigContext, TestConfig } from '../../test_config_context';
const UseField = getUseField({ component: Field });
interface Props {
handleExecute: (documents: object[], verbose: boolean) => void;
isPipelineValid: boolean;
isExecuting: boolean;
}
export const DocumentsTab: React.FunctionComponent<Props> = ({
isPipelineValid,
handleExecute,
isExecuting,
}) => {
const { services } = useKibana();
export const DocumentsTab: React.FunctionComponent<Props> = ({ handleExecute, isExecuting }) => {
const { links } = usePipelineProcessorsContext();
const { setCurrentTestConfig, testConfig } = useTestConfigContext();
const { verbose: cachedVerbose, documents: cachedDocuments } = testConfig;
const executePipeline: FormConfig['onSubmit'] = async (formData, isValid) => {
if (!isValid || !isPipelineValid) {
if (!isValid) {
return;
}
@ -76,7 +71,7 @@ export const DocumentsTab: React.FunctionComponent<Props> = ({
values={{
learnMoreLink: (
<EuiLink
href={services.documentation.getSimulatePipelineApiUrl()}
href={`${links.esDocsBasePath}/simulate-pipeline-api.html`}
target="_blank"
external
>
@ -98,7 +93,7 @@ export const DocumentsTab: React.FunctionComponent<Props> = ({
<Form
form={form}
data-test-subj="testPipelineForm"
isInvalid={form.isSubmitted && !form.isValid && !isPipelineValid}
isInvalid={form.isSubmitted && !form.isValid}
error={form.getErrors()}
>
{/* Documents editor */}
@ -125,7 +120,7 @@ export const DocumentsTab: React.FunctionComponent<Props> = ({
onClick={form.submit}
size="s"
isLoading={isExecuting}
disabled={(form.isSubmitted && !form.isValid) || !isPipelineValid}
disabled={form.isSubmitted && !form.isValid}
>
{isExecuting ? (
<FormattedMessage

View file

@ -16,7 +16,8 @@ import {
EuiFlexGroup,
EuiFlexItem,
} from '@elastic/eui';
import { useTestConfigContext } from '../../test_config_context';
import { useTestConfigContext } from '../../../context';
interface Props {
executeOutput?: { docs: object[] };

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { PipelineTestFlyoutProvider as PipelineTestFlyout } from './pipeline_test_flyout_provider';
export { TestPipelineButton } from './button';

View file

@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FunctionComponent } from 'react';
import {
PipelineProcessorsContextProvider,
Props as ProcessorsContextProps,
} from './processors_context';
import { TestConfigContextProvider } from './test_config_context';
interface Props extends ProcessorsContextProps {
children: React.ReactNode;
}
export const ProcessorsEditorContextProvider: FunctionComponent<Props> = ({
children,
links,
api,
toasts,
onUpdate,
value,
onFlyoutOpen,
}: Props) => {
return (
<TestConfigContextProvider>
<PipelineProcessorsContextProvider
onFlyoutOpen={onFlyoutOpen}
links={links}
api={api}
toasts={toasts}
onUpdate={onUpdate}
value={value}
>
{children}
</PipelineProcessorsContextProvider>
</TestConfigContextProvider>
);
};

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { ProcessorsEditorContextProvider } from './context';
export { TestConfigContextProvider, useTestConfigContext, TestConfig } from './test_config_context';
export {
PipelineProcessorsContextProvider,
usePipelineProcessorsContext,
Props,
} from './processors_context';

View file

@ -15,7 +15,10 @@ import React, {
useRef,
} from 'react';
import { Processor } from '../../../../common/types';
import { NotificationsSetup } from 'src/core/public';
import { Processor } from '../../../../../common/types';
import { ApiService } from '../../../services';
import {
EditorMode,
@ -26,29 +29,31 @@ import {
ContextValueState,
Links,
ProcessorInternal,
} from './types';
} from '../types';
import { useProcessorsState, isOnFailureSelector } from './processors_reducer';
import { useProcessorsState, isOnFailureSelector } from '../processors_reducer';
import { deserialize } from './deserialize';
import { deserialize } from '../deserialize';
import { serialize } from './serialize';
import { serialize } from '../serialize';
import { OnActionHandler } from './components/processors_tree';
import { OnActionHandler } from '../components/processors_tree';
import {
ProcessorRemoveModal,
PipelineProcessorsItemTooltip,
ProcessorSettingsForm,
OnSubmitHandler,
} from './components';
} from '../components';
import { getValue } from './utils';
import { getValue } from '../utils';
const PipelineProcessorsContext = createContext<ContextValue>({} as any);
export interface Props {
links: Links;
api: ApiService;
toasts: NotificationsSetup['toasts'];
value: {
processors: Processor[];
onFailure?: Processor[];
@ -62,6 +67,8 @@ export interface Props {
export const PipelineProcessorsContextProvider: FunctionComponent<Props> = ({
links,
api,
toasts,
value: { processors: originalProcessors, onFailure: originalOnFailureProcessors },
onUpdate,
onFlyoutOpen,
@ -205,6 +212,8 @@ export const PipelineProcessorsContextProvider: FunctionComponent<Props> = ({
<PipelineProcessorsContext.Provider
value={{
links,
api,
toasts,
onTreeAction,
state,
}}

View file

@ -6,10 +6,12 @@
export { PipelineProcessorsContextProvider, Props } from './context';
export { ProcessorsEditorContextProvider } from './context';
export { ProcessorsEditor, GlobalOnFailureProcessorsEditor } from './editors';
export { OnUpdateHandlerArg, OnUpdateHandler } from './types';
export { SerializeResult } from './serialize';
export { LoadFromJsonButton, OnDoneLoadJsonHandler } from './components';
export { LoadFromJsonButton, OnDoneLoadJsonHandler, TestPipelineButton } from './components';

View file

@ -5,7 +5,9 @@
*/
import { Dispatch } from 'react';
import { NotificationsSetup } from 'src/core/public';
import { OnFormUpdateArg } from '../../../shared_imports';
import { ApiService } from '../../services';
import { SerializeResult } from './serialize';
import { OnActionHandler, ProcessorInfo } from './components';
import { ProcessorsDispatch, State as ProcessorsReducerState } from './processors_reducer';
@ -75,6 +77,8 @@ export interface ContextValueState {
export interface ContextValue {
links: Links;
toasts: NotificationsSetup['toasts'];
api: ApiService;
onTreeAction: OnActionHandler;
state: ContextValueState;
}

View file

@ -108,7 +108,7 @@ export class ApiService {
public async simulatePipeline(testConfig: {
documents: object[];
verbose?: boolean;
pipeline: Omit<Pipeline, 'name'>;
pipeline: Pick<Pipeline, 'processors' | 'on_failure'>;
}) {
const result = await this.sendRequest({
path: `${API_BASE_PATH}/simulate`,

View file

@ -34,10 +34,6 @@ export class DocumentationService {
public getPutPipelineApiUrl() {
return `${this.esDocBasePath}/put-pipeline-api.html`;
}
public getSimulatePipelineApiUrl() {
return `${this.esDocBasePath}/simulate-pipeline-api.html`;
}
}
export const documentationService = new DocumentationService();

View file

@ -9799,7 +9799,6 @@
"xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel": "値",
"xpack.ingestPipelines.pipelineEditor.setForm.valueRequiredError": "設定する値が必要です。",
"xpack.ingestPipelines.pipelineEditor.settingsForm.learnMoreLabelLink.processor": "{processorLabel}ドキュメント",
"xpack.ingestPipelines.pipelineEditor.testPipelineButtonLabel": "パイプラインをテスト",
"xpack.ingestPipelines.pipelineEditor.typeField.fieldRequiredError": "タイプが必要です。",
"xpack.ingestPipelines.pipelineEditor.typeField.typeFieldLabel": "プロセッサー",
"xpack.ingestPipelines.processors.label.append": "末尾に追加",
@ -9857,13 +9856,11 @@
"xpack.ingestPipelines.testPipelineFlyout.documentsTab.simulateDocumentionLink": "詳細",
"xpack.ingestPipelines.testPipelineFlyout.documentsTab.tabDescriptionText": "投入するパイプラインのドキュメントの配列を指定します。{learnMoreLink}",
"xpack.ingestPipelines.testPipelineFlyout.executePipelineError": "パイプラインを実行できません",
"xpack.ingestPipelines.testPipelineFlyout.invalidPipelineErrorMessage": "実行するパイプラインが無効です。",
"xpack.ingestPipelines.testPipelineFlyout.outputTab.descriptionLinkLabel": "出力を更新",
"xpack.ingestPipelines.testPipelineFlyout.outputTab.descriptionText": "出力データを表示するか、パイプライン経由で渡されるときに各プロセッサーがドキュメントにどのように影響するのかを確認します。",
"xpack.ingestPipelines.testPipelineFlyout.outputTab.verboseSwitchLabel": "冗長出力を表示",
"xpack.ingestPipelines.testPipelineFlyout.successNotificationText": "パイプラインが実行されました",
"xpack.ingestPipelines.testPipelineFlyout.title": "パイプラインをテスト",
"xpack.ingestPipelines.testPipelineFlyout.withPipelineNameTitle": "パイプライン'{pipelineName}'をテスト",
"xpack.lens.app.docLoadingError": "保存されたドキュメントの保存中にエラーが発生",
"xpack.lens.app.docSavingError": "ドキュメントの保存中にエラーが発生",
"xpack.lens.app.indexPatternLoadingError": "インデックスパターンの読み込み中にエラーが発生",

View file

@ -9801,7 +9801,6 @@
"xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel": "值",
"xpack.ingestPipelines.pipelineEditor.setForm.valueRequiredError": "需要设置值。",
"xpack.ingestPipelines.pipelineEditor.settingsForm.learnMoreLabelLink.processor": "{processorLabel}文档",
"xpack.ingestPipelines.pipelineEditor.testPipelineButtonLabel": "测试管道",
"xpack.ingestPipelines.pipelineEditor.typeField.fieldRequiredError": "类型必填。",
"xpack.ingestPipelines.pipelineEditor.typeField.typeFieldLabel": "处理器",
"xpack.ingestPipelines.processors.label.append": "追加",
@ -9859,13 +9858,11 @@
"xpack.ingestPipelines.testPipelineFlyout.documentsTab.simulateDocumentionLink": "了解详情",
"xpack.ingestPipelines.testPipelineFlyout.documentsTab.tabDescriptionText": "为管道提供要采集的一系列文档。{learnMoreLink}",
"xpack.ingestPipelines.testPipelineFlyout.executePipelineError": "无法执行管道",
"xpack.ingestPipelines.testPipelineFlyout.invalidPipelineErrorMessage": "要执行的管道无效。",
"xpack.ingestPipelines.testPipelineFlyout.outputTab.descriptionLinkLabel": "刷新输出",
"xpack.ingestPipelines.testPipelineFlyout.outputTab.descriptionText": "查看输出数据或了解文档通过管道时每个处理器对文档的影响。",
"xpack.ingestPipelines.testPipelineFlyout.outputTab.verboseSwitchLabel": "查看详细输出",
"xpack.ingestPipelines.testPipelineFlyout.successNotificationText": "管道已执行",
"xpack.ingestPipelines.testPipelineFlyout.title": "测试管道",
"xpack.ingestPipelines.testPipelineFlyout.withPipelineNameTitle": "测试管道“{pipelineName}”",
"xpack.lens.app.docLoadingError": "加载已保存文档时出错",
"xpack.lens.app.docSavingError": "保存文档时出错",
"xpack.lens.app.indexPatternLoadingError": "加载索引模式时出错",