Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List pipelines #62785

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* 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 { i18n } from '@kbn/i18n';
import {
EuiFlyout,
EuiFlyoutHeader,
EuiFlyoutBody,
EuiTitle,
EuiDescriptionList,
EuiCodeBlock,
EuiFlyoutFooter,
EuiFlexGroup,
EuiFlexItem,
EuiButtonEmpty,
} from '@elastic/eui';
import { Pipeline } from '../../../../common/types';

export interface Props {
pipeline: Pipeline;
onEditClick: () => void;
onDeleteClick: () => void;
onClose: () => void;
}

export const PipelineDetails: FunctionComponent<Props> = ({
pipeline,
onClose,
onEditClick,
onDeleteClick,
}) => {
const descriptionListItems = [
{
title: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.descriptionTitle', {
defaultMessage: 'Description',
}),
description: pipeline.description ?? '',
},
];

if (pipeline.version) {
descriptionListItems.push({
title: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.versionTitle', {
defaultMessage: 'Version',
}),
description: String(pipeline.version),
});
}

descriptionListItems.push({
title: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.processorsTitle', {
defaultMessage: 'Processors JSON',
}),
// We use this title from the description list and display the processors in a code block underneath
jloleysens marked this conversation as resolved.
Show resolved Hide resolved
description: '',
});

return (
<EuiFlyout
onClose={onClose}
aria-labelledby="pipelineDetailsFlyoutTitle"
size="m"
maxWidth={550}
>
<EuiFlyoutHeader>
<EuiTitle id="pipelineDetailsFlyoutTitle">
<h2>{pipeline.name}</h2>
</EuiTitle>
</EuiFlyoutHeader>

<EuiFlyoutBody>
<EuiDescriptionList listItems={descriptionListItems} />
<EuiCodeBlock overflowHeight={500} isCopyable language="json">
{JSON.stringify(pipeline.processors, null, 2)}
</EuiCodeBlock>
</EuiFlyoutBody>

<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty iconType="cross" onClick={onClose} flush="left">
{i18n.translate('xpack.ingestPipelines.list.pipelineDetails.closeButtonLabel', {
defaultMessage: 'Close',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexGroup gutterSize="none" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={onEditClick}>
{i18n.translate('xpack.ingestPipelines.list.pipelineDetails.editButtonLabel', {
defaultMessage: 'Edit',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="danger" onClick={onDeleteClick}>
{i18n.translate('xpack.ingestPipelines.list.pipelineDetails.deleteButtonLabel', {
defaultMessage: 'Delete',
})}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexGroup>
</EuiFlyoutFooter>
</EuiFlyout>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';

interface Props {
onClick: () => void;
}

export const EmptyList: FunctionComponent<Props> = ({ onClick }) => (
<EuiEmptyPrompt
iconType="managementApp"
titleSize="xs"
jloleysens marked this conversation as resolved.
Show resolved Hide resolved
title={
<h1>
jloleysens marked this conversation as resolved.
Show resolved Hide resolved
{i18n.translate('xpack.ingestPipelines.list.table.emptyPromptTitle', {
defaultMessage: 'Create your first pipeline',
})}
</h1>
}
actions={
<EuiButton onClick={onClick}>
{i18n.translate('xpack.ingestPipelines.list.table.emptyPrompt.createButtonLabel', {
defaultMessage: 'Create pipeline',
})}
</EuiButton>
}
/>
);
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

export { PipelinesList } from './pipelines_list';
export { PipelinesList } from './main';
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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, { useEffect, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';

import {
EuiPageBody,
EuiPageContent,
EuiTitle,
EuiFlexGroup,
EuiFlexItem,
EuiButtonEmpty,
EuiCallOut,
EuiLoadingSpinner,
} from '@elastic/eui';

import { EuiSpacer, EuiText } from '@elastic/eui';

import { Pipeline } from '../../../../common/types';
import { useKibana } from '../../../shared_imports';
import { UIM_PIPELINES_LIST_LOAD } from '../../constants';

import { EmptyList } from './empty_list';
import { PipelineTable } from './table';
import { PipelineDetails } from './details';

export const PipelinesList: React.FunctionComponent = () => {
const { services } = useKibana();

const [selectedPipeline, setSelectedPipeline] = useState<Pipeline | undefined>(undefined);

// Track component loaded
useEffect(() => {
services.metric.trackUiMetric(UIM_PIPELINES_LIST_LOAD);
}, [services.metric]);

const { data, isLoading, error, sendRequest } = services.api.useLoadPipelines();

let content: React.ReactNode;

if (isLoading) {
content = (
<EuiFlexGroup justifyContent="spaceAround">
jloleysens marked this conversation as resolved.
Show resolved Hide resolved
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="xl" />
</EuiFlexItem>
</EuiFlexGroup>
);
} else if (data?.length) {
content = (
<PipelineTable
onReloadClick={() => {
sendRequest();
}}
onEditPipelineClick={() => {}}
onDeletePipelineClick={() => {}}
onViewPipelineClick={setSelectedPipeline}
pipelines={data}
/>
);
} else {
content = <EmptyList onClick={() => {}} />;
}

return (
<>
<EuiPageBody>
<EuiPageContent>
<EuiTitle size="l">
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<h1 data-test-subj="appTitle">
<FormattedMessage
id="xpack.ingestPipelines.list.listTitle"
defaultMessage="Ingest Pipelines"
/>
</h1>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
href={services.documentation.getIngestNodeUrl()}
target="_blank"
iconType="help"
>
<FormattedMessage
id="xpack.ingestPipelines.list.pipelinesDocsLinkText"
defaultMessage="Ingest Pipelines docs"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
<EuiSpacer size="s" />
<EuiTitle size="s">
<EuiText color="subdued">
<FormattedMessage
id="xpack.ingestPipelines.list.pipelinesDescription"
defaultMessage="Use ingest node pipelines to pre-process documents before indexing."
/>
</EuiText>
</EuiTitle>
<EuiSpacer size="m" />
{/* Error call out or pipeline table */}
{error ? (
<EuiCallOut
iconType="faceSad"
color="danger"
title={i18n.translate('xpack.ingestPipelines.list.loadErrorTitle', {
defaultMessage: 'Cannot load pipelines, please refresh the page to try again.',
})}
/>
) : (
content
)}
</EuiPageContent>
</EuiPageBody>
{selectedPipeline && (
<PipelineDetails
pipeline={selectedPipeline}
onClose={() => setSelectedPipeline(undefined)}
onDeleteClick={() => {}}
onEditClick={() => {}}
/>
)}
</>
);
};

This file was deleted.

Loading