diff --git a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/miscAPI.ts b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/miscAPI.ts
index bdc1ecc58e2..90570966ae2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/miscAPI.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/miscAPI.ts
@@ -12,8 +12,10 @@
*/
import { AxiosResponse } from 'axios';
+import { isUndefined } from 'lodash';
import { Edge } from '../components/EntityLineage/EntityLineage.interface';
import { SearchIndex } from '../enums/search.enum';
+import { getURLWithQueryFields } from '../utils/APIUtils';
import { getCurrentUserId } from '../utils/CommonUtils';
import { getSearchAPIQuery } from '../utils/SearchUtils';
import APIClient from './index';
@@ -116,7 +118,18 @@ export const getInitialEntity: Function = (): Promise => {
export const deleteEntity: Function = (
entityType: string,
- entityId: string
+ entityId: string,
+ isRecursive: boolean
): Promise => {
- return APIClient.delete(`/${entityType}/${entityId}?hardDelete=true`);
+ const searchParams = new URLSearchParams({ hardDelete: `true` });
+ if (!isUndefined(isRecursive)) {
+ searchParams.set('recursive', `${isRecursive}`);
+ }
+ const path = getURLWithQueryFields(
+ `/${entityType}/${entityId}`,
+ '',
+ `${searchParams.toString()}`
+ );
+
+ return APIClient.delete(path);
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ManageTab/ManageTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ManageTab/ManageTab.component.tsx
index c4a4b044bba..27c2d7b724e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ManageTab/ManageTab.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ManageTab/ManageTab.component.tsx
@@ -26,6 +26,7 @@ import { getCategory } from '../../axiosAPIs/tagAPI';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { TITLE_FOR_NON_ADMIN_ACTION } from '../../constants/constants';
import { ENTITY_DELETE_STATE } from '../../constants/entity.constants';
+import { EntityType } from '../../enums/entity.enum';
import { Operation } from '../../generated/entity/policies/accessControl/rule';
import { useAuth } from '../../hooks/authHooks';
import jsonData from '../../jsons/en';
@@ -53,6 +54,8 @@ const ManageTab: FunctionComponent = ({
entityName,
entityType,
entityId,
+ isRecursiveDelete,
+ deletEntityMessage,
}: ManageProps) => {
const history = useHistory();
const { userPermissions, isAdminUser } = useAuth();
@@ -166,9 +169,28 @@ const ManageTab: FunctionComponent = ({
setEntityDeleteState(ENTITY_DELETE_STATE);
};
+ const prepareEntityType = () => {
+ const services = [
+ EntityType.DASHBOARD_SERVICE,
+ EntityType.DATABASE_SERVICE,
+ EntityType.MESSAGING_SERVICE,
+ EntityType.PIPELINE_SERVICE,
+ ];
+
+ if (services.includes((entityType || '') as EntityType)) {
+ return `services/${entityType}s`;
+ } else {
+ return `${entityType}s`;
+ }
+ };
+
+ const prepareDeleteMessage = () => {
+ return `Once you delete this ${entityType}, it will be removed permanently`;
+ };
+
const handleOnEntityDeleteConfirm = () => {
setEntityDeleteState((prev) => ({ ...prev, loading: 'waiting' }));
- deleteEntity(`${entityType}s`, entityId)
+ deleteEntity(prepareEntityType(), entityId, isRecursiveDelete)
.then((res: AxiosResponse) => {
if (res.status === 200) {
setTimeout(() => {
@@ -201,6 +223,7 @@ const ManageTab: FunctionComponent = ({
if (allowDelete && entityDeleteState.state) {
return (
= ({
Delete {entityType} {entityName}
-
- {`Once you delete this ${entityType}, it would be removed permanently`}
-
+ {prepareDeleteMessage()}
{
const inputBox = await findByTestId(container, 'confirmation-text-input');
fireEvent.change(inputBox, {
- target: { value: `${mockProp.entityType}/${mockProp.entityName}` },
+ target: { value: 'DELETE' },
});
expect(confirmButton).not.toBeDisabled();
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityDeleteModal/EntityDeleteModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityDeleteModal/EntityDeleteModal.tsx
index 9546b45e6aa..b95d0854084 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityDeleteModal/EntityDeleteModal.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityDeleteModal/EntityDeleteModal.tsx
@@ -28,6 +28,7 @@ interface Prop extends HTMLAttributes {
entityName: string;
entityType: string;
loadingState: string;
+ bodyText?: string;
}
const EntityDeleteModal: FC = ({
@@ -37,6 +38,7 @@ const EntityDeleteModal: FC = ({
entityType,
onCancel,
onConfirm,
+ bodyText,
}: Prop) => {
const [name, setName] = useState('');
@@ -45,7 +47,7 @@ const EntityDeleteModal: FC = ({
};
const isNameMatching = useCallback(() => {
- return name === `${entityType}/${entityName}`;
+ return name === 'DELETE';
}, [name]);
return (
@@ -60,13 +62,12 @@ const EntityDeleteModal: FC = ({
-
{`Once you delete this ${entityType}, it would be removed permanently`}
- Type{' '}
-
- {entityType}/{entityName}
- {' '}
- to confirm
+ {bodyText ||
+ `Once you delete this ${entityType}, it will be removed permanently`}
+
+
+ Type DELETE to confirm
= ({
indexType,
matches,
tableType,
- service,
- database,
- databaseSchema,
deleted = false,
}: Props) => {
const location = useLocation();
@@ -116,45 +112,6 @@ const TableDataCard: FunctionComponent
= ({
}
};
- const getPopOverContent = () => {
- const entityDetails = [
- {
- key: 'Service Type',
- value: serviceType,
- },
- ];
- if (service) {
- entityDetails.push({
- key: 'Service',
- value: service,
- });
- }
-
- if (database) {
- entityDetails.push({
- key: 'Database',
- value: database,
- });
- }
- if (databaseSchema) {
- entityDetails.push({
- key: 'Schema',
- value: databaseSchema,
- });
- }
-
- return (
-
- {entityDetails.map((detail) => (
-
- {detail.key} :
- {detail.value}
-
- ))}
-
- );
- };
-
return (
= ({
className="tw-inline tw-h-5 tw-w-5"
src={serviceTypeLogo(serviceType || '')}
/>
-
-
-
- {fullyQualifiedName}
-
-
-
+
+
+ {fullyQualifiedName}
+
+
{deleted && (
<>
{
);
};
+ const getDeleteEntityMessage = () => {
+ if (!tableInstanceCount) {
+ return;
+ }
+
+ return `Deleting this databaseSchema will also delete ${pluralize(
+ tableInstanceCount,
+ 'table',
+ 's'
+ )}.`;
+ };
+
useEffect(() => {
if (TabSpecificField.ACTIVITY_FEED === tab) {
fetchActivityFeed();
@@ -675,7 +688,9 @@ const DatabaseSchemaPage: FunctionComponent = () => {
{
useState(pagingObject);
const [databaseSchemaInstanceCount, setSchemaInstanceCount] =
useState(0);
+ const [tableInstanceCount, setTableInstanceCount] = useState(0);
const [activeTab, setActiveTab] = useState(
getCurrentDatabaseDetailsTab(tab)
@@ -521,6 +528,25 @@ const DatabaseDetails: FunctionComponent = () => {
});
};
+ const fetchTablesCount = () => {
+ // limit=0 will fetch empty data list with total count
+ getAllTables('', 0)
+ .then((res: AxiosResponse) => {
+ if (res.data) {
+ setTableInstanceCount(res.data.paging.total);
+ } else {
+ throw jsonData['api-error-messages']['unexpected-server-response'];
+ }
+ })
+ .catch((err: AxiosError) => {
+ showErrorToast(
+ err,
+ jsonData['api-error-messages']['unexpected-server-response']
+ );
+ setTableInstanceCount(0);
+ });
+ };
+
const getLoader = () => {
return isentityThreadLoading ? : null;
};
@@ -535,8 +561,21 @@ const DatabaseDetails: FunctionComponent = () => {
}
};
+ const getDeleteEntityMessage = () => {
+ if (!databaseSchemaInstanceCount && !tableInstanceCount) {
+ return;
+ }
+
+ return `Deleting this database will also delete ${pluralize(
+ databaseSchemaInstanceCount,
+ 'schema',
+ 's'
+ )} and ${pluralize(tableInstanceCount, 'table', 's')}.`;
+ };
+
useEffect(() => {
getEntityFeedCount();
+ fetchTablesCount();
}, []);
useEffect(() => {
@@ -758,7 +797,9 @@ const DatabaseDetails: FunctionComponent = () => {
{