From 3adfd4aacaf0f5fdbd1d7ab423536f37915faa06 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Wed, 23 Aug 2023 00:40:17 +0200 Subject: [PATCH] refactor IndexService into custom hooks using react-query --- x-pack/plugins/transform/common/constants.ts | 2 + .../public/app/hooks/use_can_delete_index.ts | 33 ++++++ .../public/app/hooks/use_data_view_exists.ts | 32 ++++++ .../public/app/hooks/use_delete_transform.tsx | 108 ++++++++---------- .../action_delete/use_delete_action.tsx | 2 +- .../public/app/services/es_index_service.ts | 29 ----- 6 files changed, 117 insertions(+), 89 deletions(-) create mode 100644 x-pack/plugins/transform/public/app/hooks/use_can_delete_index.ts create mode 100644 x-pack/plugins/transform/public/app/hooks/use_data_view_exists.ts delete mode 100644 x-pack/plugins/transform/public/app/services/es_index_service.ts diff --git a/x-pack/plugins/transform/common/constants.ts b/x-pack/plugins/transform/common/constants.ts index 1584811c0df6b98..1bf6a811671243c 100644 --- a/x-pack/plugins/transform/common/constants.ts +++ b/x-pack/plugins/transform/common/constants.ts @@ -33,7 +33,9 @@ export const addInternalBasePath = (uri: string): string => `${INTERNAL_API_BASE export const addExternalBasePath = (uri: string): string => `${EXTERNAL_API_BASE_PATH}${uri}`; export const TRANSFORM_REACT_QUERY_KEYS = { + CAN_DELETE_INDEX: 'transform.can_delete_index', DATA_SEARCH: 'transform.data_search', + DATA_VIEW_EXISTS: 'transform.data_view_exists', GET_DATA_VIEW_TITLES: 'transform.get_data_view_titles', GET_ES_INDICES: 'transform.get_es_indices', GET_ES_INGEST_PIPELINES: 'transform.get_es_ingest_pipelines', diff --git a/x-pack/plugins/transform/public/app/hooks/use_can_delete_index.ts b/x-pack/plugins/transform/public/app/hooks/use_can_delete_index.ts new file mode 100644 index 000000000000000..d198daa9ab26f59 --- /dev/null +++ b/x-pack/plugins/transform/public/app/hooks/use_can_delete_index.ts @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useQuery } from '@tanstack/react-query'; + +import type { IHttpFetchError } from '@kbn/core-http-browser'; + +import type { PrivilegesAndCapabilities } from '../../../common/privilege/has_privilege_factory'; +import { addInternalBasePath, TRANSFORM_REACT_QUERY_KEYS } from '../../../common/constants'; + +import { useAppDependencies } from '../app_dependencies'; + +export const useCanDeleteIndex = () => { + const { http } = useAppDependencies(); + + return useQuery( + [TRANSFORM_REACT_QUERY_KEYS.CAN_DELETE_INDEX], + async ({ signal }) => { + const resp = await http.get(addInternalBasePath('privileges'), { + version: '1', + signal, + }); + if (!resp) { + return false; + } + return resp.privileges.hasAllPrivileges; + } + ); +}; diff --git a/x-pack/plugins/transform/public/app/hooks/use_data_view_exists.ts b/x-pack/plugins/transform/public/app/hooks/use_data_view_exists.ts new file mode 100644 index 000000000000000..7f2a2b48af1c12d --- /dev/null +++ b/x-pack/plugins/transform/public/app/hooks/use_data_view_exists.ts @@ -0,0 +1,32 @@ +/* + * 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 { useQuery } from '@tanstack/react-query'; + +import type { ErrorType } from '@kbn/ml-error-utils'; + +import { TRANSFORM_REACT_QUERY_KEYS } from '../../../common/constants'; + +import { useAppDependencies } from '../app_dependencies'; + +export const useDataViewExists = (indexName?: string, enabled?: boolean, initialData?: boolean) => { + const { + data: { dataViews: dataViewsContract }, + } = useAppDependencies(); + + return useQuery( + [TRANSFORM_REACT_QUERY_KEYS.DATA_VIEW_EXISTS, indexName], + async () => { + if (indexName === undefined) { + return false; + } + + return (await dataViewsContract.find(indexName)).some(({ title }) => title === indexName); + }, + { enabled, initialData } + ); +}; diff --git a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx index 99fb593b4d1653e..58e3db4e7cd2272 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useMutation } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; @@ -19,24 +19,42 @@ import type { DeleteTransformsResponseSchema, } from '../../../common/api_schemas/delete_transforms'; import { getErrorMessage } from '../../../common/utils/errors'; + import { useAppDependencies, useToastNotifications } from '../app_dependencies'; +import { useCanDeleteIndex } from './use_can_delete_index'; +import { useDataViewExists } from './use_data_view_exists'; import { useRefreshTransformList, type TransformListRow } from '../common'; import { ToastNotificationText } from '../components'; -import { indexService } from '../services/es_index_service'; export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { const { - http, - data: { dataViews: dataViewsContract }, application: { capabilities }, } = useAppDependencies(); const toastNotifications = useToastNotifications(); + const userCanDeleteDataView = + capabilities.savedObjectsManagement.delete === true || capabilities.indexPatterns.save === true; + const [deleteDestIndex, setDeleteDestIndex] = useState(true); - const [deleteDataView, setDeleteDataView] = useState(true); - const [userCanDeleteIndex, setUserCanDeleteIndex] = useState(false); - const [dataViewExists, setDataViewExists] = useState(false); - const [userCanDeleteDataView, setUserCanDeleteDataView] = useState(false); + const [deleteDataView, setDeleteDataView] = useState(userCanDeleteDataView); + + const { error: canDeleteIndexError, data: canDeleteIndex } = useCanDeleteIndex(); + const userCanDeleteIndex = canDeleteIndex === true; + + useEffect(() => { + if (canDeleteIndexError !== null) { + toastNotifications.addDanger( + i18n.translate( + 'xpack.transform.transformList.errorWithCheckingIfUserCanDeleteIndexNotificationErrorMessage', + { + defaultMessage: 'An error occurred checking if user can delete destination index', + } + ) + ); + } + // custom comparison + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [canDeleteIndexError]); const toggleDeleteIndex = useCallback( () => setDeleteDestIndex(!deleteDestIndex), @@ -46,67 +64,39 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { () => setDeleteDataView(!deleteDataView), [deleteDataView] ); - const checkDataViewExists = useCallback( - async (indexName: string) => { - try { - const dvExists = await indexService.dataViewExists(dataViewsContract, indexName); - setDataViewExists(dvExists); - } catch (e) { - const error = extractErrorMessage(e); - toastNotifications.addDanger( - i18n.translate( - 'xpack.transform.deleteTransform.errorWithCheckingIfDataViewExistsNotificationErrorMessage', - { - defaultMessage: 'An error occurred checking if data view {dataView} exists: {error}', - values: { dataView: indexName, error }, - } - ) - ); - } - }, - [dataViewsContract, toastNotifications] + const indexName = useMemo(() => { + // if user only deleting one transform + if (items.length === 1) { + const config = items[0].config; + return Array.isArray(config.dest.index) ? config.dest.index[0] : config.dest.index; + } + }, [items]); + + const { error: dataViewExistsError, data: dataViewExists } = useDataViewExists( + indexName, + items.length === 1, + items.length !== 1 ); - const checkUserIndexPermission = useCallback(async () => { - try { - const userCanDelete = await indexService.canDeleteIndex(http); - if (userCanDelete) { - setUserCanDeleteIndex(true); - } - const canDeleteDataView = - capabilities.savedObjectsManagement.delete === true || - capabilities.indexPatterns.save === true; - setUserCanDeleteDataView(canDeleteDataView); - if (canDeleteDataView === false) { - setDeleteDataView(false); - } - } catch (e) { + useEffect(() => { + if (dataViewExistsError !== null) { toastNotifications.addDanger( i18n.translate( - 'xpack.transform.transformList.errorWithCheckingIfUserCanDeleteIndexNotificationErrorMessage', + 'xpack.transform.deleteTransform.errorWithCheckingIfDataViewExistsNotificationErrorMessage', { - defaultMessage: 'An error occurred checking if user can delete destination index', + defaultMessage: 'An error occurred checking if data view {dataView} exists: {error}', + values: { + dataView: indexName, + error: extractErrorMessage(dataViewExistsError), + }, } ) ); } - }, [http, toastNotifications, capabilities]); - - useEffect(() => { - checkUserIndexPermission(); - - // if user only deleting one transform - if (items.length === 1) { - const config = items[0].config; - const destinationIndex = Array.isArray(config.dest.index) - ? config.dest.index[0] - : config.dest.index; - checkDataViewExists(destinationIndex); - } else { - setDataViewExists(true); - } - }, [checkDataViewExists, checkUserIndexPermission, items]); + // custom comparison + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [dataViewExistsError]); return { userCanDeleteIndex, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx index 357809b54746b5e..e81612fe77bace7 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx @@ -23,7 +23,7 @@ export type DeleteAction = ReturnType; export const useDeleteAction = (forceDisable: boolean) => { const { canDeleteTransform } = useContext(AuthorizationContext).capabilities; - const deleteTransforms = useDeleteTransforms(); + const { mutate: deleteTransforms } = useDeleteTransforms(); const [isModalVisible, setModalVisible] = useState(false); const [items, setItems] = useState([]); diff --git a/x-pack/plugins/transform/public/app/services/es_index_service.ts b/x-pack/plugins/transform/public/app/services/es_index_service.ts deleted file mode 100644 index d8d058b731a74f5..000000000000000 --- a/x-pack/plugins/transform/public/app/services/es_index_service.ts +++ /dev/null @@ -1,29 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { HttpSetup } from '@kbn/core/public'; -import type { DataViewsContract } from '@kbn/data-views-plugin/public'; -import { addInternalBasePath } from '../../../common/constants'; - -export class IndexService { - async canDeleteIndex(http: HttpSetup) { - const privilege = await http.get<{ hasAllPrivileges: boolean }>( - addInternalBasePath(`privileges`), - { version: '1' } - ); - if (!privilege) { - return false; - } - return privilege.hasAllPrivileges; - } - - async dataViewExists(dataViewsContract: DataViewsContract, indexName: string) { - return (await dataViewsContract.find(indexName)).some(({ title }) => title === indexName); - } -} - -export const indexService = new IndexService();