MINOR: supported full fqn based breadcrumb in bulk import/edit page (#20358)

* supported full fqn based breadcrumb in bulk import/edit page

* fix the loading infinite

* revert unwanted code

* fix bulk edit playwright test
This commit is contained in:
Ashish Gupta 2025-03-31 20:55:50 +05:30 committed by GitHub
parent f8d87185f3
commit 2e1cf3a734
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 101 additions and 58 deletions

View File

@ -13,23 +13,20 @@
import ReactDataGrid from '@inovua/reactdatagrid-community'; import ReactDataGrid from '@inovua/reactdatagrid-community';
import { Button, Col, Row } from 'antd'; import { Button, Col, Row } from 'antd';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import React, { useEffect, useMemo } from 'react'; import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { readString } from 'react-papaparse'; import { readString } from 'react-papaparse';
import { useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import { ENTITY_BULK_EDIT_STEPS } from '../../constants/BulkEdit.constants'; import { ENTITY_BULK_EDIT_STEPS } from '../../constants/BulkEdit.constants';
import { ExportTypes } from '../../constants/Export.constants'; import { ExportTypes } from '../../constants/Export.constants';
import { EntityType } from '../../enums/entity.enum'; import { EntityType } from '../../enums/entity.enum';
import { useFqn } from '../../hooks/useFqn'; import { useFqn } from '../../hooks/useFqn';
import { import { getBulkEditCSVExportEntityApi } from '../../utils/EntityBulkEdit/EntityBulkEditUtils';
getBulkEditCSVExportEntityApi, import entityUtilClassBase from '../../utils/EntityUtilClassBase';
getBulkEntityEditBreadcrumbList,
} from '../../utils/EntityBulkEdit/EntityBulkEditUtils';
import Banner from '../common/Banner/Banner'; import Banner from '../common/Banner/Banner';
import { ImportStatus } from '../common/EntityImport/ImportStatus/ImportStatus.component'; import { ImportStatus } from '../common/EntityImport/ImportStatus/ImportStatus.component';
import Loader from '../common/Loader/Loader'; import Loader from '../common/Loader/Loader';
import TitleBreadcrumb from '../common/TitleBreadcrumb/TitleBreadcrumb.component'; import TitleBreadcrumb from '../common/TitleBreadcrumb/TitleBreadcrumb.component';
import { TitleBreadcrumbProps } from '../common/TitleBreadcrumb/TitleBreadcrumb.interface';
import { useEntityExportModalProvider } from '../Entity/EntityExportModalProvider/EntityExportModalProvider.component'; import { useEntityExportModalProvider } from '../Entity/EntityExportModalProvider/EntityExportModalProvider.component';
import Stepper from '../Settings/Services/Ingestion/IngestionStepper/IngestionStepper.component'; import Stepper from '../Settings/Services/Ingestion/IngestionStepper/IngestionStepper.component';
import { BulkEditEntityProps } from './BulkEditEntity.interface'; import { BulkEditEntityProps } from './BulkEditEntity.interface';
@ -41,6 +38,7 @@ const BulkEditEntity = ({
onEditComplete, onEditComplete,
dataSource, dataSource,
columns, columns,
breadcrumbList,
setGridRef, setGridRef,
activeStep, activeStep,
handleBack, handleBack,
@ -52,15 +50,16 @@ const BulkEditEntity = ({
onCSVReadComplete, onCSVReadComplete,
}: BulkEditEntityProps) => { }: BulkEditEntityProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory();
const { fqn } = useFqn(); const { fqn } = useFqn();
const { entityType } = useParams<{ entityType: EntityType }>(); const { entityType } = useParams<{ entityType: EntityType }>();
const { triggerExportForBulkEdit, csvExportData, clearCSVExportData } = const { triggerExportForBulkEdit, csvExportData, clearCSVExportData } =
useEntityExportModalProvider(); useEntityExportModalProvider();
const breadcrumbList: TitleBreadcrumbProps['titleLinks'] = useMemo( const handleCancel = () => {
() => getBulkEntityEditBreadcrumbList(entityType, fqn), clearCSVExportData();
[entityType, fqn] history.push(entityUtilClassBase.getEntityLink(entityType, fqn));
); };
useEffect(() => { useEffect(() => {
triggerExportForBulkEdit({ triggerExportForBulkEdit({
@ -159,6 +158,12 @@ const BulkEditEntity = ({
{activeStep > 0 && ( {activeStep > 0 && (
<Col span={24}> <Col span={24}>
<div className="float-right import-footer"> <div className="float-right import-footer">
{activeStep === 1 && (
<Button disabled={isValidating} onClick={handleCancel}>
{t('label.cancel')}
</Button>
)}
{activeStep > 1 && ( {activeStep > 1 && (
<Button disabled={isValidating} onClick={handleBack}> <Button disabled={isValidating} onClick={handleBack}>
{t('label.previous')} {t('label.previous')}

View File

@ -17,10 +17,12 @@ import {
import { VALIDATION_STEP } from '../../constants/BulkImport.constant'; import { VALIDATION_STEP } from '../../constants/BulkImport.constant';
import { CSVImportResult } from '../../generated/type/csvImportResult'; import { CSVImportResult } from '../../generated/type/csvImportResult';
import { CSVImportJobType } from '../BulkImport/BulkEntityImport.interface'; import { CSVImportJobType } from '../BulkImport/BulkEntityImport.interface';
import { TitleBreadcrumbProps } from '../common/TitleBreadcrumb/TitleBreadcrumb.interface';
export interface BulkEditEntityProps { export interface BulkEditEntityProps {
dataSource: Record<string, string>[]; dataSource: Record<string, string>[];
columns: TypeColumn[]; columns: TypeColumn[];
breadcrumbList: TitleBreadcrumbProps['titleLinks'];
activeStep: VALIDATION_STEP; activeStep: VALIDATION_STEP;
activeAsyncImportJob?: CSVImportJobType; activeAsyncImportJob?: CSVImportJobType;
isValidating: boolean; isValidating: boolean;

View File

@ -81,6 +81,9 @@ export const EntityExportModalProvider = ({
const handleCancel = () => { const handleCancel = () => {
setExportData(null); setExportData(null);
setCSVExportData(undefined);
setCSVExportJob(undefined);
csvExportJobRef.current = undefined;
}; };
const showModal = (data: ExportData) => { const showModal = (data: ExportData) => {
@ -167,11 +170,9 @@ export const EntityExportModalProvider = ({
data, data,
fileName ?? `${exportData?.name}_${getCurrentISODate()}` fileName ?? `${exportData?.name}_${getCurrentISODate()}`
); );
handleCancel();
} }
setDownloading(false); setDownloading(false);
handleCancel();
setCSVExportJob(undefined);
csvExportJobRef.current = undefined;
}, },
[isBulkEdit] [isBulkEdit]
); );
@ -222,7 +223,7 @@ export const EntityExportModalProvider = ({
const providerValue = useMemo( const providerValue = useMemo(
() => ({ () => ({
csvExportData, csvExportData,
clearCSVExportData: () => setCSVExportData(undefined), clearCSVExportData: handleCancel,
showModal, showModal,
triggerExportForBulkEdit: (exportData: ExportData) => { triggerExportForBulkEdit: (exportData: ExportData) => {
setExportData(exportData); setExportData(exportData);

View File

@ -39,6 +39,7 @@ import Banner from '../../../components/common/Banner/Banner';
import { ImportStatus } from '../../../components/common/EntityImport/ImportStatus/ImportStatus.component'; import { ImportStatus } from '../../../components/common/EntityImport/ImportStatus/ImportStatus.component';
import TitleBreadcrumb from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component'; import TitleBreadcrumb from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import { TitleBreadcrumbProps } from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface'; import { TitleBreadcrumbProps } from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface';
import { DataAssetsHeaderProps } from '../../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface';
import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1'; import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1';
import Stepper from '../../../components/Settings/Services/Ingestion/IngestionStepper/IngestionStepper.component'; import Stepper from '../../../components/Settings/Services/Ingestion/IngestionStepper/IngestionStepper.component';
import { UploadFile } from '../../../components/UploadFile/UploadFile'; import { UploadFile } from '../../../components/UploadFile/UploadFile';
@ -61,7 +62,7 @@ import {
} from '../../../utils/CSV/CSV.utils'; } from '../../../utils/CSV/CSV.utils';
import { isBulkEditRoute } from '../../../utils/EntityBulkEdit/EntityBulkEditUtils'; import { isBulkEditRoute } from '../../../utils/EntityBulkEdit/EntityBulkEditUtils';
import { import {
getBulkEntityImportBreadcrumbList, getBulkEntityBreadcrumbList,
getImportedEntityType, getImportedEntityType,
validateCsvString, validateCsvString,
} from '../../../utils/EntityImport/EntityImportUtils'; } from '../../../utils/EntityImport/EntityImportUtils';
@ -97,12 +98,31 @@ const BulkEntityImportPage = () => {
const [gridRef, setGridRef] = useState< const [gridRef, setGridRef] = useState<
MutableRefObject<TypeComputedProps | null> MutableRefObject<TypeComputedProps | null>
>({ current: null }); >({ current: null });
const [entity, setEntity] = useState<DataAssetsHeaderProps['dataAsset']>();
const fetchEntityData = useCallback(async () => {
try {
const response = await entityUtilClassBase.getEntityByFqn(
entityType,
fqn
);
setEntity(response);
} catch (error) {
// not show error here
}
}, [entityType, fqn]);
const isBulkEdit = useMemo( const isBulkEdit = useMemo(
() => isBulkEditRoute(location.pathname), () => isBulkEditRoute(location.pathname),
[location] [location]
); );
const breadcrumbList: TitleBreadcrumbProps['titleLinks'] = useMemo(
() =>
entity ? getBulkEntityBreadcrumbList(entityType, entity, isBulkEdit) : [],
[entityType, entity, isBulkEdit]
);
const importedEntityType = useMemo( const importedEntityType = useMemo(
() => getImportedEntityType(entityType), () => getImportedEntityType(entityType),
[entityType] [entityType]
@ -181,11 +201,6 @@ const BulkEntityImportPage = () => {
[dataSource] [dataSource]
); );
const breadcrumbList: TitleBreadcrumbProps['titleLinks'] = useMemo(
() => getBulkEntityImportBreadcrumbList(entityType, fqn),
[entityType, fqn]
);
const handleBack = () => { const handleBack = () => {
if (activeStep === VALIDATION_STEP.UPDATE) { if (activeStep === VALIDATION_STEP.UPDATE) {
handleActiveStepChange(VALIDATION_STEP.EDIT_VALIDATE); handleActiveStepChange(VALIDATION_STEP.EDIT_VALIDATE);
@ -446,6 +461,10 @@ const BulkEntityImportPage = () => {
] ]
); );
useEffect(() => {
fetchEntityData();
}, [fetchEntityData]);
useEffect(() => { useEffect(() => {
if (socket) { if (socket) {
socket.on(SOCKET_EVENTS.CSV_IMPORT_CHANNEL, (importResponse) => { socket.on(SOCKET_EVENTS.CSV_IMPORT_CHANNEL, (importResponse) => {
@ -474,6 +493,7 @@ const BulkEntityImportPage = () => {
<BulkEditEntity <BulkEditEntity
activeAsyncImportJob={activeAsyncImportJob} activeAsyncImportJob={activeAsyncImportJob}
activeStep={activeStep} activeStep={activeStep}
breadcrumbList={breadcrumbList}
columns={columns} columns={columns}
dataSource={dataSource} dataSource={dataSource}
handleBack={handleBack} handleBack={handleBack}

View File

@ -14,7 +14,6 @@ import Icon from '@ant-design/icons';
import { Button } from 'antd'; import { Button } from 'antd';
import React from 'react'; import React from 'react';
import { ReactComponent as IconEdit } from '../../assets/svg/edit-new.svg'; import { ReactComponent as IconEdit } from '../../assets/svg/edit-new.svg';
import { TitleBreadcrumbProps } from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface';
import { ROUTES } from '../../constants/constants'; import { ROUTES } from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum'; import { EntityType } from '../../enums/entity.enum';
import { import {
@ -23,29 +22,12 @@ import {
} from '../../rest/databaseAPI'; } from '../../rest/databaseAPI';
import { exportDatabaseServiceDetailsInCSV } from '../../rest/serviceAPI'; import { exportDatabaseServiceDetailsInCSV } from '../../rest/serviceAPI';
import { exportTableDetailsInCSV } from '../../rest/tableAPI'; import { exportTableDetailsInCSV } from '../../rest/tableAPI';
import entityUtilClassBase from '../EntityUtilClassBase';
import Fqn from '../Fqn';
import i18n from '../i18next/LocalUtil'; import i18n from '../i18next/LocalUtil';
export const isBulkEditRoute = (pathname: string) => { export const isBulkEditRoute = (pathname: string) => {
return pathname.includes(ROUTES.BULK_EDIT_ENTITY); return pathname.includes(ROUTES.BULK_EDIT_ENTITY);
}; };
export const getBulkEntityEditBreadcrumbList = (
entityType: EntityType,
fqn: string
): TitleBreadcrumbProps['titleLinks'] => [
{
name: Fqn.split(fqn).pop(),
url: entityUtilClassBase.getEntityLink(entityType, fqn),
},
{
name: i18n.t('label.bulk-edit'),
url: '',
activeTitle: true,
},
];
export const getBulkEditCSVExportEntityApi = (entityType: EntityType) => { export const getBulkEditCSVExportEntityApi = (entityType: EntityType) => {
switch (entityType) { switch (entityType) {
case EntityType.DATABASE_SERVICE: case EntityType.DATABASE_SERVICE:

View File

@ -10,16 +10,16 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import i18next from 'i18next';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { TitleBreadcrumbProps } from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface'; import { TitleBreadcrumbProps } from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface';
import { DataAssetsHeaderProps } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface';
import { EntityType } from '../../enums/entity.enum'; import { EntityType } from '../../enums/entity.enum';
import { import {
importEntityInCSVFormat, importEntityInCSVFormat,
importServiceInCSVFormat, importServiceInCSVFormat,
} from '../../rest/importExportAPI'; } from '../../rest/importExportAPI';
import entityUtilClassBase from '../EntityUtilClassBase'; import { getEntityBreadcrumbs } from '../EntityUtils';
import Fqn from '../Fqn'; import i18n from '../i18next/LocalUtil';
type ParsedDataType<T> = Array<T>; type ParsedDataType<T> = Array<T>;
@ -61,20 +61,20 @@ export const getImportedEntityType = (entityType: EntityType) => {
} }
}; };
export const getBulkEntityImportBreadcrumbList = ( export const getBulkEntityBreadcrumbList = (
entityType: EntityType, entityType: EntityType,
fqn: string entity: DataAssetsHeaderProps['dataAsset'],
): TitleBreadcrumbProps['titleLinks'] => [ isBulkEdit: boolean
{ ): TitleBreadcrumbProps['titleLinks'] => {
name: Fqn.split(fqn).pop(), return [
url: entityUtilClassBase.getEntityLink(entityType, fqn), ...getEntityBreadcrumbs(entity, entityType, true),
}, {
{ name: i18n.t(`label.${isBulkEdit ? 'bulk-edit' : 'import'}`),
name: i18next.t('label.import'), url: '',
url: '', activeTitle: true,
activeTitle: true, },
}, ];
]; };
export const validateCsvString = async ( export const validateCsvString = async (
csvData: string, csvData: string,

View File

@ -36,6 +36,12 @@ import SearchIndexDetailsPage from '../pages/SearchIndexDetailsPage/SearchIndexD
import StoredProcedurePage from '../pages/StoredProcedure/StoredProcedurePage'; import StoredProcedurePage from '../pages/StoredProcedure/StoredProcedurePage';
import TableDetailsPageV1 from '../pages/TableDetailsPageV1/TableDetailsPageV1'; import TableDetailsPageV1 from '../pages/TableDetailsPageV1/TableDetailsPageV1';
import TopicDetailsPage from '../pages/TopicDetails/TopicDetailsPage.component'; import TopicDetailsPage from '../pages/TopicDetails/TopicDetailsPage.component';
import {
getDatabaseDetailsByFQN,
getDatabaseSchemaDetailsByFQN,
} from '../rest/databaseAPI';
import { getServiceByFQN } from '../rest/serviceAPI';
import { getTableDetailsByFQN } from '../rest/tableAPI';
import { ExtraDatabaseDropdownOptions } from './Database/Database.util'; import { ExtraDatabaseDropdownOptions } from './Database/Database.util';
import { ExtraDatabaseSchemaDropdownOptions } from './DatabaseSchemaDetailsUtils'; import { ExtraDatabaseSchemaDropdownOptions } from './DatabaseSchemaDetailsUtils';
import { ExtraDatabaseServiceDropdownOptions } from './DatabaseServiceUtils'; import { ExtraDatabaseServiceDropdownOptions } from './DatabaseServiceUtils';
@ -273,6 +279,19 @@ class EntityUtilClassBase {
} }
} }
public getEntityByFqn(entityType: string, fqn: string, fields?: string[]) {
switch (entityType) {
case EntityType.DATABASE_SERVICE:
return getServiceByFQN('databaseServices', fqn, { fields });
case EntityType.DATABASE:
return getDatabaseDetailsByFQN(fqn, { fields });
case EntityType.DATABASE_SCHEMA:
return getDatabaseSchemaDetailsByFQN(fqn, { fields });
default:
return getTableDetailsByFQN(fqn, { fields });
}
}
public getEntityDetailComponent(entityType: string) { public getEntityDetailComponent(entityType: string) {
switch (entityType) { switch (entityType) {
case EntityType.DATABASE: case EntityType.DATABASE:

View File

@ -1651,7 +1651,8 @@ export const getBreadcrumbForTable = (
name: entity.name, name: entity.name,
url: getEntityLinkFromType( url: getEntityLinkFromType(
entity.fullyQualifiedName ?? '', entity.fullyQualifiedName ?? '',
(entity as SourceType).entityType as EntityType ((entity as SourceType).entityType as EntityType) ??
EntityType.TABLE
), ),
}, },
] ]
@ -1938,7 +1939,8 @@ export const getEntityBreadcrumbs = (
name: entity.name, name: entity.name,
url: getEntityLinkFromType( url: getEntityLinkFromType(
entity.fullyQualifiedName ?? '', entity.fullyQualifiedName ?? '',
(entity as SourceType).entityType as EntityType ((entity as SourceType).entityType as EntityType) ??
EntityType.DATABASE
), ),
}, },
]; ];
@ -1975,7 +1977,8 @@ export const getEntityBreadcrumbs = (
name: entity.name, name: entity.name,
url: getEntityLinkFromType( url: getEntityLinkFromType(
entity.fullyQualifiedName ?? '', entity.fullyQualifiedName ?? '',
(entity as SourceType).entityType as EntityType ((entity as SourceType).entityType as EntityType) ??
EntityType.DATABASE_SCHEMA
), ),
}, },
]; ];
@ -1989,6 +1992,17 @@ export const getEntityBreadcrumbs = (
getServiceRouteFromServiceType(ServiceCategory.DATABASE_SERVICES) getServiceRouteFromServiceType(ServiceCategory.DATABASE_SERVICES)
), ),
}, },
...(includeCurrent
? [
{
name: entity.name,
url: getServiceDetailsPath(
entity?.name,
ServiceCategory.DATABASE_SERVICES
),
},
]
: []),
]; ];
case EntityType.DASHBOARD_SERVICE: case EntityType.DASHBOARD_SERVICE: