Skip to content

Commit

Permalink
[EPM] add/remove package in package settings page (#63389) (#63460)
Browse files Browse the repository at this point in the history
* fix bug where assets were not being returned, use archive info for assets

* add settings page, add install/remove button and text

* check existence of datasources associated with this package

* add package title variable to text

* update modal text and rename to uninstall
  • Loading branch information
neptunian authored Apr 15, 2020
1 parent 1cd4895 commit 383ea04
Show file tree
Hide file tree
Showing 13 changed files with 373 additions and 108 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/ingest_manager/common/types/models/epm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export enum InstallStatus {
uninstalling = 'uninstalling',
}

export type DetailViewPanelName = 'overview' | 'data-sources';
export type DetailViewPanelName = 'overview' | 'data-sources' | 'settings';
export type ServiceName = 'kibana' | 'elasticsearch';
export type AssetType = KibanaAssetType | ElasticsearchAssetType | AgentAssetType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Datasource, NewDatasource } from '../models';
import { ListWithKuery } from './common';

export interface GetDatasourcesRequest {
query: ListWithKuery;
query: {
page: number;
perPage: number;
kuery?: string;
};
}

export interface GetDatasourcesResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { sendRequest } from './use_request';
import { sendRequest, useRequest } from './use_request';
import { datasourceRouteService } from '../../services';
import { CreateDatasourceRequest, CreateDatasourceResponse } from '../../types';
import {
DeleteDatasourcesRequest,
DeleteDatasourcesResponse,
GetDatasourcesRequest,
GetDatasourcesResponse,
} from '../../../../../common/types/rest_spec';

export const sendCreateDatasource = (body: CreateDatasourceRequest['body']) => {
Expand All @@ -26,3 +28,11 @@ export const sendDeleteDatasource = (body: DeleteDatasourcesRequest['body']) =>
body: JSON.stringify(body),
});
};

