Skip to content

Commit

Permalink
[Ingest pipelines] Delete pipeline (#63635)
Browse files Browse the repository at this point in the history
  • Loading branch information
alisonelizabeth authored Apr 20, 2020
1 parent 7e7d776 commit 6ffaeda
Show file tree
Hide file tree
Showing 12 changed files with 352 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ export const UIM_APP_NAME = 'ingest_pipelines';
export const UIM_PIPELINES_LIST_LOAD = 'pipelines_list_load';
export const UIM_PIPELINE_CREATE = 'pipeline_create';
export const UIM_PIPELINE_UPDATE = 'pipeline_update';
export const UIM_PIPELINE_DELETE = 'pipeline_delete';
export const UIM_PIPELINE_DELETE_MANY = 'pipeline_delete_many';
2 changes: 2 additions & 0 deletions x-pack/plugins/ingest_pipelines/public/application/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import React, { ReactNode } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { NotificationsSetup } from 'kibana/public';
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';

import { App } from './app';
Expand All @@ -16,6 +17,7 @@ export interface AppServices {
metric: UiMetricService;
documentation: DocumentationService;
api: ApiService;
notifications: NotificationsSetup;
}

export const renderApp = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export async function mountManagementSection(
metric: uiMetricService,
documentation: documentationService,
api: apiService,
notifications: coreSetup.notifications,
};

return renderApp(element, I18nContext, services);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* 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 { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';

import { useKibana } from '../../../shared_imports';

export const PipelineDeleteModal = ({
pipelinesToDelete,
callback,
}: {
pipelinesToDelete: string[];
callback: (data?: { hasDeletedPipelines: boolean }) => void;
}) => {
const { services } = useKibana();

const numPipelinesToDelete = pipelinesToDelete.length;

const handleDeletePipelines = () => {
services.api
.deletePipelines(pipelinesToDelete)
.then(({ data: { itemsDeleted, errors }, error }) => {
const hasDeletedPipelines = itemsDeleted && itemsDeleted.length;

if (hasDeletedPipelines) {
const successMessage =
itemsDeleted.length === 1
? i18n.translate(
'xpack.ingestPipelines.deleteModal.successDeleteSingleNotificationMessageText',
{
defaultMessage: "Deleted pipeline '{pipelineName}'",
values: { pipelineName: pipelinesToDelete[0] },
}
)
: i18n.translate(
'xpack.ingestPipelines.deleteModal.successDeleteMultipleNotificationMessageText',
{
defaultMessage:
'Deleted {numSuccesses, plural, one {# pipeline} other {# pipelines}}',
values: { numSuccesses: itemsDeleted.length },
}
);

callback({ hasDeletedPipelines });
services.notifications.toasts.addSuccess(successMessage);
}

if (error || errors?.length) {
const hasMultipleErrors = errors?.length > 1 || (error && pipelinesToDelete.length > 1);
const errorMessage = hasMultipleErrors
? i18n.translate(
'xpack.ingestPipelines.deleteModal.multipleErrorsNotificationMessageText',
{
defaultMessage: 'Error deleting {count} pipelines',
values: {
count: errors?.length || pipelinesToDelete.length,
},
}
)
: i18n.translate('xpack.ingestPipelines.deleteModal.errorNotificationMessageText', {
defaultMessage: "Error deleting pipeline '{name}'",
values: { name: (errors && errors[0].name) || pipelinesToDelete[0] },
});
services.notifications.toasts.addDanger(errorMessage);
}
});
};

const handleOnCancel = () => {
callback();
};

return (
<EuiOverlayMask>
<EuiConfirmModal
buttonColor="danger"
data-test-subj="deletePipelinesConfirmation"
title={
<FormattedMessage
id="xpack.ingestPipelines.deleteModal.modalTitleText"
defaultMessage="Delete {numPipelinesToDelete, plural, one {pipeline} other {# pipelines}}"
values={{ numPipelinesToDelete }}
/>
}
onCancel={handleOnCancel}
onConfirm={handleDeletePipelines}
cancelButtonText={
<FormattedMessage
id="xpack.ingestPipelines.deleteModal.cancelButtonLabel"
defaultMessage="Cancel"
/>
}
confirmButtonText={
<FormattedMessage
id="xpack.ingestPipelines.deleteModal.confirmButtonLabel"
defaultMessage="Delete {numPipelinesToDelete, plural, one {pipeline} other {pipelines} }"
values={{ numPipelinesToDelete }}
/>
}
>
<>
<p>
<FormattedMessage
id="xpack.ingestPipelines.deleteModal.deleteDescription"
defaultMessage="You are about to delete {numPipelinesToDelete, plural, one {this pipeline} other {these pipelines} }:"
values={{ numPipelinesToDelete }}
/>
</p>

<ul>
{pipelinesToDelete.map(name => (
<li key={name}>{name}</li>
))}
</ul>
</>
</EuiConfirmModal>
</EuiOverlayMask>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { PipelineDetailsJsonBlock } from './details_json_block';
export interface Props {
pipeline: Pipeline;
onEditClick: (pipelineName: string) => void;
onDeleteClick: () => void;
onDeleteClick: (pipelineName: string[]) => void;
onClose: () => void;
}

Expand Down Expand Up @@ -122,7 +122,7 @@ export const PipelineDetails: FunctionComponent<Props> = ({
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="danger" onClick={onDeleteClick}>
<EuiButtonEmpty color="danger" onClick={() => onDeleteClick([pipeline.name])}>
{i18n.translate('xpack.ingestPipelines.list.pipelineDetails.deleteButtonLabel', {
defaultMessage: 'Delete',
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ import { UIM_PIPELINES_LIST_LOAD } from '../../constants';
import { EmptyList } from './empty_list';
import { PipelineTable } from './table';
import { PipelineDetails } from './details';
import { PipelineDeleteModal } from './delete_modal';

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

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

// Track component loaded
useEffect(() => {
Expand Down Expand Up @@ -63,7 +65,7 @@ export const PipelinesList: React.FunctionComponent<RouteComponentProps> = ({ hi
<PipelineTable
onReloadClick={sendRequest}
onEditPipelineClick={editPipeline}
onDeletePipelineClick={() => {}}
onDeletePipelineClick={setPipelinesToDelete}
onViewPipelineClick={setSelectedPipeline}
pipelines={data}
/>
Expand Down Expand Up @@ -128,10 +130,23 @@ export const PipelinesList: React.FunctionComponent<RouteComponentProps> = ({ hi
<PipelineDetails
pipeline={selectedPipeline}
onClose={() => setSelectedPipeline(undefined)}
onDeleteClick={() => {}}
onDeleteClick={setPipelinesToDelete}
onEditClick={editPipeline}
/>
)}
{pipelinesToDelete?.length > 0 ? (
<PipelineDeleteModal
callback={deleteResponse => {
if (deleteResponse?.hasDeletedPipelines) {
// reload pipelines list
sendRequest();
}
setPipelinesToDelete([]);
setSelectedPipeline(undefined);
}}
pipelinesToDelete={pipelinesToDelete}
/>
) : null}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* 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 React, { FunctionComponent, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiInMemoryTable, EuiLink, EuiButton } from '@elastic/eui';

import { BASE_PATH } from '../../../../common/constants';
Expand All @@ -13,8 +14,8 @@ import { Pipeline } from '../../../../common/types';
export interface Props {
pipelines: Pipeline[];
onReloadClick: () => void;
onEditPipelineClick: (pipeineName: string) => void;
onDeletePipelineClick: (pipeline: Pipeline) => void;
onEditPipelineClick: (pipelineName: string) => void;
onDeletePipelineClick: (pipelineName: string[]) => void;
onViewPipelineClick: (pipeline: Pipeline) => void;
}

Expand All @@ -25,9 +26,32 @@ export const PipelineTable: FunctionComponent<Props> = ({
onDeletePipelineClick,
onViewPipelineClick,
}) => {
const [selection, setSelection] = useState<Pipeline[]>([]);

return (
<EuiInMemoryTable
itemId="name"
isSelectable
selection={{
onSelectionChange: setSelection,
}}
search={{
toolsLeft:
selection.length > 0 ? (
<EuiButton
data-test-subj="deletePipelinesButton"
onClick={() => onDeletePipelineClick(selection.map(pipeline => pipeline.name))}
color="danger"
>
<FormattedMessage
id="xpack.ingestPipelines.list.table.deletePipelinesButtonLabel"
defaultMessage="Delete {count, plural, one {pipeline} other {pipelines} }"
values={{ count: selection.length }}
/>
</EuiButton>
) : (
undefined
),
toolsRight: [
<EuiButton
key="reloadButton"
Expand Down Expand Up @@ -66,7 +90,7 @@ export const PipelineTable: FunctionComponent<Props> = ({
name: i18n.translate('xpack.ingestPipelines.list.table.nameColumnTitle', {
defaultMessage: 'Name',
}),
render: (name: any, pipeline) => (
render: (name: string, pipeline) => (
<EuiLink onClick={() => onViewPipelineClick(pipeline)}>{name}</EuiLink>
),
},
Expand Down Expand Up @@ -98,7 +122,7 @@ export const PipelineTable: FunctionComponent<Props> = ({
type: 'icon',
icon: 'trash',
color: 'danger',
onClick: onDeletePipelineClick,
onClick: ({ name }) => onDeletePipelineClick([name]),
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import {
useRequest as _useRequest,
} from '../../shared_imports';
import { UiMetricService } from './ui_metric';
import { UIM_PIPELINE_CREATE, UIM_PIPELINE_UPDATE } from '../constants';
import {
UIM_PIPELINE_CREATE,
UIM_PIPELINE_UPDATE,
UIM_PIPELINE_DELETE,
UIM_PIPELINE_DELETE_MANY,
} from '../constants';

export class ApiService {
private client: HttpSetup | undefined;
Expand Down Expand Up @@ -87,6 +92,17 @@ export class ApiService {

return result;
}

public async deletePipelines(names: string[]) {
const result = this.sendRequest({
path: `${API_BASE_PATH}/${names.map(name => encodeURIComponent(name)).join(',')}`,
method: 'delete',
});

this.trackUiMetric(names.length > 1 ? UIM_PIPELINE_DELETE_MANY : UIM_PIPELINE_DELETE);

return result;
}
}

export const apiService = new ApiService();
49 changes: 49 additions & 0 deletions x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 { schema } from '@kbn/config-schema';

import { API_BASE_PATH } from '../../../common/constants';
import { RouteDependencies } from '../../types';

const paramsSchema = schema.object({
names: schema.string(),
});

export const registerDeleteRoute = ({ router, license }: RouteDependencies): void => {
router.delete(
{
path: `${API_BASE_PATH}/{names}`,
validate: {
params: paramsSchema,
},
},
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient;
const { names } = req.params;
const pipelineNames = names.split(',');

const response: { itemsDeleted: string[]; errors: any[] } = {
itemsDeleted: [],
errors: [],
};

await Promise.all(
pipelineNames.map(pipelineName => {
return callAsCurrentUser('ingest.deletePipeline', { id: pipelineName })
.then(() => response.itemsDeleted.push(pipelineName))
.catch(e =>
response.errors.push({
name: pipelineName,
error: e,
})
);
})
);

return res.ok({ body: response });
})
);
};
2 changes: 2 additions & 0 deletions x-pack/plugins/ingest_pipelines/server/routes/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ export { registerGetRoutes } from './get';
export { registerCreateRoute } from './create';

export { registerUpdateRoute } from './update';

export { registerDeleteRoute } from './delete';
Loading

0 comments on commit 6ffaeda

Please sign in to comment.