export function useGetDatasources(query: GetDatasourcesRequest['query']) {
return useRequest<GetDatasourcesResponse>({
method: 'get',
path: datasourceRouteService.getListPath(),
query,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export { useLinks } from './use_links';
export { useLocalSearch, searchIdField } from './use_local_search';
export {
PackageInstallProvider,
useDeletePackage,
useUninstallPackage,
useGetPackageInstallStatus,
useInstallPackage,
useSetPackageInstallStatus,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

import createContainer from 'constate';
import React, { useCallback, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { NotificationsStart } from 'src/core/public';
import { useLinks } from '.';
import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public';
import { PackageInfo } from '../../../types';
import { sendInstallPackage, sendRemovePackage } from '../../../hooks';
Expand All @@ -25,7 +25,6 @@ type InstallPackageProps = Pick<PackageInfo, 'name' | 'version' | 'title'>;

function usePackageInstall({ notifications }: { notifications: NotificationsStart }) {
const [packages, setPackage] = useState<PackagesInstall>({});
const { toDetailView } = useLinks();

const setPackageInstallStatus = useCallback(
({ name, status }: { name: PackageInfo['name']; status: InstallStatus }) => {
Expand All @@ -46,34 +45,43 @@ function usePackageInstall({ notifications }: { notifications: NotificationsStar
if (res.error) {
setPackageInstallStatus({ name, status: InstallStatus.notInstalled });
notifications.toasts.addWarning({
title: `Failed to install ${title} package`,
text:
'Something went wrong while trying to install this package. Please try again later.',
title: toMountPoint(
<FormattedMessage
id="xpack.ingestManager.integrations.packageInstallErrorTitle"
defaultMessage="Failed to install {title} package"
values={{ title }}
/>
),
text: toMountPoint(
<FormattedMessage
id="xpack.ingestManager.integrations.packageInstallErrorDescription"
defaultMessage="Something went wrong while trying to install this package. Please try again later."
/>
),
iconType: 'alert',
});
} else {
setPackageInstallStatus({ name, status: InstallStatus.installed });
const SuccessMsg = <p>Successfully installed {name}</p>;

notifications.toasts.addSuccess({
title: `Installed ${title} package`,
text: toMountPoint(SuccessMsg),
title: toMountPoint(
<FormattedMessage
id="xpack.ingestManager.integrations.packageInstallSuccessTitle"
defaultMessage="Installed {title}"
values={{ title }}
/>
),
text: toMountPoint(
<FormattedMessage
id="xpack.ingestManager.integrations.packageInstallSuccessDescription"
defaultMessage="Successfully installed {title}"
values={{ title }}
/>
),
});

// TODO: this should probably live somewhere else and use <Redirect />,
// this hook could return the request state and a component could
// use that state. the component should be able to unsubscribe to prevent memory leaks
const packageUrl = toDetailView({ name, version });
const dataSourcesUrl = toDetailView({
name,
version,
panel: 'data-sources',
withAppRoot: false,
});
if (window.location.href.includes(packageUrl)) window.location.hash = dataSourcesUrl;
}
},
[notifications.toasts, setPackageInstallStatus, toDetailView]
[notifications.toasts, setPackageInstallStatus]
);

const getPackageInstallStatus = useCallback(
Expand All @@ -83,7 +91,7 @@ function usePackageInstall({ notifications }: { notifications: NotificationsStar
[packages]
);

const deletePackage = useCallback(
const uninstallPackage = useCallback(
async ({ name, version, title }: Pick<PackageInfo, 'name' | 'version' | 'title'>) => {
setPackageInstallStatus({ name, status: InstallStatus.uninstalling });
const pkgkey = `${name}-${version}`;
Expand All @@ -92,38 +100,51 @@ function usePackageInstall({ notifications }: { notifications: NotificationsStar
if (res.error) {
setPackageInstallStatus({ name, status: InstallStatus.installed });
notifications.toasts.addWarning({
title: `Failed to delete ${title} package`,
text: 'Something went wrong while trying to delete this package. Please try again later.',
title: toMountPoint(
<FormattedMessage
id="xpack.ingestManager.integrations.packageUninstallErrorTitle"
defaultMessage="Failed to uninstall {title} package"
values={{ title }}
/>
),
text: toMountPoint(
<FormattedMessage
id="xpack.ingestManager.integrations.packageUninstallErrorDescription"
defaultMessage="Something went wrong while trying to uninstall this package. Please try again later."
/>
),
iconType: 'alert',
});
} else {
setPackageInstallStatus({ name, status: InstallStatus.notInstalled });

const SuccessMsg = <p>Successfully deleted {title}</p>;

notifications.toasts.addSuccess({
title: `Deleted ${title} package`,
text: toMountPoint(SuccessMsg),
});

const packageUrl = toDetailView({ name, version });
const dataSourcesUrl = toDetailView({
name,
version,
panel: 'data-sources',
title: toMountPoint(
<FormattedMessage
id="xpack.ingestManager.integrations.packageUninstallSuccessTitle"
defaultMessage="Uninstalled {title}"
values={{ title }}
/>
),
text: toMountPoint(
<FormattedMessage
id="xpack.ingestManager.integrations.packageUninstallSuccessDescription"
defaultMessage="Successfully uninstalled {title}"
values={{ title }}
/>
),
});
if (window.location.href.includes(packageUrl)) window.location.href = dataSourcesUrl;
}
},
[notifications.toasts, setPackageInstallStatus, toDetailView]
[notifications.toasts, setPackageInstallStatus]
);

return {
packages,
installPackage,
setPackageInstallStatus,
getPackageInstallStatus,
deletePackage,
uninstallPackage,
};
}

Expand All @@ -132,11 +153,11 @@ export const [
useInstallPackage,
useSetPackageInstallStatus,
useGetPackageInstallStatus,
useDeletePackage,
useUninstallPackage,
] = createContainer(
usePackageInstall,
value => value.installPackage,
value => value.setPackageInstallStatus,
value => value.getPackageInstallStatus,
value => value.deletePackage
value => value.uninstallPackage
);

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
import { EuiCallOut, EuiConfirmModal, EuiOverlayMask, EuiSpacer } from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';

interface ConfirmPackageInstallProps {
onCancel: () => void;
Expand All @@ -17,18 +18,46 @@ export const ConfirmPackageInstall = (props: ConfirmPackageInstallProps) => {
return (
<EuiOverlayMask>
<EuiConfirmModal
title={`Install ${packageName}?`}
title={
<FormattedMessage
id="xpack.ingestManager.integrations.settings.confirmInstallModal.installTitle"
defaultMessage="Install {packageName}"
values={{ packageName }}
/>
}
onCancel={onCancel}
onConfirm={onConfirm}
cancelButtonText="Cancel"
confirmButtonText="Install package"
cancelButtonText={
<FormattedMessage
id="xpack.ingestManager.integrations.settings.confirmInstallModal.cancelButtonLabel"
defaultMessage="Cancel"
/>
}
confirmButtonText={
<FormattedMessage
id="xpack.ingestManager.integrations.settings.confirmInstallModal.installButtonLabel"
defaultMessage="Install {packageName}"
values={{ packageName }}
/>
}
defaultFocusedButton="confirm"
>
<EuiCallOut title={`This package will install ${numOfAssets} assets.`} />
<EuiCallOut
iconType="iInCircle"
title={
<FormattedMessage
id="xpack.ingestManager.integrations.settings.confirmInstallModal.installCalloutTitle"
defaultMessage="This action will install {numOfAssets} assets"
values={{ numOfAssets }}
/>
}
/>
<EuiSpacer size="l" />
<p>
and will only be accessible to users who have permission to view this Space. Elasticsearch
assets are installed globally and will be accessible to all Kibana users.
<FormattedMessage
id="xpack.ingestManager.integrations.settings.confirmInstallModal.installDescription"
defaultMessage="Kibana assets will be installed in the current Space (Default) and will only be accessible to users who have permission to view this Space. Elasticsearch assets are installed globally and will be accessible to all Kibana users."
/>
</p>
</EuiConfirmModal>
</EuiOverlayMask>
Expand Down
Loading

0 comments on commit 383ea04

Please sign in to comment.