- {(entityInfoV1 || []).map((item: any, index: number) => (
+ {(entityInfoV1 || []).map((item: { name: string; value: string }) => (
+ key={item.name + item.value}>
{item.name} {item.value}
))}
@@ -226,8 +226,8 @@ jest.mock('../common/DataQualitySection/DataQualitySection', () => {
return jest.fn().mockImplementation(({ tests, totalTests }) => (
{totalTests}
- {tests.map((test: any, index: number) => (
-
+ {tests.map((test: { type: string; count: number }) => (
+
{test.type}: {test.count}
))}
@@ -387,7 +387,7 @@ describe('DataAssetSummaryPanelV1', () => {
const mockOnDescriptionUpdate = jest.fn();
const defaultProps: DataAssetSummaryPanelProps = {
- dataAsset: mockDataAsset as any,
+ dataAsset: mockDataAsset as unknown as DataAssetType,
entityType: EntityType.TABLE,
isLoading: false,
onOwnerUpdate: mockOnOwnerUpdate,
@@ -434,10 +434,7 @@ describe('DataAssetSummaryPanelV1', () => {
{ name: 'Queries', value: 250, visible: ['explore'] },
{
name: 'Incidents',
- value:
- (additionalInfo && additionalInfo.incidentCount) !== undefined
- ? additionalInfo.incidentCount
- : 0,
+ value: additionalInfo?.incidentCount ?? 0,
visible: ['explore'],
},
]
@@ -608,7 +605,7 @@ describe('DataAssetSummaryPanelV1', () => {
dataAsset: {
...mockDataAsset,
deleted: true,
- } as any,
+ } as unknown as DataAssetType,
};
await act(async () => {
@@ -916,7 +913,7 @@ describe('DataAssetSummaryPanelV1', () => {
name: 'new-table',
displayName: 'New Table',
fullyQualifiedName: 'new.fqn',
- } as any;
+ } as unknown as DataAssetType;
await act(async () => {
rerender(
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.tsx
index 3a30eca1f29a..eec9149e74b5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.tsx
@@ -10,30 +10,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { AxiosError } from 'axios';
+import { Operation } from 'fast-json-patch';
import { isEmpty } from 'lodash';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
+import { ENTITY_PATH } from '../../constants/constants';
+import { PROFILER_FILTER_RANGE } from '../../constants/profiler.constant';
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
import {
OperationPermission,
ResourceEntity,
} from '../../context/PermissionProvider/PermissionProvider.interface';
import { useTourProvider } from '../../context/TourProvider/TourProvider';
-import {
- getCurrentMillis,
- getEpochMillisForPastDays,
-} from '../../utils/date-time/DateTimeUtils';
-import EntityLink from '../../utils/EntityLink';
-import {
- DRAWER_NAVIGATION_OPTIONS,
- getEntityOverview,
- hasLineageTab,
-} from '../../utils/EntityUtils';
-
-import { AxiosError } from 'axios';
-import { Operation } from 'fast-json-patch';
-import { ENTITY_PATH } from '../../constants/constants';
-import { PROFILER_FILTER_RANGE } from '../../constants/profiler.constant';
import { EntityType } from '../../enums/entity.enum';
import { EntityReference } from '../../generated/entity/type';
import { TagLabel, TestCaseStatus } from '../../generated/tests/testCase';
@@ -42,7 +31,17 @@ import { useChangeSummary } from '../../hooks/useChangeSummary';
import { getListTestCaseIncidentStatus } from '../../rest/incidentManagerAPI';
import { updateTableColumn } from '../../rest/tableAPI';
import { listTestCases } from '../../rest/testAPI';
+import { getEntityOverview } from '../../utils/DataAssetSummaryPanelUtils';
+import {
+ getCurrentMillis,
+ getEpochMillisForPastDays,
+} from '../../utils/date-time/DateTimeUtils';
+import EntityLink from '../../utils/EntityLink';
import entityUtilClassBase from '../../utils/EntityUtilClassBase';
+import {
+ DRAWER_NAVIGATION_OPTIONS,
+ hasLineageTab,
+} from '../../utils/EntityUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import { generateEntityLink, getTierTags } from '../../utils/TableUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSLACard/ContractSLA.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSLACard/ContractSLA.component.tsx
index 5bcb872b67b2..a63fad044937 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSLACard/ContractSLA.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSLACard/ContractSLA.component.tsx
@@ -20,8 +20,8 @@ import { ReactComponent as DefaultIcon } from '../../../assets/svg/ic-task.svg';
import { DATA_CONTRACT_SLA } from '../../../constants/DataContract.constants';
import { DataContract } from '../../../generated/entity/data/dataContract';
import { Table } from '../../../generated/entity/data/table';
-import { Transi18next } from '../../../utils/CommonUtils';
import { getEntityName } from '../../../utils/EntityUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
import './contract-sla.less';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSLACard/ContractSLA.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSLACard/ContractSLA.test.tsx
index 26ca45c264ae..1f3ec3324e7b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSLACard/ContractSLA.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSLACard/ContractSLA.test.tsx
@@ -22,12 +22,23 @@ import { MOCK_DATA_CONTRACT } from '../../../mocks/DataContract.mock';
import { mockTableData } from '../../../mocks/TableVersion.mock';
import ContractSLA from './ContractSLA.component';
-jest.mock('../../../utils/CommonUtils', () => ({
- Transi18next: ({ i18nKey, values }: any) => (
+jest.mock('../../../utils/i18next/LocalUtil', () => ({
+ Transi18next: ({
+ i18nKey,
+ values,
+ }: {
+ i18nKey: string;
+ values: Record
;
+ }) => (
{i18nKey} - {values?.label}: {values?.data}
),
+ __esModule: true,
+ default: {
+ t: jest.fn().mockImplementation((key) => key),
+ },
+ t: jest.fn().mockImplementation((key) => key),
}));
jest.mock('../../../assets/svg/ic-check-circle-2.svg', () => ({
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/EmptyGraphPlaceholder.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/EmptyGraphPlaceholder.tsx
index ff60ae271c23..3d3cf5bf7da5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/EmptyGraphPlaceholder.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/EmptyGraphPlaceholder.tsx
@@ -16,7 +16,7 @@ import { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import { DATA_INSIGHT_DOCS } from '../../constants/docs.constants';
import { ERROR_PLACEHOLDER_TYPE, SIZE } from '../../enums/common.enum';
-import { Transi18next } from '../../utils/CommonUtils';
+import { Transi18next } from '../../utils/i18next/LocalUtil';
import ErrorPlaceHolder from '../common/ErrorWithPlaceholder/ErrorPlaceHolder';
export const EmptyGraphPlaceholder = ({ icon }: { icon?: ReactElement }) => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/KPIChart.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/KPIChart.test.tsx
index f8fa7e2c1f0c..9e24397d17e8 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/KPIChart.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/KPIChart.test.tsx
@@ -22,14 +22,6 @@ jest.mock('../../rest/KpiAPI', () => ({
.mockImplementation(() => Promise.resolve({ data: KPI_LIST })),
}));
-jest.mock('../../utils/i18next/LocalUtil', () => ({
- t: jest.fn((key: string) => key),
- translateWithNestedKeys: jest.fn((key: string, nestedKey?: string) => {
- return nestedKey ? `${key}.${nestedKey}` : key;
- }),
- detectBrowserLanguage: jest.fn(() => 'en-US'),
-}));
-
describe('Test KPIChart Component', () => {
const mockProps = {
chartFilter: {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataMarketplace/MarketplaceNavBar/MarketplaceNavBar.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataMarketplace/MarketplaceNavBar/MarketplaceNavBar.component.tsx
index 8454dd3ad87c..8d6c103601ca 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataMarketplace/MarketplaceNavBar/MarketplaceNavBar.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataMarketplace/MarketplaceNavBar/MarketplaceNavBar.component.tsx
@@ -15,7 +15,6 @@ import { Alert, Badge, Button, Dropdown, Tooltip } from 'antd';
import { Header } from 'antd/lib/layout/layout';
import { AxiosError } from 'axios';
import { CookieStorage } from 'cookie-storage';
-import i18next from 'i18next';
import { startCase, upperCase } from 'lodash';
import { MenuInfo } from 'rc-menu/lib/interface';
import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -58,7 +57,8 @@ import {
prepareFeedLink,
} from '../../../utils/FeedUtils';
import { languageSelectOptions } from '../../../utils/i18next/i18nextUtil';
-import { SupportedLocales } from '../../../utils/i18next/LocalUtil.interface';
+import i18n from '../../../utils/i18next/LocalUtil';
+import localUtilClassBase from '../../../utils/i18next/LocalUtilClassBase';
import { getHelpDropdownItems } from '../../../utils/NavbarUtils';
import { getSettingPath } from '../../../utils/RouterUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
@@ -90,7 +90,7 @@ const MarketplaceNavBar = () => {
const [activeTab, setActiveTab] = useState('Task');
const { appVersion: version, setAppVersion } = useApplicationStore();
const {
- preferences: { isSidebarCollapsed, language },
+ preferences: { isSidebarCollapsed },
setPreference,
} = useCurrentUserPreferences();
@@ -363,12 +363,16 @@ const MarketplaceNavBar = () => {
fetchOMVersion();
}, []);
- const handleLanguageChange = useCallback(({ key }: MenuInfo) => {
- i18next.changeLanguage(key);
- setPreference({ language: key as SupportedLocales });
+ const handleLanguageChange = useCallback(async ({ key }: MenuInfo) => {
+ await localUtilClassBase.loadLocales(key);
+ await i18n.changeLanguage(key);
navigate(0);
}, []);
+ const currentLanguage = i18n.language
+ ? upperCase(i18n.language.split('-')[0])
+ : '';
+
return (
<>
{
className="flex-center gap-2 p-x-xs font-medium"
data-testid="language-selector-button"
type="text">
- {language ? upperCase(language.split('-')[0]) : ''}{' '}
-
+ {currentLanguage}
({
- useTranslation: jest.fn(() => ({
- t: (key: string, params?: Record) => {
- if (key.includes('.') && params) {
- return key.replace(
- /\{\{(\w+)\}\}/g,
- (_, paramKey) => params[paramKey] || ''
- );
- }
-
- return key;
- },
- })),
- Trans: jest.fn(({ children, i18nKey }) => {
- // Simple mock for Trans component that renders children
- if (typeof children === 'string') {
- return children;
- }
-
- return children || i18nKey;
- }),
-}));
-
jest.mock('@openmetadata/ui-core-components', () => ({
Alert: ({
title,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.tsx
index f497157a02da..80da4b0d0e98 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.tsx
@@ -99,7 +99,6 @@ import {
import {
filterSelectOptions,
replaceAllSpacialCharWith_,
- Transi18next,
} from '../../../../utils/CommonUtils';
import {
convertSearchSourceToTable,
@@ -111,6 +110,7 @@ import {
generateFormFields,
getPopupContainer,
} from '../../../../utils/formUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import { getScheduleOptionsFromSchedules } from '../../../../utils/SchedularUtils';
import { getIngestionName } from '../../../../utils/ServiceUtils';
import {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx
index 340b5a39c799..fffd19b7c25e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx
@@ -47,12 +47,13 @@ import { TestSuite } from '../../../../generated/tests/testSuite';
import { TestCasePageTabs } from '../../../../pages/IncidentManager/IncidentManager.interface';
import { getListTestCaseIncidentByStateId } from '../../../../rest/incidentManagerAPI';
import { removeTestCaseFromTestSuite } from '../../../../rest/testAPI';
-import { getNameFromFQN, Transi18next } from '../../../../utils/CommonUtils';
+import { getNameFromFQN } from '../../../../utils/CommonUtils';
import {
getColumnNameFromEntityLink,
getEntityName,
} from '../../../../utils/EntityUtils';
import { getEntityFQN } from '../../../../utils/FeedUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import {
getEntityDetailsPath,
getTestCaseDetailPagePath,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/SingleColumnProfile.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/SingleColumnProfile.tsx
index e469e58f89ef..2b7d69b97678 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/SingleColumnProfile.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/SingleColumnProfile.tsx
@@ -34,11 +34,9 @@ import {
import { Table } from '../../../../generated/entity/data/table';
import useCustomLocation from '../../../../hooks/useCustomLocation/useCustomLocation';
import { getColumnProfilerList } from '../../../../rest/tableAPI';
-import {
- formatNumberWithComma,
- Transi18next,
-} from '../../../../utils/CommonUtils';
+import { formatNumberWithComma } from '../../../../utils/CommonUtils';
import documentationLinksClassBase from '../../../../utils/DocumentationLinksClassBase';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import {
calculateColumnProfilerMetrics,
calculateCustomMetrics,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/TableProfilerChart/TableProfilerChart.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/TableProfilerChart/TableProfilerChart.tsx
index 86a46dbd4a0f..1e7559c8effa 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/TableProfilerChart/TableProfilerChart.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/TableProfilerChart/TableProfilerChart.tsx
@@ -31,8 +31,8 @@ import {
getSystemProfileList,
getTableProfilesList,
} from '../../../../../rest/tableAPI';
-import { Transi18next } from '../../../../../utils/CommonUtils';
import documentationLinksClassBase from '../../../../../utils/DocumentationLinksClassBase';
+import { Transi18next } from '../../../../../utils/i18next/LocalUtil';
import {
calculateCustomMetrics,
calculateRowCountMetrics,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SampleDataTable/SampleDataTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SampleDataTable/SampleDataTable.component.tsx
index daa2528cb95c..332ca93d6844 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SampleDataTable/SampleDataTable.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SampleDataTable/SampleDataTable.component.tsx
@@ -33,11 +33,9 @@ import {
deleteSampleDataByTableId,
getSampleDataByTableId,
} from '../../../rest/tableAPI';
-import {
- getEntityDeleteMessage,
- Transi18next,
-} from '../../../utils/CommonUtils';
+import { getEntityDeleteMessage } from '../../../utils/CommonUtils';
import { downloadFile } from '../../../utils/Export/ExportUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { showErrorToast } from '../../../utils/ToastUtils';
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import Loader from '../../common/Loader/Loader';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SampleDataWithMessages/SampleDataWithMessages.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SampleDataWithMessages/SampleDataWithMessages.tsx
index ebe6ac527d33..c461fbc5a11c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SampleDataWithMessages/SampleDataWithMessages.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SampleDataWithMessages/SampleDataWithMessages.tsx
@@ -22,7 +22,7 @@ import { TopicSampleData } from '../../../generated/entity/data/topic';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import { getSampleDataBySearchIndexId } from '../../../rest/SearchIndexAPI';
import { getSampleDataByTopicId } from '../../../rest/topicsAPI';
-import { Transi18next } from '../../../utils/CommonUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import Loader from '../../common/Loader/Loader';
import MessageCard from './MessageCard';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailPage/DomainDetailPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailPage/DomainDetailPage.test.tsx
index 6f342eca829b..8a97f23e0c4d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailPage/DomainDetailPage.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailPage/DomainDetailPage.test.tsx
@@ -26,7 +26,6 @@ jest.mock('../../../utils/i18next/LocalUtil', () => ({
t: (key: string) => key,
},
t: (key: string) => key,
- detectBrowserLanguage: () => 'en-US',
}));
// Mock react-helmet-async
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx
index 6c03c3380d1d..06f6fd712e1d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx
@@ -64,7 +64,7 @@ import {
import { addDomains, patchDomains } from '../../../rest/domainAPI';
import { getActiveAnnouncement } from '../../../rest/feedsAPI';
import { searchQuery } from '../../../rest/searchAPI';
-import { getFeedCounts, getIsErrorMatch } from '../../../utils/CommonUtils';
+import { getFeedCounts } from '../../../utils/CommonUtils';
import { createEntityWithCoverImage } from '../../../utils/CoverImageUploadUtils';
import {
checkIfExpandViewSupported,
@@ -107,6 +107,7 @@ import { useBreadcrumbs } from '../../common/atoms/navigation/useBreadcrumbs';
import { Avatar } from '@openmetadata/ui-core-components';
import { LEARNING_PAGE_IDS } from '../../../constants/Learning.constants';
import { FeedCounts } from '../../../interface/feed.interface';
+import { getIsErrorMatch } from '../../../utils/APIUtils';
import { getEntityAvatarProps } from '../../../utils/IconUtils';
import { withActivityFeed } from '../../AppRouter/withActivityFeed';
import { CoverImage } from '../../common/CoverImage/CoverImage.component';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchModal.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchModal.component.tsx
index 1c48413543ab..e29185044e39 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchModal.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchModal.component.tsx
@@ -13,7 +13,6 @@
import { Builder, Query } from '@react-awesome-query-builder/antd';
import { Button, Modal, Space, Typography } from 'antd';
-import 'antd/dist/antd.css';
import { FunctionComponent } from 'react';
import { useTranslation } from 'react-i18next';
import './advanced-search-modal.less';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.test.tsx
index e771ba0fd73a..0ae9421248d3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.test.tsx
@@ -32,18 +32,6 @@ jest.mock('react-router-dom', () => ({
)),
}));
-// Mock Transi18next component
-jest.mock('../../../../utils/CommonUtils', () => ({
- Transi18next: jest
- .fn()
- .mockImplementation(({ i18nKey, renderElement, values }) => (
-
- {i18nKey} - {values?.entity} - {values?.docs}
- {renderElement}
-
- )),
-}));
-
// Mock Loader component
jest.mock('../../../common/Loader/Loader', () => {
return jest.fn().mockImplementation(({ size }) => (
@@ -273,10 +261,9 @@ describe('CustomPropertiesSection', () => {
expect(errorPlaceholder).toBeInTheDocument();
expect(errorPlaceholder).toHaveAttribute('data-type', 'PERMISSION');
- const transComponent = screen.getByTestId('trans-component');
-
- expect(transComponent).toBeInTheDocument();
- expect(transComponent).toHaveTextContent('message.no-access-placeholder');
+ expect(errorPlaceholder).toHaveTextContent(
+ 'message.no-access-placeholder'
+ );
expect(screen.queryByTestId('search-bar')).not.toBeInTheDocument();
expect(screen.queryByTestId('property-name')).not.toBeInTheDocument();
@@ -297,10 +284,7 @@ describe('CustomPropertiesSection', () => {
expect(errorPlaceholder).toBeInTheDocument();
expect(errorPlaceholder).toHaveAttribute('data-type', 'CUSTOM');
- const transComponent = screen.getByTestId('trans-component');
-
- expect(transComponent).toBeInTheDocument();
- expect(transComponent).toHaveTextContent(
+ expect(errorPlaceholder).toHaveTextContent(
'message.no-custom-properties-entity'
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.tsx
index f153f4ee3a40..fe2130b7e7c9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.tsx
@@ -18,7 +18,7 @@ import { ReactComponent as AddPlaceHolderIcon } from '../../../../assets/svg/ic-
import { CUSTOM_PROPERTIES_DOCS } from '../../../../constants/docs.constants';
import { ERROR_PLACEHOLDER_TYPE } from '../../../../enums/common.enum';
import { CustomProperty } from '../../../../generated/entity/type';
-import { Transi18next } from '../../../../utils/CommonUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import { PropertyValue } from '../../../common/CustomPropertyTable/PropertyValue';
import ErrorPlaceHolderNew from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolderNew';
import Loader from '../../../common/Loader/Loader';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.test.tsx
index b92573748be8..b753447d5ee9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.test.tsx
@@ -236,7 +236,7 @@ jest.mock('../../../../utils/EntityUtils', () => ({
if (entityLink.includes('::columns::')) {
const parts = entityLink.split('::columns::');
- return parts[parts.length - 1];
+ return parts.at(-1);
}
return null;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.tsx
index da5b16cf07cb..8d11246e13dc 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.tsx
@@ -33,15 +33,13 @@ import {
import { Include } from '../../../../generated/type/include';
import { getListTestCaseIncidentStatus } from '../../../../rest/incidentManagerAPI';
import { getListTestCaseBySearch } from '../../../../rest/testAPI';
-import {
- getTableFQNFromColumnFQN,
- Transi18next,
-} from '../../../../utils/CommonUtils';
+import { getTableFQNFromColumnFQN } from '../../../../utils/CommonUtils';
import {
getCurrentMillis,
getEpochMillisForPastDays,
} from '../../../../utils/date-time/DateTimeUtils';
import { getColumnNameFromEntityLink } from '../../../../utils/EntityUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import { getTestCaseDetailPagePath } from '../../../../utils/RouterUtils';
import { generateEntityLink } from '../../../../utils/TableUtils';
import { showErrorToast } from '../../../../utils/ToastUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.test.tsx
index 249909833fb9..9bc36b61d308 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.test.tsx
@@ -89,17 +89,20 @@ jest.mock('../../../utils/EntityUtils', () => {
return {
getEntityLinkFromType: jest.fn().mockImplementation(() => 'link'),
getEntityName: jest.fn().mockImplementation(() => 'displayName'),
- getEntityOverview: jest.fn().mockImplementation(() => []),
hasLineageTab: jest.fn((entityType) => LINEAGE_TABS_SET.has(entityType)),
hasSchemaTab: jest.fn((entityType) => SCHEMA_TABS_SET.has(entityType)),
+ getEntityOverview: jest.fn().mockImplementation(() => []),
hasCustomPropertiesTab: jest.fn((entityType) =>
CUSTOM_PROPERTIES_TABS_SET.has(entityType)
),
+ DRAWER_NAVIGATION_OPTIONS: [],
};
});
jest.mock('../../../utils/StringsUtils', () => ({
getEncodedFqn: jest.fn().mockImplementation((fqn) => fqn),
stringToHTML: jest.fn(),
+ bytesToSize: jest.fn(),
+ ordinalize: jest.fn(),
}));
jest.mock('react-router-dom', () => ({
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.test.tsx
index aa9ef2473a89..e5dbb069d0f3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.test.tsx
@@ -19,12 +19,6 @@ jest.mock('react-router-dom', () => ({
}),
}));
-jest.mock('react-i18next', () => ({
- useTranslation: jest.fn().mockReturnValue({
- t: jest.fn().mockImplementation((key) => key),
- }),
-}));
-
describe('ExploreTree', () => {
it('renders the correct tree nodes', async () => {
const { getByText, queryByTestId } = render(
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx
index eb9418c9e116..4ae1f0cc8699 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx
@@ -27,7 +27,7 @@ import { EntityType } from '../../../enums/entity.enum';
import { ExplorePageTabs } from '../../../enums/Explore.enum';
import { SearchIndex } from '../../../enums/search.enum';
import { searchQuery } from '../../../rest/searchAPI';
-import { getCountBadge, Transi18next } from '../../../utils/CommonUtils';
+import { getCountBadge } from '../../../utils/CommonUtils';
import entityUtilClassBase from '../../../utils/EntityUtilClassBase';
import { getPluralizeEntityName } from '../../../utils/EntityUtils';
import {
@@ -38,6 +38,7 @@ import {
updateTreeData,
updateTreeDataWithCounts,
} from '../../../utils/ExploreUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import searchClassBase from '../../../utils/SearchClassBase';
import serviceUtilClassBase from '../../../utils/ServiceUtilClassBase';
import { generateUUID } from '../../../utils/StringsUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreV1.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreV1.test.tsx
index c10e38470805..57a0880a0c8f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreV1.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreV1.test.tsx
@@ -245,17 +245,6 @@ jest.mock('react-i18next', () => ({
Trans: ({ children }: { children: React.ReactNode }) => <>{children}>,
}));
-jest.mock('../../utils/CommonUtils', () => ({
- Transi18next: jest
- .fn()
- .mockImplementation(({ i18nKey, renderElement, values }) => (
-
- {i18nKey} {values && JSON.stringify(values)}
- {renderElement}
-
- )),
-}));
-
jest.mock('../../utils/AdvancedSearchUtils', () => ({
getDropDownItems: jest.fn().mockReturnValue([]),
}));
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/IndexNotFoundBanner.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/IndexNotFoundBanner.test.tsx
index 51bd41bc57b9..6b7c1a44d0eb 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/IndexNotFoundBanner.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/IndexNotFoundBanner.test.tsx
@@ -16,12 +16,6 @@ import { SEARCH_INDEXING_APPLICATION } from '../../constants/explore.constants';
import { getApplicationDetailsPath } from '../../utils/RouterUtils';
import { IndexNotFoundBanner } from './IndexNotFoundBanner';
-jest.mock('react-i18next', () => ({
- useTranslation: jest.fn().mockReturnValue({
- t: (key: string) => key,
- }),
-}));
-
jest.mock('../../hooks/useApplicationStore', () => ({
useApplicationStore: jest.fn().mockReturnValue({
theme: {
@@ -34,18 +28,6 @@ jest.mock('../../utils/RouterUtils', () => ({
getApplicationDetailsPath: jest.fn().mockReturnValue('/settings/search'),
}));
-jest.mock('../../utils/CommonUtils', () => ({
- Transi18next: jest
- .fn()
- .mockImplementation(({ i18nKey, renderElement, values }) => (
-
- {i18nKey}
- {values?.settings}
- {renderElement}
-
- )),
-}));
-
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
Link: ({
@@ -86,17 +68,17 @@ jest.mock('antd', () => ({
}));
describe('IndexNotFoundBanner', () => {
- it('renders indexing error details and re-index help text', () => {
+ it('renders indexing error details and re-index help text', async () => {
render();
expect(screen.getByTestId('index-not-found-alert')).toBeInTheDocument();
expect(screen.getByText('server.indexing-error')).toBeInTheDocument();
- expect(screen.getByTestId('trans-i18next')).toHaveTextContent(
- 'message.configure-search-re-index'
- );
- expect(screen.getByTestId('trans-settings-value')).toHaveTextContent(
- 'label.search-index-setting-plural'
- );
+ expect(
+ await screen.findByText(/message.configure-search-re-index/)
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(/label.search-index-setting-plural/)
+ ).toBeInTheDocument();
});
it('builds the settings link using search indexing application path', () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/IndexNotFoundBanner.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/IndexNotFoundBanner.tsx
index fe54bddd81bd..2af63492c9d9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/IndexNotFoundBanner.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/IndexNotFoundBanner.tsx
@@ -17,7 +17,7 @@ import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { SEARCH_INDEXING_APPLICATION } from '../../constants/explore.constants';
import { useApplicationStore } from '../../hooks/useApplicationStore';
-import { Transi18next } from '../../utils/CommonUtils';
+import { Transi18next } from '../../utils/i18next/LocalUtil';
import { getApplicationDetailsPath } from '../../utils/RouterUtils';
export const IndexNotFoundBanner = () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx
index 359b2b60f0b4..742db91b4855 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx
@@ -87,7 +87,6 @@ import {
patchGlossaryTerm,
searchGlossaryTermsPaginated,
} from '../../../rest/glossaryAPI';
-import { Transi18next } from '../../../utils/CommonUtils';
import { getBulkEditButton } from '../../../utils/EntityBulkEdit/EntityBulkEditUtils';
import { EntityStatusClass } from '../../../utils/EntityStatusUtils';
import {
@@ -101,6 +100,7 @@ import {
glossaryTermTableColumnsWidth,
permissionForApproveOrReject,
} from '../../../utils/GlossaryUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { getGlossaryPath } from '../../../utils/RouterUtils';
import { ownerTableObject } from '../../../utils/TableColumn.util';
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Layout/CarouselLayout/CarouselLayout.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Layout/CarouselLayout/CarouselLayout.tsx
index 6056176245ad..efae472360f8 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Layout/CarouselLayout/CarouselLayout.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Layout/CarouselLayout/CarouselLayout.tsx
@@ -13,11 +13,15 @@
import { Col, Grid, Layout, Row } from 'antd';
import { Content } from 'antd/lib/layout/layout';
import classNames from 'classnames';
-import { ReactNode } from 'react';
-import LoginCarousel from '../../../pages/LoginPage/LoginCarousel';
+import { lazy, ReactNode } from 'react';
+import withSuspenseFallback from '../../AppRouter/withSuspenseFallback';
import DocumentTitle from '../../common/DocumentTitle/DocumentTitle';
import './carousel-layout.less';
+const LoginCarousel = withSuspenseFallback(
+ lazy(() => import('../../../pages/LoginPage/LoginCarousel'))
+);
+
export const CarouselLayout = ({
pageTitle,
children,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.test.tsx
index a194e42337e7..136a0a296cd6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.test.tsx
@@ -75,7 +75,6 @@ jest.mock('react-router-dom', () => ({
}));
jest.mock('../../utils/CommonUtils', () => ({
- Transi18next: ({ i18nKey }: { i18nKey: string }) => i18nKey,
getPartialNameFromTableFQN: jest
.fn()
.mockImplementation((fqn: string) => fqn),
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.tsx
index fdd867dfb75c..564118389707 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.tsx
@@ -55,7 +55,6 @@ import {
getLineageByEntityCount,
getLineageDataByFQN,
} from '../../rest/lineageAPI';
-import { Transi18next } from '../../utils/CommonUtils';
import {
getEntityLinkFromType,
getEntityName,
@@ -63,6 +62,7 @@ import {
} from '../../utils/EntityUtils';
import { getQuickFilterQuery } from '../../utils/ExploreUtils';
import Fqn from '../../utils/Fqn';
+import { Transi18next } from '../../utils/i18next/LocalUtil';
import {
getSearchNameEsQuery,
LINEAGE_IMPACT_OPTIONS,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ChangeParentHierarchy/ChangeParentHierarchy.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ChangeParentHierarchy/ChangeParentHierarchy.component.tsx
index e2c52f9cb79c..970f072db257 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ChangeParentHierarchy/ChangeParentHierarchy.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ChangeParentHierarchy/ChangeParentHierarchy.component.tsx
@@ -28,9 +28,9 @@ import {
GlossaryTerm,
} from '../../../generated/entity/data/glossaryTerm';
import { moveGlossaryTerm } from '../../../rest/glossaryAPI';
-import { Transi18next } from '../../../utils/CommonUtils';
import { EntityStatusClass } from '../../../utils/EntityStatusUtils';
import { getEntityName } from '../../../utils/EntityUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { getGlossaryPath } from '../../../utils/RouterUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
import Banner from '../../common/Banner/Banner';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ChangeParentHierarchy/ChangeParentHierarchy.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ChangeParentHierarchy/ChangeParentHierarchy.test.tsx
index 6fe3b74d3e05..20a3dfdeee8e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ChangeParentHierarchy/ChangeParentHierarchy.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ChangeParentHierarchy/ChangeParentHierarchy.test.tsx
@@ -11,14 +11,9 @@
* limitations under the License.
*/
-import {
- act,
- findByRole,
- fireEvent,
- render,
- screen,
-} from '@testing-library/react';
+import { findByRole, fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import { act } from 'react';
import { PageType } from '../../../generated/system/ui/page';
import { mockedGlossaryTerms } from '../../../mocks/Glossary.mock';
import ChangeParent from './ChangeParentHierarchy.component';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityDeleteModal/EntityDeleteModal.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityDeleteModal/EntityDeleteModal.test.tsx
index 565a492e3020..e79e9e8298c0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityDeleteModal/EntityDeleteModal.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityDeleteModal/EntityDeleteModal.test.tsx
@@ -11,9 +11,10 @@
* limitations under the License.
*/
-import { act, fireEvent, render, screen } from '@testing-library/react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
-import * as CommonUtils from '../../../utils/CommonUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import EntityDeleteModal from './EntityDeleteModal';
const onCancel = jest.fn();
@@ -35,13 +36,6 @@ jest.mock('../../../utils/BrandData/BrandClassBase', () => ({
},
}));
-jest.mock('react-i18next', () => ({
- Trans: jest.fn().mockImplementation(() => Trans
),
- useTranslation: () => ({
- t: (key: string) => key,
- }),
-}));
-
describe('Test EntityDelete Modal Component', () => {
it('Should render component', async () => {
await act(async () => {
@@ -141,18 +135,6 @@ describe('Test EntityDelete Modal Component', () => {
});
it('should render with correct brandName (OpenMetadata or Collate)', async () => {
- // Mock Transi18next to actually render interpolated values
- const mockTransi18next = jest.fn(({ values }) => (
-
- {values?.entityName && `Entity: ${values.entityName}`}
- {values?.brandName && ` Brand: ${values.brandName}`}
-
- ));
-
- jest
- .spyOn(CommonUtils, 'Transi18next')
- .mockImplementation(mockTransi18next);
-
await act(async () => {
render(, {
wrapper: MemoryRouter,
@@ -168,7 +150,7 @@ describe('Test EntityDelete Modal Component', () => {
expect(bodyText.textContent).not.toContain('{{brandName}}');
// Verify Transi18next was called with brandName parameter
- expect(mockTransi18next).toHaveBeenCalledWith(
+ expect(Transi18next).toHaveBeenCalledWith(
expect.objectContaining({
i18nKey: 'message.permanently-delete-metadata',
values: expect.objectContaining({
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 7bbc831c4935..42c4c40d3905 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
@@ -15,7 +15,7 @@ import { Button, Input, InputRef, Modal, Typography } from 'antd';
import { ChangeEvent, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import brandClassBase from '../../../utils/BrandData/BrandClassBase';
-import { Transi18next } from '../../../utils/CommonUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { EntityDeleteModalProp } from './EntityDeleteModal.interface';
const EntityDeleteModal = ({
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx
index 27ad2d6a5b99..ec6f45fe2d79 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx
@@ -24,7 +24,7 @@ import { Link, useNavigate } from 'react-router-dom';
import { PageType } from '../../../../generated/system/ui/page';
import { useFqn } from '../../../../hooks/useFqn';
import { useCustomizeStore } from '../../../../pages/CustomizablePage/CustomizeStore';
-import { Transi18next } from '../../../../utils/CommonUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import { getPersonaDetailsPath } from '../../../../utils/RouterUtils';
import { UnsavedChangesModal } from '../../../Modals/UnsavedChangesModal/UnsavedChangesModal.component';
import './customizable-page-header.less';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/left-sidebar.less b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/left-sidebar.less
index e46656063771..6434a41d33c2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/left-sidebar.less
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/left-sidebar.less
@@ -36,8 +36,10 @@
}
.left-sidebar-menu {
- display: flex;
- flex-direction: column;
+ &:not(.ant-menu-submenu-hidden) {
+ display: flex;
+ flex-direction: column;
+ }
.ant-menu-item,
.ant-menu-submenu {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/MyTaskWidget/my-task-widget.less b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/MyTaskWidget/my-task-widget.less
index eaa06b019f9b..73289d967edb 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/MyTaskWidget/my-task-widget.less
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/MyTaskWidget/my-task-widget.less
@@ -115,18 +115,8 @@
}
.widget-header-options {
- padding: 10px;
- height: 40px;
min-width: 40px;
max-width: 150px;
- border-radius: 8px;
- border: 1px solid @grey-15;
- font-size: 20px;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: @white;
&:hover {
background-color: @grey-1;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx
index 619f0fc33e4b..8066e4d6cacb 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx
@@ -10,7 +10,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { act, render, screen } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import { act } from 'react';
import {
LAST_VERSION_FETCH_TIME_KEY,
ONE_HOUR_MS,
@@ -213,6 +214,11 @@ jest.mock('./PopupAlertClassBase', () => ({
},
}));
+jest.mock('../../utils/i18next/i18nextUtil', () => ({
+ languageSelectOptions: [],
+ getInitOptions: jest.fn().mockImplementation(() => ({})),
+}));
+
describe('Test NavBar Component', () => {
it('Should render NavBar component', async () => {
render();
@@ -287,6 +293,7 @@ describe('handleDocumentVisibilityChange one hour threshold', () => {
jest.resetModules();
jest.clearAllMocks();
global.Date.now = jest.fn();
+ mockUseCustomLocation.pathname = '/';
});
afterEach(() => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
index 4659927d9249..0bed65667459 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
@@ -24,7 +24,6 @@ import { Header } from 'antd/lib/layout/layout';
import { AxiosError } from 'axios';
import classNames from 'classnames';
import { CookieStorage } from 'cookie-storage';
-import i18next from 'i18next';
import { startCase, upperCase } from 'lodash';
import { MenuInfo } from 'rc-menu/lib/interface';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -73,7 +72,8 @@ import {
prepareFeedLink,
} from '../../utils/FeedUtils';
import { languageSelectOptions } from '../../utils/i18next/i18nextUtil';
-import { SupportedLocales } from '../../utils/i18next/LocalUtil.interface';
+import i18n from '../../utils/i18next/LocalUtil';
+import localUtilClassBase from '../../utils/i18next/LocalUtilClassBase';
import { isCommandKeyPress, Keys } from '../../utils/KeyboardUtil';
import { getHelpDropdownItems } from '../../utils/NavbarUtils';
import { getSettingPath } from '../../utils/RouterUtils';
@@ -111,7 +111,7 @@ const NavBar = () => {
const { appVersion: version, setAppVersion } = useApplicationStore();
const [isDomainDropdownOpen, setIsDomainDropdownOpen] = useState(false);
const {
- preferences: { isSidebarCollapsed, language },
+ preferences: { isSidebarCollapsed },
setPreference,
} = useCurrentUserPreferences();
@@ -438,12 +438,16 @@ const NavBar = () => {
[activeDomainEntityRef, activeDomain, t]
);
- const handleLanguageChange = useCallback(({ key }: MenuInfo) => {
- i18next.changeLanguage(key);
- setPreference({ language: key as SupportedLocales });
+ const handleLanguageChange = useCallback(async ({ key }: MenuInfo) => {
+ await localUtilClassBase.loadLocales(key);
+ await i18n.changeLanguage(key);
navigate(0);
}, []);
+ const currentLanguage = i18n.language
+ ? upperCase(i18n.language.split('-')[0])
+ : '';
+
return (
<>
{
className="flex-center gap-2 p-x-xs font-medium"
data-testid="language-selector-button"
type="text">
- {language ? upperCase(language.split('-')[0]) : ''}{' '}
+ {currentLanguage}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationBox.utils.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationBox.utils.tsx
index 5ddb863e62e0..7680c59b148b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationBox.utils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationBox.utils.tsx
@@ -12,16 +12,16 @@
*/
import Icon from '@ant-design/icons';
-import i18next from 'i18next';
import { ReactComponent as IconMentions } from '../../assets/svg/ic-mentions.svg';
import { ReactComponent as IconTask } from '../../assets/svg/ic-task.svg';
import { FeedFilter } from '../../enums/mydata.enum';
import { NotificationTabsKey } from '../../enums/notification.enum';
import { ThreadType } from '../../generated/api/feed/createThread';
+import i18n from '../../utils/i18next/LocalUtil';
export const tabsInfo = [
{
- name: i18next.t('label.task-plural'),
+ name: i18n.t('label.task-plural'),
key: NotificationTabsKey.TASK,
icon: (
(
+ lazy(() => import('../ApplicationConfiguration/ApplicationConfiguration'))
+ );
+
class ApplicationsClassBase {
public async importSchema(fqn: string) {
const module = await import(
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppInstallVerifyCard/AppInstallVerifyCard.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppInstallVerifyCard/AppInstallVerifyCard.component.tsx
index 5f25fddad5ca..768612b3812d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppInstallVerifyCard/AppInstallVerifyCard.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppInstallVerifyCard/AppInstallVerifyCard.component.tsx
@@ -27,9 +27,9 @@ import {
import { useTranslation } from 'react-i18next';
import { LIGHT_GREEN_COLOR } from '../../../../constants/constants';
import { useApplicationStore } from '../../../../hooks/useApplicationStore';
-import { Transi18next } from '../../../../utils/CommonUtils';
import { getRelativeTime } from '../../../../utils/date-time/DateTimeUtils';
import { getEntityName } from '../../../../utils/EntityUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import BrandImage from '../../../common/BrandImage/BrandImage';
import UserPopOverCard from '../../../common/PopOverCard/UserPopOverCard';
import AppLogo from '../AppLogo/AppLogo.component';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/MarketPlaceAppDetails/MarketPlaceAppDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/MarketPlaceAppDetails/MarketPlaceAppDetails.component.tsx
index 42f9b4845568..48d978694ee5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/MarketPlaceAppDetails/MarketPlaceAppDetails.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/MarketPlaceAppDetails/MarketPlaceAppDetails.component.tsx
@@ -35,8 +35,8 @@ import { useFqn } from '../../../../hooks/useFqn';
import { getApplicationByName } from '../../../../rest/applicationAPI';
import { getMarketPlaceApplicationByFqn } from '../../../../rest/applicationMarketPlaceAPI';
import brandClassBase from '../../../../utils/BrandData/BrandClassBase';
-import { Transi18next } from '../../../../utils/CommonUtils';
import { getEntityName } from '../../../../utils/EntityUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import { getAppInstallPath } from '../../../../utils/RouterUtils';
import { showErrorToast } from '../../../../utils/ToastUtils';
import Loader from '../../../common/Loader/Loader';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.test.tsx
index 2c8ea78134d1..360e28d182c9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.test.tsx
@@ -11,8 +11,9 @@
* limitations under the License.
*/
-import { act, fireEvent, render, screen } from '@testing-library/react';
+import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { usePermissionProvider } from '../../../../../context/PermissionProvider/PermissionProvider';
import { mockIngestionData } from '../../../../../mocks/Ingestion.mock';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx
index 3c059d5746f1..78d5f8cf2683 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx
@@ -40,12 +40,12 @@ import {
deleteIngestionPipelineById,
getRunHistoryForPipeline,
} from '../../../../../rest/ingestionPipelineAPI';
-import { Transi18next } from '../../../../../utils/CommonUtils';
import {
getColumnSorter,
getEntityName,
highlightSearchText,
} from '../../../../../utils/EntityUtils';
+import { Transi18next } from '../../../../../utils/i18next/LocalUtil';
import {
renderNameField,
renderScheduleField,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/ServiceConfig/ConnectionConfigForm.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/ServiceConfig/ConnectionConfigForm.test.tsx
index 84c5b9402ae3..20e864d9ed8a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/ServiceConfig/ConnectionConfigForm.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/ServiceConfig/ConnectionConfigForm.test.tsx
@@ -10,13 +10,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { act, fireEvent, render, screen } from '@testing-library/react';
-import { forwardRef } from 'react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { act, forwardRef } from 'react';
import { LOADING_STATE } from '../../../../enums/common.enum';
import { ServiceCategory } from '../../../../enums/service.enum';
import { MOCK_ATHENA_SERVICE } from '../../../../mocks/Service.mock';
import { getPipelineServiceHostIp } from '../../../../rest/ingestionPipelineAPI';
-import * as CommonUtils from '../../../../utils/CommonUtils';
+import * as LocalUtils from '../../../../utils/i18next/LocalUtil';
import { formatFormDataForSubmit } from '../../../../utils/JSONSchemaFormUtils';
import { getConnectionSchemas } from '../../../../utils/ServiceConnectionUtils';
import ConnectionConfigForm from './ConnectionConfigForm';
@@ -140,10 +140,6 @@ jest.mock('../../../common/AirflowMessageBanner/AirflowMessageBanner', () => {
);
});
-jest.mock('../../../../utils/CommonUtils', () => ({
- Transi18next: jest.fn().mockReturnValue('message.airflow-host-ip-address'),
-}));
-
jest.mock('../../../../utils/BrandData/BrandClassBase', () => ({
__esModule: true,
default: {
@@ -216,6 +212,12 @@ const mockProps = {
};
describe('ServiceConfig', () => {
+ beforeEach(() => {
+ jest
+ .spyOn(LocalUtils, 'Transi18next')
+ .mockImplementation(() => <>message.airflow-host-ip-address>);
+ });
+
it('should render Service Config', async () => {
render();
@@ -305,9 +307,7 @@ describe('ServiceConfig', () => {
));
- jest
- .spyOn(CommonUtils, 'Transi18next')
- .mockImplementation(mockTransi18next);
+ jest.spyOn(LocalUtils, 'Transi18next').mockImplementation(mockTransi18next);
await act(async () => {
render(
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/ServiceConfig/ConnectionConfigForm.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/ServiceConfig/ConnectionConfigForm.tsx
index 4a4c7143ae2c..c95b5e11baee 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/ServiceConfig/ConnectionConfigForm.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/ServiceConfig/ConnectionConfigForm.tsx
@@ -30,8 +30,7 @@ import { useApplicationStore } from '../../../../hooks/useApplicationStore';
import { ConfigData } from '../../../../interface/service.interface';
import { getPipelineServiceHostIp } from '../../../../rest/ingestionPipelineAPI';
import brandClassBase from '../../../../utils/BrandData/BrandClassBase';
-import { Transi18next } from '../../../../utils/CommonUtils';
-import i18n from '../../../../utils/i18next/LocalUtil';
+import i18n, { Transi18next } from '../../../../utils/i18next/LocalUtil';
import { formatFormDataForSubmit } from '../../../../utils/JSONSchemaFormUtils';
import {
getConnectionSchemas,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/TeamDetailsV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/TeamDetailsV1.tsx
index 057e851f82cd..bb3a69934365 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/TeamDetailsV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/TeamDetailsV1.tsx
@@ -68,13 +68,13 @@ import AddAttributeModal from '../../../../pages/RolesPage/AddAttributeModal/Add
import { ImportType } from '../../../../pages/TeamsPage/ImportTeamsPage/ImportTeamsPage.interface';
import { searchQuery } from '../../../../rest/searchAPI';
import { exportTeam, restoreTeam } from '../../../../rest/teamsAPI';
-import { Transi18next } from '../../../../utils/CommonUtils';
import { getEntityName } from '../../../../utils/EntityUtils';
import {
EXTENSION_POINTS,
TabContribution,
} from '../../../../utils/ExtensionPointTypes';
import { getSettingPageEntityBreadCrumb } from '../../../../utils/GlobalSettingsUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import {
getSettingsPathWithFqn,
getTeamsWithFqnPath,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/TeamHierarchy.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/TeamHierarchy.tsx
index c45f34549ddb..1843a1405a3c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/TeamHierarchy.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/TeamHierarchy.tsx
@@ -25,8 +25,8 @@ import { TabSpecificField } from '../../../../enums/entity.enum';
import { Team } from '../../../../generated/entity/teams/team';
import { Include } from '../../../../generated/type/include';
import { getTeamByName, patchTeamDetail } from '../../../../rest/teamsAPI';
-import { Transi18next } from '../../../../utils/CommonUtils';
import { getEntityName } from '../../../../utils/EntityUtils';
+import { Transi18next } from '../../../../utils/i18next/LocalUtil';
import { descriptionTableObject } from '../../../../utils/TableColumn.util';
import { getTableExpandableConfig } from '../../../../utils/TableUtils';
import { isDropRestricted } from '../../../../utils/TeamUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.test.tsx
index d2e5117f1a3a..39e61c001487 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.test.tsx
@@ -28,12 +28,6 @@ jest.mock('../../utils/ToastUtils', () => ({
showErrorToast: jest.fn(),
}));
-jest.mock('../../utils/CommonUtils', () => ({
- Transi18next: jest
- .fn()
- .mockReturnValue('message.drag-and-drop-or-browse-csv-files-here'),
-}));
-
describe('UploadFile Component', () => {
const defaultProps: UploadFileProps = {
fileType: '.csv',
@@ -44,13 +38,13 @@ describe('UploadFile Component', () => {
jest.clearAllMocks();
});
- it('should render the upload component with correct props', () => {
+ it('should render the upload component with correct props', async () => {
render(
);
expect(screen.getByTestId('upload-file-widget')).toBeInTheDocument();
expect(screen.getByTestId('import-icon')).toBeInTheDocument();
expect(
- screen.getByText('message.drag-and-drop-or-browse-csv-files-here')
+ await screen.findByText(/message.drag-and-drop-or-browse-csv-files-here/)
).toBeInTheDocument();
});
@@ -204,11 +198,11 @@ describe('UploadFile Component', () => {
});
});
- it('should render browse text correctly', () => {
+ it('should render browse text correctly', async () => {
render(
);
expect(
- screen.getByText('message.drag-and-drop-or-browse-csv-files-here')
+ await screen.findByText(/message.drag-and-drop-or-browse-csv-files-here/)
).toBeInTheDocument();
});
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.tsx b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.tsx
index 1d675a554d0f..1ceb7eeeaf2c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.tsx
@@ -17,7 +17,7 @@ import type { UploadRequestOption } from 'rc-upload/lib/interface';
import { FC, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as ImportIcon } from '../../assets/svg/ic-drag-drop.svg';
-import { Transi18next } from '../../utils/CommonUtils';
+import { Transi18next } from '../../utils/i18next/LocalUtil';
import { showErrorToast } from '../../utils/ToastUtils';
import Loader from '../common/Loader/Loader';
import './upload-file.less';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.test.tsx
index bef41340f168..ce489ed09450 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.test.tsx
@@ -12,11 +12,11 @@
*/
import {
- act,
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react';
+import { act } from 'react';
import { EntityType } from '../../../enums/entity.enum';
import { Table } from '../../../generated/entity/data/table';
import { getTypeByFQN } from '../../../rest/metadataTypeAPI';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.tsx
index 14b1c46e3175..83b359b316ea 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.tsx
@@ -26,13 +26,13 @@ import { DetailPageWidgetKeys } from '../../../enums/CustomizeDetailPage.enum';
import { EntityTabs } from '../../../enums/entity.enum';
import { ChangeDescription, Type } from '../../../generated/entity/type';
import { getTypeByFQN } from '../../../rest/metadataTypeAPI';
-import { Transi18next } from '../../../utils/CommonUtils';
import entityUtilClassBase from '../../../utils/EntityUtilClassBase';
import {
getChangedEntityNewValue,
getDiffByFieldName,
getUpdatedExtensionDiffFields,
} from '../../../utils/EntityVersionUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { showErrorToast } from '../../../utils/ToastUtils';
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
import ErrorPlaceHolder from '../ErrorWithPlaceholder/ErrorPlaceHolder';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.test.tsx
index 6bf00eb563c7..07b8eb24d1e0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.test.tsx
@@ -13,6 +13,7 @@
import { act, fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import { ReactNode } from 'react';
import { EntityType } from '../../../enums/entity.enum';
import { mockUserData } from '../../../mocks/MyDataPage.mock';
import { DeleteWidgetModalProps } from './DeleteWidget.interface';
@@ -36,11 +37,6 @@ const mockPropsUser: DeleteWidgetModalProps = {
const mockOnLogoutHandler = jest.fn();
-jest.mock('lodash', () => ({
- ...jest.requireActual('lodash'),
- startCase: jest.fn(),
-}));
-
jest.mock('../../../rest/miscAPI', () => ({
deleteEntity: jest.fn().mockImplementation(() =>
Promise.resolve({
@@ -65,6 +61,11 @@ jest.mock('../../../utils/ToastUtils', () => ({
showSuccessToast: jest.fn(),
}));
+jest.mock('../../../utils/i18next/LocalUtil', () => ({
+ Transi18next: ({ children }: { children: ReactNode }) => children,
+ t: jest.fn().mockImplementation((key: string) => key),
+}));
+
describe('Test DeleteWidgetV1 Component', () => {
it('Component should render properly', async () => {
render(
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx
index b04ee82469d5..4c7545304bbe 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx
@@ -36,8 +36,8 @@ import { useAsyncDeleteProvider } from '../../../context/AsyncDeleteProvider/Asy
import { EntityType } from '../../../enums/entity.enum';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import { deleteEntity } from '../../../rest/miscAPI';
-import { Transi18next } from '../../../utils/CommonUtils';
import deleteWidgetClassBase from '../../../utils/DeleteWidget/DeleteWidgetClassBase';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
import { useAuthProvider } from '../../Auth/AuthProviders/AuthProvider';
import './delete-widget-modal.style.less';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorBoundary/ErrorFallback.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorBoundary/ErrorFallback.tsx
index 9fae684b4927..458fb33992a8 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorBoundary/ErrorFallback.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorBoundary/ErrorFallback.tsx
@@ -27,7 +27,13 @@ const ErrorFallback: React.FC
= ({
}) => {
const navigate = useNavigate();
- const isChunkLoadError = error.message?.startsWith('Loading chunk');
+ const isChunkLoadError =
+ error?.name === 'ChunkLoadError' ||
+ error.message?.startsWith('Loading chunk') || // Legacy Webpack
+ error.message
+ ?.toLowerCase()
+ .includes('failed to fetch dynamically imported module') || // Vite
+ error.message?.toLowerCase().includes('importing a module script failed'); // Vite (Safari)
const message = isChunkLoadError
? t('message.please-refresh-the-page')
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/CreateErrorPlaceHolder.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/CreateErrorPlaceHolder.tsx
index c18ba89a8db4..b519c13bf53d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/CreateErrorPlaceHolder.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/CreateErrorPlaceHolder.tsx
@@ -17,7 +17,7 @@ import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { ReactComponent as AddPlaceHolderIcon } from '../../../assets/svg/add-placeholder.svg';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
-import { Transi18next } from '../../../utils/CommonUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import PermissionErrorPlaceholder from './PermissionErrorPlaceholder';
import { CreatePlaceholderProps } from './placeholder.interface';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/ErrorPlaceHolderES.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/ErrorPlaceHolderES.test.tsx
index 32b21017c789..e443809b4592 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/ErrorPlaceHolderES.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/ErrorPlaceHolderES.test.tsx
@@ -41,6 +41,17 @@ jest.mock('../../../utils/BrandData/BrandClassBase', () => ({
},
}));
+jest.mock('../../../utils/i18next/LocalUtil', () => ({
+ Transi18next: jest.fn().mockImplementation(({ i18nKey }) => {
+ return {i18nKey};
+ }),
+ __esModule: true,
+ default: {
+ t: jest.fn().mockImplementation((key) => key),
+ },
+ t: jest.fn().mockImplementation((key) => key),
+}));
+
const mockErrorMessage =
'An exception with message [Elasticsearch exception [type=index_not_found_exception, reason=no such index [test_search_index]]] was thrown while processing request.';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/ErrorPlaceHolderES.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/ErrorPlaceHolderES.tsx
index 954c49da6b74..f35f26d139d0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/ErrorPlaceHolderES.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/ErrorPlaceHolderES.tsx
@@ -34,8 +34,7 @@ import {
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import { useDomainStore } from '../../../hooks/useDomainStore';
import brandClassBase from '../../../utils/BrandData/BrandClassBase';
-import { Transi18next } from '../../../utils/CommonUtils';
-import i18n from '../../../utils/i18next/LocalUtil';
+import i18n, { Transi18next } from '../../../utils/i18next/LocalUtil';
import { useRequiredParams } from '../../../utils/useRequiredParams';
import ErrorPlaceHolder from './ErrorPlaceHolder';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/FilterErrorPlaceHolder.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/FilterErrorPlaceHolder.tsx
index 1816c3d6cf2a..70a3661302d7 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/FilterErrorPlaceHolder.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/FilterErrorPlaceHolder.tsx
@@ -16,7 +16,7 @@ import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { ReactComponent as FilterPlaceHolderIcon } from '../../../assets/svg/no-search-placeholder.svg';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
-import { Transi18next } from '../../../utils/CommonUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { FilterPlaceholderProps } from './placeholder.interface';
const FilterErrorPlaceHolder = ({
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/PermissionErrorPlaceholder.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/PermissionErrorPlaceholder.tsx
index 0234b2360af8..04bc36cc4077 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/PermissionErrorPlaceholder.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/ErrorWithPlaceholder/PermissionErrorPlaceholder.tsx
@@ -14,7 +14,7 @@ import { Space, Typography } from 'antd';
import classNames from 'classnames';
import { ReactComponent as NoAccessPlaceHolderIcon } from '../../../assets/svg/add-placeholder.svg';
import { SIZE } from '../../../enums/common.enum';
-import { Transi18next } from '../../../utils/CommonUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { PermissionPlaceholderProps } from './placeholder.interface';
const PermissionErrorPlaceholder = ({
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.test.tsx
index ba1e4995f9b7..b15cb8a2f644 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.test.tsx
@@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { AntdConfig, BasicConfig } from '@react-awesome-query-builder/antd';
+import { AntdConfig } from '@react-awesome-query-builder/antd';
import { Registry } from '@rjsf/utils';
import { render, screen } from '@testing-library/react';
import React from 'react';
@@ -19,7 +19,7 @@ import QueryBuilderWidget from './QueryBuilderWidget';
const mockOnFocus = jest.fn();
const mockOnBlur = jest.fn();
const mockOnChange = jest.fn();
-const baseConfig = AntdConfig as BasicConfig;
+const baseConfig = AntdConfig;
jest.mock(
'../../../../../Explore/AdvanceSearchProvider/AdvanceSearchProvider.component',
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
index e919f8060cd0..ea361ba56d19 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
@@ -11,6 +11,14 @@
* limitations under the License.
*/
import { InfoCircleOutlined } from '@ant-design/icons';
+import {
+ Actions,
+ Builder,
+ Config,
+ ImmutableTree,
+ Query,
+ Utils as QbUtils,
+} from '@react-awesome-query-builder/antd';
import { WidgetProps } from '@rjsf/utils';
import {
Alert,
@@ -23,19 +31,8 @@ import {
Typography,
} from 'antd';
import classNames from 'classnames';
-import { useEffect } from 'react';
-
-import {
- Actions,
- Builder,
- Config,
- ImmutableTree,
- Query,
- Utils as QbUtils,
-} from '@react-awesome-query-builder/antd';
-import 'antd/dist/antd.css';
import { debounce, isEmpty, isUndefined } from 'lodash';
-import { FC, useCallback, useMemo, useState } from 'react';
+import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { EntityType } from '../../../../../../enums/entity.enum';
import { SearchIndex } from '../../../../../../enums/search.enum';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/TestConnection/TestConnection.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/TestConnection/TestConnection.test.tsx
index 061422086808..5c3088f42542 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/TestConnection/TestConnection.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/TestConnection/TestConnection.test.tsx
@@ -11,12 +11,12 @@
* limitations under the License.
*/
import {
- act,
fireEvent,
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react';
+import { act } from 'react';
import { useAirflowStatus } from '../../../context/AirflowStatusProvider/AirflowStatusProvider';
import { ServiceCategory } from '../../../enums/service.enum';
import { ConfigData } from '../../../interface/service.interface';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/TestConnection/TestConnection.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/TestConnection/TestConnection.tsx
index 34526d993cde..d43e840e96ac 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/TestConnection/TestConnection.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/TestConnection/TestConnection.tsx
@@ -50,7 +50,7 @@ import {
getWorkflowById,
triggerWorkflowById,
} from '../../../rest/workflowAPI';
-import { Transi18next } from '../../../utils/CommonUtils';
+import { Transi18next } from '../../../utils/i18next/LocalUtil';
import { formatFormDataForSubmit } from '../../../utils/JSONSchemaFormUtils';
import {
getServiceType,
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/LoginClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/constants/LoginClassBase.ts
index e1a6d6992fb7..add5ef1bbcc1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/LoginClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/LoginClassBase.ts
@@ -10,6 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
import dataCollaborationImg from '../assets/img/login-screen/data-collaboration/data-collaboration.png';
import discoveryImg from '../assets/img/login-screen/discovery/data-discovery.png';
import governanceImg from '../assets/img/login-screen/governance/governance.png';
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/ServiceType.constant.ts b/openmetadata-ui/src/main/resources/ui/src/constants/ServiceType.constant.ts
new file mode 100644
index 000000000000..c378e8ff53fd
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/ServiceType.constant.ts
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2022 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { map, startCase } from 'lodash';
+import { ServiceTypes, StepperStepType } from 'Models';
+import { EntityType } from '../enums/entity.enum';
+import { ServiceCategory } from '../enums/service.enum';
+import { PipelineType } from '../generated/api/services/ingestionPipelines/createIngestionPipeline';
+import { WorkflowStatus } from '../generated/entity/automations/workflow';
+import { StorageServiceType } from '../generated/entity/data/container';
+import { APIServiceType } from '../generated/entity/services/apiService';
+import { DashboardServiceType } from '../generated/entity/services/dashboardService';
+import { DatabaseServiceType } from '../generated/entity/services/databaseService';
+import { DriveServiceType } from '../generated/entity/services/driveService';
+import { MessagingServiceType } from '../generated/entity/services/messagingService';
+import { MetadataServiceType } from '../generated/entity/services/metadataService';
+import { MlModelServiceType } from '../generated/entity/services/mlmodelService';
+import { PipelineServiceType } from '../generated/entity/services/pipelineService';
+import { SearchServiceType } from '../generated/entity/services/searchService';
+import { Type as SecurityServiceType } from '../generated/entity/services/securityService';
+import { ServiceType } from '../generated/entity/services/serviceType';
+
+export const OPEN_METADATA = 'OpenMetadata';
+export const JWT_CONFIG = 'openMetadataJWTClientConfig';
+
+export const excludedService = [
+ MlModelServiceType.Sklearn,
+ MetadataServiceType.MetadataES,
+ MetadataServiceType.OpenMetadata,
+ PipelineServiceType.Spark,
+];
+
+export const arrServiceTypes: Array = [
+ 'databaseServices',
+ 'messagingServices',
+ 'dashboardServices',
+ 'pipelineServices',
+ 'mlmodelServices',
+ 'storageServices',
+ 'apiServices',
+ 'securityServices',
+ 'driveServices',
+];
+
+export const SERVICE_CATEGORY: { [key: string]: ServiceCategory } = {
+ databases: ServiceCategory.DATABASE_SERVICES,
+ messaging: ServiceCategory.MESSAGING_SERVICES,
+ dashboards: ServiceCategory.DASHBOARD_SERVICES,
+ pipelines: ServiceCategory.PIPELINE_SERVICES,
+ mlmodels: ServiceCategory.ML_MODEL_SERVICES,
+ metadata: ServiceCategory.METADATA_SERVICES,
+ storages: ServiceCategory.STORAGE_SERVICES,
+ search: ServiceCategory.SEARCH_SERVICES,
+ apiServices: ServiceCategory.API_SERVICES,
+ security: ServiceCategory.SECURITY_SERVICES,
+ drives: ServiceCategory.DRIVE_SERVICES,
+};
+
+export const servicesDisplayName: Record<
+ string,
+ { key: string; entity: string }
+> = {
+ databaseServices: { key: 'label.entity-service', entity: 'label.database' },
+ messagingServices: { key: 'label.entity-service', entity: 'label.messaging' },
+ dashboardServices: { key: 'label.entity-service', entity: 'label.dashboard' },
+ pipelineServices: { key: 'label.entity-service', entity: 'label.pipeline' },
+ mlmodelServices: { key: 'label.entity-service', entity: 'label.ml-model' },
+ metadataServices: { key: 'label.entity-service', entity: 'label.metadata' },
+ storageServices: { key: 'label.entity-service', entity: 'label.storage' },
+ searchServices: { key: 'label.entity-service', entity: 'label.search' },
+ dashboardDataModel: {
+ key: 'label.entity-service',
+ entity: 'label.data-model',
+ },
+ apiServices: { key: 'label.entity-service', entity: 'label.api-uppercase' },
+ securityServices: { key: 'label.entity-service', entity: 'label.security' },
+ driveServices: { key: 'label.entity-service', entity: 'label.drive' },
+};
+
+export const SERVICE_CATEGORY_OPTIONS = map(ServiceCategory, (value) => ({
+ label: startCase(value),
+ value,
+}));
+
+export const STEPS_FOR_ADD_SERVICE: Array = [
+ {
+ name: 'label.select-field',
+ nameData: { field: 'label.service-type' },
+ step: 1,
+ },
+ {
+ name: 'label.configure-entity',
+ nameData: { entity: 'label.service' },
+ step: 2,
+ },
+ {
+ name: 'label.connection-entity',
+ nameData: { entity: 'label.detail-plural' },
+ step: 3,
+ },
+ {
+ name: 'label.set-default-filters',
+ step: 4,
+ },
+];
+
+export const STEPS_FOR_EDIT_SERVICE: Array = [
+ {
+ name: 'label.connection-entity',
+ nameData: { entity: 'label.detail-plural' },
+ step: 1,
+ },
+ {
+ name: 'label.set-default-filters',
+ step: 2,
+ },
+];
+
+export const SERVICE_DEFAULT_ERROR_MAP = {
+ serviceType: false,
+};
+
+export const FETCHING_EXPIRY_TIME = 3 * 60 * 1000;
+export const FETCH_INTERVAL = 2000;
+
+export const WORKFLOW_COMPLETE_STATUS = [
+ WorkflowStatus.Failed,
+ WorkflowStatus.Successful,
+];
+
+export const TEST_CONNECTION_PROGRESS_PERCENTAGE = {
+ ZERO: 0,
+ ONE: 1,
+ TEN: 10,
+ TWENTY: 20,
+ FORTY: 40,
+ HUNDRED: 100,
+};
+
+export const SERVICE_TYPE_MAP = {
+ [ServiceCategory.DASHBOARD_SERVICES]: ServiceType.Dashboard,
+ [ServiceCategory.DATABASE_SERVICES]: ServiceType.Database,
+ [ServiceCategory.MESSAGING_SERVICES]: ServiceType.Messaging,
+ [ServiceCategory.ML_MODEL_SERVICES]: ServiceType.MlModel,
+ [ServiceCategory.METADATA_SERVICES]: ServiceType.Metadata,
+ [ServiceCategory.STORAGE_SERVICES]: ServiceType.Storage,
+ [ServiceCategory.PIPELINE_SERVICES]: ServiceType.Pipeline,
+ [ServiceCategory.SEARCH_SERVICES]: ServiceType.Search,
+ [ServiceCategory.API_SERVICES]: ServiceType.API,
+ [ServiceCategory.SECURITY_SERVICES]: ServiceType.Security,
+ [ServiceCategory.DRIVE_SERVICES]: ServiceType.Drive,
+};
+
+export const SERVICE_TYPES_ENUM = {
+ [ServiceCategory.DASHBOARD_SERVICES]: DashboardServiceType,
+ [ServiceCategory.DATABASE_SERVICES]: DatabaseServiceType,
+ [ServiceCategory.MESSAGING_SERVICES]: MessagingServiceType,
+ [ServiceCategory.ML_MODEL_SERVICES]: MlModelServiceType,
+ [ServiceCategory.METADATA_SERVICES]: MetadataServiceType,
+ [ServiceCategory.STORAGE_SERVICES]: StorageServiceType,
+ [ServiceCategory.PIPELINE_SERVICES]: PipelineServiceType,
+ [ServiceCategory.SEARCH_SERVICES]: SearchServiceType,
+ [ServiceCategory.API_SERVICES]: APIServiceType,
+ [ServiceCategory.SECURITY_SERVICES]: SecurityServiceType,
+ [ServiceCategory.DRIVE_SERVICES]: DriveServiceType,
+};
+
+export const BETA_SERVICES = [
+ PipelineServiceType.OpenLineage,
+ PipelineServiceType.Wherescape,
+ DatabaseServiceType.Cassandra,
+ MetadataServiceType.AlationSink,
+ DatabaseServiceType.Cockroach,
+ SearchServiceType.OpenSearch,
+ PipelineServiceType.Ssis,
+ DatabaseServiceType.Ssas,
+ DashboardServiceType.ThoughtSpot,
+ SecurityServiceType.Ranger,
+ DatabaseServiceType.Epic,
+ DashboardServiceType.Grafana,
+ DashboardServiceType.Hex,
+ DatabaseServiceType.ServiceNow,
+ DatabaseServiceType.Timescale,
+ DatabaseServiceType.Dremio,
+ MetadataServiceType.Collibra,
+ PipelineServiceType.Mulesoft,
+ DatabaseServiceType.MicrosoftFabric,
+ PipelineServiceType.MicrosoftFabricPipeline,
+ DatabaseServiceType.BurstIQ,
+ DatabaseServiceType.StarRocks,
+ DriveServiceType.SFTP,
+ DatabaseServiceType.Informix,
+ DatabaseServiceType.MicrosoftAccess,
+];
+
+export const TEST_CONNECTION_INITIAL_MESSAGE =
+ 'message.test-your-connection-before-creating-service';
+
+export const TEST_CONNECTION_SUCCESS_MESSAGE =
+ 'message.connection-test-successful';
+
+export const TEST_CONNECTION_FAILURE_MESSAGE = 'message.connection-test-failed';
+
+export const TEST_CONNECTION_TESTING_MESSAGE =
+ 'message.testing-your-connection-may-take-two-minutes';
+
+export const TEST_CONNECTION_WARNING_MESSAGE =
+ 'message.connection-test-warning';
+
+export const ADVANCED_PROPERTIES = [
+ 'connectionArguments',
+ 'connectionOptions',
+ 'scheme',
+ 'sampleDataStorageConfig',
+ 'computeTableMetrics',
+ 'computeColumnMetrics',
+ 'includeViews',
+ 'useStatistics',
+ 'confidence',
+ 'profileSampleConfig',
+ 'randomizedSample',
+ 'sampleDataCount',
+ 'threadCount',
+ 'timeoutSeconds',
+ 'metrics',
+ 'sslConfig',
+ 'sslMode',
+ 'schemaRegistrySSL',
+ 'consumerConfigSSL',
+ 'verify',
+ 'useNonce',
+ 'disablePkce',
+ 'maxClockSkew',
+ 'tokenValidity',
+ 'maxAge',
+ 'sessionExpiry',
+];
+
+export const PIPELINE_SERVICE_PLATFORM = 'Airflow';
+
+export const SERVICE_TYPES = [
+ EntityType.DATABASE_SERVICE,
+ EntityType.DASHBOARD_SERVICE,
+ EntityType.MESSAGING_SERVICE,
+ EntityType.PIPELINE_SERVICE,
+ EntityType.MLMODEL_SERVICE,
+ EntityType.METADATA_SERVICE,
+ EntityType.STORAGE_SERVICE,
+ EntityType.SEARCH_SERVICE,
+ EntityType.API_SERVICE,
+ EntityType.SECURITY_SERVICE,
+ EntityType.DRIVE_SERVICE,
+];
+
+export const EXCLUDE_AUTO_PILOT_SERVICE_TYPES = [EntityType.SECURITY_SERVICE];
+
+export const SERVICE_INGESTION_PIPELINE_TYPES = [
+ PipelineType.Metadata,
+ PipelineType.Usage,
+ PipelineType.Lineage,
+ PipelineType.Profiler,
+ PipelineType.AutoClassification,
+ PipelineType.Dbt,
+];
+
+export const SERVICE_AUTOPILOT_AGENT_TYPES = [
+ PipelineType.Metadata,
+ PipelineType.Lineage,
+ PipelineType.Usage,
+ PipelineType.AutoClassification,
+ PipelineType.Profiler,
+];
+
+export const SERVICE_TYPE_WITH_DISPLAY_NAME = new Map([
+ [PipelineServiceType.GluePipeline, 'Glue Pipeline'],
+ [DatabaseServiceType.DomoDatabase, 'Domo Database'],
+ [DashboardServiceType.DomoDashboard, 'Domo Dashboard'],
+ [DashboardServiceType.MicroStrategy, 'Micro Strategy'],
+ [DashboardServiceType.PowerBIReportServer, 'PowerBI Report Server'],
+ [PipelineServiceType.DatabricksPipeline, 'Databricks Pipeline'],
+ [PipelineServiceType.DomoPipeline, 'Domo Pipeline'],
+ [PipelineServiceType.KafkaConnect, 'Kafka Connect'],
+ [DatabaseServiceType.SapERP, 'SAP ERP'],
+ [DatabaseServiceType.SapHana, 'SAP HANA'],
+ [DatabaseServiceType.UnityCatalog, 'Unity Catalog'],
+ [PipelineServiceType.DataFactory, 'Data Factory'],
+ [PipelineServiceType.DBTCloud, 'DBT Cloud'],
+ [PipelineServiceType.OpenLineage, 'Open Lineage'],
+ [MetadataServiceType.AlationSink, 'Alation Sink'],
+ [SearchServiceType.ElasticSearch, 'Elasticsearch'],
+ [DatabaseServiceType.MicrosoftFabric, 'Microsoft Fabric'],
+ [PipelineServiceType.MicrosoftFabricPipeline, 'Microsoft Fabric Pipeline'],
+]);
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/ServiceUISchema.constant.ts b/openmetadata-ui/src/main/resources/ui/src/constants/ServiceUISchema.constant.ts
new file mode 100644
index 000000000000..5bc2a5ddfe85
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/ServiceUISchema.constant.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ServiceNestedConnectionFields } from '../enums/service.enum';
+import { SERVICE_FILTER_PATTERN_FIELDS } from './ServiceConnection.constants';
+
+export const DEF_UI_SCHEMA = {
+ supportsIncrementalMetadataExtraction: {
+ 'ui:widget': 'hidden',
+ 'ui:hideError': true,
+ },
+ supportsMetadataExtraction: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ supportsSystemProfile: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ supportsDataDiff: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ supportsUsageExtraction: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ supportsLineageExtraction: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ supportsViewLineageExtraction: {
+ 'ui:widget': 'hidden',
+ 'ui:hideError': true,
+ },
+ supportsProfiler: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ supportsDatabase: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ supportsQueryComment: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ supportsDBTExtraction: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ type: { 'ui:widget': 'hidden' },
+};
+
+export const INGESTION_ELASTIC_SEARCH_WORKFLOW_UI_SCHEMA = {
+ useSSL: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ verifyCerts: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ timeout: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ caCerts: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ useAwsCredentials: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ regionName: { 'ui:widget': 'hidden', 'ui:hideError': true },
+};
+
+export const INGESTION_WORKFLOW_UI_SCHEMA = {
+ type: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ name: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ processingEngine: { 'ui:widget': 'hidden', 'ui:hideError': true },
+ 'ui:order': [
+ 'rootProcessingEngine',
+ 'name',
+ 'displayName',
+ ...SERVICE_FILTER_PATTERN_FIELDS,
+ 'enableDebugLog',
+ '*',
+ ],
+};
+
+export const EXCLUDE_INCREMENTAL_EXTRACTION_SUPPORT_UI_SCHEMA = {
+ incremental: {
+ 'ui:widget': 'hidden',
+ 'ui:hideError': true,
+ },
+};
+
+export const COMMON_UI_SCHEMA = {
+ ...DEF_UI_SCHEMA,
+ [ServiceNestedConnectionFields.CONNECTION]: {
+ ...DEF_UI_SCHEMA,
+ },
+ [ServiceNestedConnectionFields.METASTORE_CONNECTION]: {
+ ...DEF_UI_SCHEMA,
+ },
+ [ServiceNestedConnectionFields.DATABASE_CONNECTION]: {
+ ...DEF_UI_SCHEMA,
+ },
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/Services.constant.ts b/openmetadata-ui/src/main/resources/ui/src/constants/Services.constant.ts
index fc57abb3c710..3feb917bc017 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/Services.constant.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/Services.constant.ts
@@ -11,560 +11,21 @@
* limitations under the License.
*/
-import { map, startCase } from 'lodash';
-import { ServiceTypes, StepperStepType } from 'Models';
-import airbyte from '../assets/img/Airbyte.png';
-import airflow from '../assets/img/service-icon-airflow.png';
-import alationsink from '../assets/img/service-icon-alation-sink.png';
-import amazonS3 from '../assets/img/service-icon-amazon-s3.svg';
-import amundsen from '../assets/img/service-icon-amundsen.png';
-import athena from '../assets/img/service-icon-athena.png';
-import atlas from '../assets/img/service-icon-atlas.svg';
-import azuresql from '../assets/img/service-icon-azuresql.png';
-import bigtable from '../assets/img/service-icon-bigtable.png';
-import burstiq from '../assets/img/service-icon-burstiq.png';
-import cassandra from '../assets/img/service-icon-cassandra.png';
-import clickhouse from '../assets/img/service-icon-clickhouse.png';
-import cockroach from '../assets/img/service-icon-cockroach.png';
-import couchbase from '../assets/img/service-icon-couchbase.svg';
-import dagster from '../assets/img/service-icon-dagster.png';
-import databrick from '../assets/img/service-icon-databrick.png';
-import datalake from '../assets/img/service-icon-datalake.png';
-import dbt from '../assets/img/service-icon-dbt.png';
-import deltalake from '../assets/img/service-icon-delta-lake.png';
-import domo from '../assets/img/service-icon-domo.png';
-import doris from '../assets/img/service-icon-doris.png';
-import druid from '../assets/img/service-icon-druid.png';
-import dynamodb from '../assets/img/service-icon-dynamodb.png';
-import exasol from '../assets/img/service-icon-exasol.png';
-import fivetran from '../assets/img/service-icon-fivetran.png';
-import flink from '../assets/img/service-icon-flink.png';
-import gcs from '../assets/img/service-icon-gcs.png';
-import glue from '../assets/img/service-icon-glue.png';
-import grafana from '../assets/img/service-icon-grafana.png';
-import greenplum from '../assets/img/service-icon-greenplum.png';
-import hive from '../assets/img/service-icon-hive.png';
-import ibmdb2 from '../assets/img/service-icon-ibmdb2.png';
-import impala from '../assets/img/service-icon-impala.png';
-import iomete from '../assets/img/service-icon-iomete.png';
-import kafka from '../assets/img/service-icon-kafka.png';
-import kinesis from '../assets/img/service-icon-kinesis.png';
-import lightDash from '../assets/img/service-icon-lightdash.png';
-import looker from '../assets/img/service-icon-looker.png';
-import mariadb from '../assets/img/service-icon-mariadb.png';
-import metabase from '../assets/img/service-icon-metabase.png';
-import microstrategy from '../assets/img/service-icon-microstrategy.svg';
-import mode from '../assets/img/service-icon-mode.png';
-import mongodb from '../assets/img/service-icon-mongodb.png';
-import mssql from '../assets/img/service-icon-mssql.png';
-import nifi from '../assets/img/service-icon-nifi.png';
-import openlineage from '../assets/img/service-icon-openlineage.svg';
-import oracle from '../assets/img/service-icon-oracle.png';
-import pinot from '../assets/img/service-icon-pinot.png';
-import postgres from '../assets/img/service-icon-post.png';
-import powerbi from '../assets/img/service-icon-power-bi.png';
-import presto from '../assets/img/service-icon-presto.png';
-import qlikSense from '../assets/img/service-icon-qlik-sense.png';
-import query from '../assets/img/service-icon-query.png';
-import quicksight from '../assets/img/service-icon-quicksight.png';
-import redash from '../assets/img/service-icon-redash.png';
-import redpanda from '../assets/img/service-icon-redpanda.png';
-import redshift from '../assets/img/service-icon-redshift.png';
-import sagemaker from '../assets/img/service-icon-sagemaker.png';
-import salesforce from '../assets/img/service-icon-salesforce.png';
-import sapErp from '../assets/img/service-icon-sap-erp.png';
-import sapHana from '../assets/img/service-icon-sap-hana.png';
-import sas from '../assets/img/service-icon-sas.svg';
-import scikit from '../assets/img/service-icon-scikit.png';
-import sigma from '../assets/img/service-icon-sigma.png';
-import singlestore from '../assets/img/service-icon-singlestore.png';
-import snowflakes from '../assets/img/service-icon-snowflakes.png';
-import spark from '../assets/img/service-icon-spark.png';
-import spline from '../assets/img/service-icon-spline.png';
-import mysql from '../assets/img/service-icon-sql.png';
-import sqlite from '../assets/img/service-icon-sqlite.png';
-import starrocks from '../assets/img/service-icon-starrocks.png';
-import superset from '../assets/img/service-icon-superset.png';
-import synapse from '../assets/img/service-icon-synapse.png';
-import tableau from '../assets/img/service-icon-tableau.png';
-import timescale from '../assets/img/service-icon-timescale.png';
-import trino from '../assets/img/service-icon-trino.png';
-import unitycatalog from '../assets/img/service-icon-unitycatalog.svg';
-import vertica from '../assets/img/service-icon-vertica.png';
-import dashboardDefault from '../assets/svg/dashboard.svg';
-import iconDefaultService from '../assets/svg/default-service-icon.svg';
-import elasticSearch from '../assets/svg/elasticsearch.svg';
-import databaseDefault from '../assets/svg/ic-custom-database.svg';
-import { default as customDriveDefault } from '../assets/svg/ic-custom-drive.svg';
-import mlModelDefault from '../assets/svg/ic-custom-model.svg';
-import searchDefault from '../assets/svg/ic-custom-search.svg';
-import { default as storageDefault } from '../assets/svg/ic-custom-storage.svg';
-import { default as driveDefault } from '../assets/svg/ic-drive-service.svg';
-import restService from '../assets/svg/ic-service-rest-api.svg';
-import logo from '../assets/svg/logo-monogram.svg';
-import openSearch from '../assets/svg/open-search.svg';
-import pipelineDefault from '../assets/svg/pipeline.svg';
-import securitySafe from '../assets/svg/security-safe.svg';
-import googleDrive from '../assets/svg/service-icon-google-drive.svg';
-import hex from '../assets/svg/service-icon-hex.svg';
-import mlflow from '../assets/svg/service-icon-mlflow.svg';
-import pubsub from '../assets/svg/service-icon-pubsub.svg';
-import sftp from '../assets/svg/service-icon-sftp.svg';
-import teradata from '../assets/svg/teradata.svg';
-import topicDefault from '../assets/svg/topic.svg';
-import { EntityType } from '../enums/entity.enum';
-import {
- ServiceCategory,
- ServiceNestedConnectionFields,
-} from '../enums/service.enum';
-import { PipelineType } from '../generated/api/services/ingestionPipelines/createIngestionPipeline';
-import { WorkflowStatus } from '../generated/entity/automations/workflow';
-import { StorageServiceType } from '../generated/entity/data/container';
-import { APIServiceType } from '../generated/entity/services/apiService';
-import { DashboardServiceType } from '../generated/entity/services/dashboardService';
-import { DatabaseServiceType } from '../generated/entity/services/databaseService';
-import { DriveServiceType } from '../generated/entity/services/driveService';
-import { MessagingServiceType } from '../generated/entity/services/messagingService';
-import { MetadataServiceType } from '../generated/entity/services/metadataService';
-import { MlModelServiceType } from '../generated/entity/services/mlmodelService';
-import { PipelineServiceType } from '../generated/entity/services/pipelineService';
-import { SearchServiceType } from '../generated/entity/services/searchService';
-import { Type as SecurityServiceType } from '../generated/entity/services/securityService';
-import { ServiceType } from '../generated/entity/services/serviceType';
-import { SERVICE_FILTER_PATTERN_FIELDS } from './ServiceConnection.constants';
-
-export const MYSQL = mysql;
-export const SQLITE = sqlite;
-export const MSSQL = mssql;
-export const REDSHIFT = redshift;
-export const BIGQUERY = query;
-export const BIGTABLE = bigtable;
-export const HEX = hex;
-export const HIVE = hive;
-export const IMPALA = impala;
-export const POSTGRES = postgres;
-export const ORACLE = oracle;
-export const SNOWFLAKE = snowflakes;
-export const ATHENA = athena;
-export const PRESTO = presto;
-export const TRINO = trino;
-export const GLUE = glue;
-export const MARIADB = mariadb;
-export const VERTICA = vertica;
-export const KAFKA = kafka;
-export const PUBSUB = pubsub;
-export const REDPANDA = redpanda;
-export const SUPERSET = superset;
-export const SYNAPSE = synapse;
-export const LOOKER = looker;
-export const MICROSTRATEGY = microstrategy;
-export const TABLEAU = tableau;
-export const REDASH = redash;
-export const METABASE = metabase;
-export const AZURESQL = azuresql;
-export const CLICKHOUSE = clickhouse;
-export const DATABRICK = databrick;
-export const UNITYCATALOG = unitycatalog;
-export const IBMDB2 = ibmdb2;
-export const DORIS = doris;
-export const STARROCKS = starrocks;
-export const DRUID = druid;
-export const DYNAMODB = dynamodb;
-export const SIGMA = sigma;
-export const SINGLESTORE = singlestore;
-export const SALESFORCE = salesforce;
-export const MLFLOW = mlflow;
-export const SAP_HANA = sapHana;
-export const SAP_ERP = sapErp;
-export const SCIKIT = scikit;
-export const DELTALAKE = deltalake;
-export const DEFAULT_SERVICE = iconDefaultService;
-export const AIRBYTE = airbyte;
-export const PINOT = pinot;
-export const DATALAKE = datalake;
-export const MODE = mode;
-export const DAGSTER = dagster;
-export const DBT = dbt;
-export const FIVETRAN = fivetran;
-export const AMUNDSEN = amundsen;
-export const ATLAS = atlas;
-export const ALATIONSINK = alationsink;
-export const SAS = sas;
-export const OPENLINEAGE = openlineage;
-export const LOGO = logo;
-export const EXASOL = exasol;
-export const AIRFLOW = airflow;
-export const POWERBI = powerbi;
-export const DATABASE_DEFAULT = databaseDefault;
-export const TOPIC_DEFAULT = topicDefault;
-export const DASHBOARD_DEFAULT = dashboardDefault;
-export const PIPELINE_DEFAULT = pipelineDefault;
-export const ML_MODEL_DEFAULT = mlModelDefault;
-export const CUSTOM_STORAGE_DEFAULT = storageDefault;
-export const CUSTOM_DRIVE_DEFAULT = customDriveDefault;
-export const DRIVE_DEFAULT = driveDefault;
-export const NIFI = nifi;
-export const KINESIS = kinesis;
-export const QUICKSIGHT = quicksight;
-export const DOMO = domo;
-export const SAGEMAKER = sagemaker;
-export const AMAZON_S3 = amazonS3;
-export const GCS = gcs;
-export const SPARK = spark;
-export const SPLINE = spline;
-export const MONGODB = mongodb;
-export const CASSANDRA = cassandra;
-export const QLIK_SENSE = qlikSense;
-export const LIGHT_DASH = lightDash;
-export const COUCHBASE = couchbase;
-export const GREENPLUM = greenplum;
-export const ELASTIC_SEARCH = elasticSearch;
-export const OPEN_SEARCH = openSearch;
-export const CUSTOM_SEARCH_DEFAULT = searchDefault;
-export const TERADATA = teradata;
-export const FLINK = flink;
-export const REST_SERVICE = restService;
-export const COCKROACH = cockroach;
-export const SECURITY_DEFAULT = securitySafe;
-export const GRAFANA = grafana;
-export const GOOGLE_DRIVE = googleDrive;
-export const SFTP = sftp;
-export const TIMESCALE = timescale;
-export const BURSTIQ = burstiq;
-export const IOMETE = iomete;
-export const excludedService = [
- MlModelServiceType.Sklearn,
- MetadataServiceType.MetadataES,
- MetadataServiceType.OpenMetadata,
- PipelineServiceType.Spark,
-];
-
-export const arrServiceTypes: Array = [
- 'databaseServices',
- 'messagingServices',
- 'dashboardServices',
- 'pipelineServices',
- 'mlmodelServices',
- 'storageServices',
- 'apiServices',
- 'securityServices',
- 'driveServices',
-];
-
-export const SERVICE_CATEGORY: { [key: string]: ServiceCategory } = {
- databases: ServiceCategory.DATABASE_SERVICES,
- messaging: ServiceCategory.MESSAGING_SERVICES,
- dashboards: ServiceCategory.DASHBOARD_SERVICES,
- pipelines: ServiceCategory.PIPELINE_SERVICES,
- mlmodels: ServiceCategory.ML_MODEL_SERVICES,
- metadata: ServiceCategory.METADATA_SERVICES,
- storages: ServiceCategory.STORAGE_SERVICES,
- search: ServiceCategory.SEARCH_SERVICES,
- apiServices: ServiceCategory.API_SERVICES,
- security: ServiceCategory.SECURITY_SERVICES,
- drives: ServiceCategory.DRIVE_SERVICES,
-};
-
-export const servicesDisplayName: Record<
- string,
- { key: string; entity: string }
-> = {
- databaseServices: { key: 'label.entity-service', entity: 'label.database' },
- messagingServices: { key: 'label.entity-service', entity: 'label.messaging' },
- dashboardServices: { key: 'label.entity-service', entity: 'label.dashboard' },
- pipelineServices: { key: 'label.entity-service', entity: 'label.pipeline' },
- mlmodelServices: { key: 'label.entity-service', entity: 'label.ml-model' },
- metadataServices: { key: 'label.entity-service', entity: 'label.metadata' },
- storageServices: { key: 'label.entity-service', entity: 'label.storage' },
- searchServices: { key: 'label.entity-service', entity: 'label.search' },
- dashboardDataModel: {
- key: 'label.entity-service',
- entity: 'label.data-model',
- },
- apiServices: { key: 'label.entity-service', entity: 'label.api-uppercase' },
- securityServices: { key: 'label.entity-service', entity: 'label.security' },
- driveServices: { key: 'label.entity-service', entity: 'label.drive' },
-};
-
-export const DEF_UI_SCHEMA = {
- supportsIncrementalMetadataExtraction: {
- 'ui:widget': 'hidden',
- 'ui:hideError': true,
- },
- supportsMetadataExtraction: { 'ui:widget': 'hidden', 'ui:hideError': true },
- supportsSystemProfile: { 'ui:widget': 'hidden', 'ui:hideError': true },
- supportsDataDiff: { 'ui:widget': 'hidden', 'ui:hideError': true },
- supportsUsageExtraction: { 'ui:widget': 'hidden', 'ui:hideError': true },
- supportsLineageExtraction: { 'ui:widget': 'hidden', 'ui:hideError': true },
- supportsViewLineageExtraction: {
- 'ui:widget': 'hidden',
- 'ui:hideError': true,
- },
- supportsProfiler: { 'ui:widget': 'hidden', 'ui:hideError': true },
- supportsDatabase: { 'ui:widget': 'hidden', 'ui:hideError': true },
- supportsQueryComment: { 'ui:widget': 'hidden', 'ui:hideError': true },
- supportsDBTExtraction: { 'ui:widget': 'hidden', 'ui:hideError': true },
- type: { 'ui:widget': 'hidden' },
-};
-
-export const INGESTION_ELASTIC_SEARCH_WORKFLOW_UI_SCHEMA = {
- useSSL: { 'ui:widget': 'hidden', 'ui:hideError': true },
- verifyCerts: { 'ui:widget': 'hidden', 'ui:hideError': true },
- timeout: { 'ui:widget': 'hidden', 'ui:hideError': true },
- caCerts: { 'ui:widget': 'hidden', 'ui:hideError': true },
- useAwsCredentials: { 'ui:widget': 'hidden', 'ui:hideError': true },
- regionName: { 'ui:widget': 'hidden', 'ui:hideError': true },
-};
-
-export const INGESTION_WORKFLOW_UI_SCHEMA = {
- type: { 'ui:widget': 'hidden', 'ui:hideError': true },
- name: { 'ui:widget': 'hidden', 'ui:hideError': true },
- processingEngine: { 'ui:widget': 'hidden', 'ui:hideError': true },
- 'ui:order': [
- 'rootProcessingEngine',
- 'name',
- 'displayName',
- ...SERVICE_FILTER_PATTERN_FIELDS,
- 'enableDebugLog',
- '*',
- ],
-};
-
-export const EXCLUDE_INCREMENTAL_EXTRACTION_SUPPORT_UI_SCHEMA = {
- incremental: {
- 'ui:widget': 'hidden',
- 'ui:hideError': true,
- },
-};
-
-export const COMMON_UI_SCHEMA = {
- ...DEF_UI_SCHEMA,
- [ServiceNestedConnectionFields.CONNECTION]: {
- ...DEF_UI_SCHEMA,
- },
- [ServiceNestedConnectionFields.METASTORE_CONNECTION]: {
- ...DEF_UI_SCHEMA,
- },
- [ServiceNestedConnectionFields.DATABASE_CONNECTION]: {
- ...DEF_UI_SCHEMA,
- },
-};
-
-export const OPEN_METADATA = 'OpenMetadata';
-export const JWT_CONFIG = 'openMetadataJWTClientConfig';
-
-export const SERVICE_CATEGORY_OPTIONS = map(ServiceCategory, (value) => ({
- label: startCase(value),
- value,
-}));
-
-export const STEPS_FOR_ADD_SERVICE: Array = [
- {
- name: 'label.select-field',
- nameData: { field: 'label.service-type' },
- step: 1,
- },
- {
- name: 'label.configure-entity',
- nameData: { entity: 'label.service' },
- step: 2,
- },
- {
- name: 'label.connection-entity',
- nameData: { entity: 'label.detail-plural' },
- step: 3,
- },
- {
- name: 'label.set-default-filters',
- step: 4,
- },
-];
-
-export const STEPS_FOR_EDIT_SERVICE: Array = [
- {
- name: 'label.connection-entity',
- nameData: { entity: 'label.detail-plural' },
- step: 1,
- },
- {
- name: 'label.set-default-filters',
- step: 2,
- },
-];
-
-export const SERVICE_DEFAULT_ERROR_MAP = {
- serviceType: false,
-};
-// 3 minutes timeout to wait for test connection status
-// Increasing it temporarily while we investigate test connection delays
-// @pmbrull
-export const FETCHING_EXPIRY_TIME = 3 * 60 * 1000;
-export const FETCH_INTERVAL = 2000;
-export const WORKFLOW_COMPLETE_STATUS = [
- WorkflowStatus.Failed,
- WorkflowStatus.Successful,
-];
-export const TEST_CONNECTION_PROGRESS_PERCENTAGE = {
- ZERO: 0,
- ONE: 1,
- TEN: 10,
- TWENTY: 20,
- FORTY: 40,
- HUNDRED: 100,
-};
-
-export const SERVICE_TYPE_MAP = {
- [ServiceCategory.DASHBOARD_SERVICES]: ServiceType.Dashboard,
- [ServiceCategory.DATABASE_SERVICES]: ServiceType.Database,
- [ServiceCategory.MESSAGING_SERVICES]: ServiceType.Messaging,
- [ServiceCategory.ML_MODEL_SERVICES]: ServiceType.MlModel,
- [ServiceCategory.METADATA_SERVICES]: ServiceType.Metadata,
- [ServiceCategory.STORAGE_SERVICES]: ServiceType.Storage,
- [ServiceCategory.PIPELINE_SERVICES]: ServiceType.Pipeline,
- [ServiceCategory.SEARCH_SERVICES]: ServiceType.Search,
- [ServiceCategory.API_SERVICES]: ServiceType.API,
- [ServiceCategory.SECURITY_SERVICES]: ServiceType.Security,
- [ServiceCategory.DRIVE_SERVICES]: ServiceType.Drive,
-};
-
-export const SERVICE_TYPES_ENUM = {
- [ServiceCategory.DASHBOARD_SERVICES]: DashboardServiceType,
- [ServiceCategory.DATABASE_SERVICES]: DatabaseServiceType,
- [ServiceCategory.MESSAGING_SERVICES]: MessagingServiceType,
- [ServiceCategory.ML_MODEL_SERVICES]: MlModelServiceType,
- [ServiceCategory.METADATA_SERVICES]: MetadataServiceType,
- [ServiceCategory.STORAGE_SERVICES]: StorageServiceType,
- [ServiceCategory.PIPELINE_SERVICES]: PipelineServiceType,
- [ServiceCategory.SEARCH_SERVICES]: SearchServiceType,
- [ServiceCategory.API_SERVICES]: APIServiceType,
- [ServiceCategory.SECURITY_SERVICES]: SecurityServiceType,
- [ServiceCategory.DRIVE_SERVICES]: DriveServiceType,
-};
-
-export const BETA_SERVICES = [
- PipelineServiceType.OpenLineage,
- PipelineServiceType.Wherescape,
- DatabaseServiceType.Cassandra,
- MetadataServiceType.AlationSink,
- DatabaseServiceType.Cockroach,
- SearchServiceType.OpenSearch,
- PipelineServiceType.Ssis,
- DatabaseServiceType.Ssas,
- DashboardServiceType.ThoughtSpot,
- SecurityServiceType.Ranger,
- DatabaseServiceType.Epic,
- DashboardServiceType.Grafana,
- DashboardServiceType.Hex,
- DatabaseServiceType.ServiceNow,
- DatabaseServiceType.Timescale,
- DatabaseServiceType.Dremio,
- MetadataServiceType.Collibra,
- PipelineServiceType.Mulesoft,
- DatabaseServiceType.MicrosoftFabric,
- PipelineServiceType.MicrosoftFabricPipeline,
- DatabaseServiceType.BurstIQ,
- DatabaseServiceType.StarRocks,
- DriveServiceType.SFTP,
- DatabaseServiceType.Informix,
- DatabaseServiceType.MicrosoftAccess,
- DatabaseServiceType.Iomete,
-];
-
-export const TEST_CONNECTION_INITIAL_MESSAGE =
- 'message.test-your-connection-before-creating-service';
-
-export const TEST_CONNECTION_SUCCESS_MESSAGE =
- 'message.connection-test-successful';
-
-export const TEST_CONNECTION_FAILURE_MESSAGE = 'message.connection-test-failed';
-
-export const TEST_CONNECTION_TESTING_MESSAGE =
- 'message.testing-your-connection-may-take-two-minutes';
-
-export const TEST_CONNECTION_WARNING_MESSAGE =
- 'message.connection-test-warning';
-
-export const ADVANCED_PROPERTIES = [
- 'connectionArguments',
- 'connectionOptions',
- 'scheme',
- 'sampleDataStorageConfig',
- 'computeTableMetrics',
- 'computeColumnMetrics',
- 'includeViews',
- 'useStatistics',
- 'confidence',
- 'profileSampleConfig',
- 'randomizedSample',
- 'sampleDataCount',
- 'threadCount',
- 'timeoutSeconds',
- 'metrics',
- 'sslConfig',
- 'sslMode',
- 'schemaRegistrySSL',
- 'consumerConfigSSL',
- 'verify',
- 'useNonce',
- 'disablePkce',
- 'maxClockSkew',
- 'tokenValidity',
- 'maxAge',
- 'sessionExpiry',
-];
-
-export const PIPELINE_SERVICE_PLATFORM = 'Airflow';
-
-export const SERVICE_TYPES = [
- EntityType.DATABASE_SERVICE,
- EntityType.DASHBOARD_SERVICE,
- EntityType.MESSAGING_SERVICE,
- EntityType.PIPELINE_SERVICE,
- EntityType.MLMODEL_SERVICE,
- EntityType.METADATA_SERVICE,
- EntityType.STORAGE_SERVICE,
- EntityType.SEARCH_SERVICE,
- EntityType.API_SERVICE,
- EntityType.SECURITY_SERVICE,
- EntityType.DRIVE_SERVICE,
-];
-
-export const EXCLUDE_AUTO_PILOT_SERVICE_TYPES = [EntityType.SECURITY_SERVICE];
-
-export const SERVICE_INGESTION_PIPELINE_TYPES = [
- PipelineType.Metadata,
- PipelineType.Usage,
- PipelineType.Lineage,
- PipelineType.Profiler,
- PipelineType.AutoClassification,
- PipelineType.Dbt,
-];
-
-export const SERVICE_AUTOPILOT_AGENT_TYPES = [
- PipelineType.Metadata,
- PipelineType.Lineage,
- PipelineType.Usage,
- PipelineType.AutoClassification,
- PipelineType.Profiler,
-];
+/**
+ * @deprecated This file previously contained 93 eager image imports that bloated the bundle.
+ *
+ * All exports have been moved to modular files. Update your imports:
+ *
+ * UI Schemas:
+ * - import { COMMON_UI_SCHEMA, DEF_UI_SCHEMA } from './ServiceUISchema.constant';
+ *
+ * Service Types & Constants:
+ * - import { SERVICE_TYPES, BETA_SERVICES } from './ServiceType.constant';
+ *
+ * Service Icons (lazy-loaded):
+ * - import { getServiceIcon } from '../utils/ServiceIconUtils';
+ * - const icon = await getServiceIcon('mysql');
+ */
-export const SERVICE_TYPE_WITH_DISPLAY_NAME = new Map([
- [PipelineServiceType.GluePipeline, 'Glue Pipeline'],
- [DatabaseServiceType.DomoDatabase, 'Domo Database'],
- [DashboardServiceType.DomoDashboard, 'Domo Dashboard'],
- [DashboardServiceType.MicroStrategy, 'Micro Strategy'],
- [DashboardServiceType.PowerBIReportServer, 'PowerBI Report Server'],
- [PipelineServiceType.DatabricksPipeline, 'Databricks Pipeline'],
- [PipelineServiceType.DomoPipeline, 'Domo Pipeline'],
- [PipelineServiceType.KafkaConnect, 'Kafka Connect'],
- [DatabaseServiceType.SapERP, 'SAP ERP'],
- [DatabaseServiceType.SapHana, 'SAP HANA'],
- [DatabaseServiceType.UnityCatalog, 'Unity Catalog'],
- [PipelineServiceType.DataFactory, 'Data Factory'],
- [PipelineServiceType.DBTCloud, 'DBT Cloud'],
- [PipelineServiceType.OpenLineage, 'Open Lineage'],
- [MetadataServiceType.AlationSink, 'Alation Sink'],
- [SearchServiceType.ElasticSearch, 'Elasticsearch'],
- [DatabaseServiceType.MicrosoftFabric, 'Microsoft Fabric'],
- [PipelineServiceType.MicrosoftFabricPipeline, 'Microsoft Fabric Pipeline'],
-]);
+export * from './ServiceType.constant';
+export * from './ServiceUISchema.constant';
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/TestSuite.constant.ts b/openmetadata-ui/src/main/resources/ui/src/constants/TestSuite.constant.ts
index 3ddb488d7825..4d3cfae42d13 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/TestSuite.constant.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/TestSuite.constant.ts
@@ -11,15 +11,14 @@
* limitations under the License.
*/
-import i18next from 'i18next';
import { StepperStepType } from 'Models';
import { TestCaseResolutionStatusTypes } from '../generated/tests/testCaseResolutionStatus';
import { DataQualityPageTabs } from '../pages/DataQuality/DataQualityPage.interface';
import i18n from '../utils/i18next/LocalUtil';
-const TEST_SUITE_LABEL = i18next.t('label.test-suite');
-const ADD_TEST_SUITE_LABEL = i18next.t('label.add-entity', {
- entity: i18next.t('label.test-suite'),
+const TEST_SUITE_LABEL = i18n.t('label.test-suite');
+const ADD_TEST_SUITE_LABEL = i18n.t('label.add-entity', {
+ entity: i18n.t('label.test-suite'),
});
export const STEPS_FOR_ADD_TEST_SUITE: Array = [
@@ -28,13 +27,13 @@ export const STEPS_FOR_ADD_TEST_SUITE: Array = [
step: 1,
},
{
- name: i18next.t('label.add-entity', {
- entity: i18next.t('label.test-case'),
+ name: i18n.t('label.add-entity', {
+ entity: i18n.t('label.test-case'),
}),
step: 2,
},
{
- name: i18next.t('label.test-suite-status'),
+ name: i18n.t('label.test-suite-status'),
step: 3,
},
];
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts
index 83e465511b2d..3fcb09c38b43 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts
@@ -82,19 +82,10 @@ export const LAST_VERSION_FETCH_TIME_KEY = 'versionFetchTime';
export const LOCALSTORAGE_RECENTLY_VIEWED = `recentlyViewedData`;
export const LOCALSTORAGE_RECENTLY_SEARCHED = `recentlySearchedData`;
export const VERSION = 'VERSION';
-export const REDIRECT_PATHNAME = 'redirectUrlPath';
export const TERM_ADMIN = 'Admin';
export const TERM_USER = 'User';
export const DISABLED = 'disabled';
-export const imageTypes = {
- image: 's96-c',
- image192: 's192-c',
- image24: 's24-c',
- image32: 's32-c',
- image48: 's48-c',
- image512: 's512-c',
- image72: 's72-c',
-};
+
export const NO_DATA_PLACEHOLDER = '--';
export const PIPE_SYMBOL = '|';
export const NO_DATA = '-';
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/router.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/router.constants.ts
new file mode 100644
index 000000000000..75a501588f70
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/router.constants.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const APP_ROUTER_ROUTES = {
+ HOME: '/',
+ MY_DATA: '/my-data',
+ NOT_FOUND: '/404',
+ LOGOUT: '/logout',
+ UNAUTHORISED: '/unauthorised',
+ SIGNUP: '/signup',
+ AUTH_CALLBACK: '/auth/callback',
+ SIGNIN: '/signin',
+ FORGOT_PASSWORD: '/forgot-password',
+ CALLBACK: '/callback',
+ SILENT_CALLBACK: '/silent-callback',
+ REGISTER: '/register',
+ RESET_PASSWORD: '/users/password/reset',
+ ACCOUNT_ACTIVATION: '/users/registrationConfirmation',
+} as const;
+
+export const UNPROTECTED_ROUTES: Set = new Set([
+ APP_ROUTER_ROUTES.SIGNUP,
+ APP_ROUTER_ROUTES.SIGNIN,
+ APP_ROUTER_ROUTES.FORGOT_PASSWORD,
+ APP_ROUTER_ROUTES.CALLBACK,
+ APP_ROUTER_ROUTES.SILENT_CALLBACK,
+ APP_ROUTER_ROUTES.REGISTER,
+ APP_ROUTER_ROUTES.RESET_PASSWORD,
+ APP_ROUTER_ROUTES.ACCOUNT_ACTIVATION,
+ APP_ROUTER_ROUTES.HOME,
+ APP_ROUTER_ROUTES.AUTH_CALLBACK,
+ APP_ROUTER_ROUTES.NOT_FOUND,
+ APP_ROUTER_ROUTES.LOGOUT,
+]);
+
+export const REDIRECT_PATHNAME = 'redirectUrlPath';
diff --git a/openmetadata-ui/src/main/resources/ui/src/context/AntDConfigProvider/AntDConfigProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/context/AntDConfigProvider/AntDConfigProvider.tsx
index b42bb48be7a4..b976ac2644a1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/context/AntDConfigProvider/AntDConfigProvider.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/context/AntDConfigProvider/AntDConfigProvider.tsx
@@ -19,7 +19,9 @@ import { generatePalette } from '../../styles/colorPallet';
const AntDConfigProvider: FC<{ children: ReactNode }> = ({ children }) => {
const { i18n } = useTranslation();
- const { applicationConfig } = useApplicationStore();
+ const applicationConfig = useApplicationStore(
+ (state) => state.applicationConfig
+ );
useEffect(() => {
const palette = generatePalette(
diff --git a/openmetadata-ui/src/main/resources/ui/src/context/PermissionProvider/PermissionProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/context/PermissionProvider/PermissionProvider.tsx
index e72e99960193..52aeb541ab87 100644
--- a/openmetadata-ui/src/main/resources/ui/src/context/PermissionProvider/PermissionProvider.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/context/PermissionProvider/PermissionProvider.tsx
@@ -24,20 +24,19 @@ import {
} from 'react';
import { useNavigate } from 'react-router-dom';
import Loader from '../../components/common/Loader/Loader';
-import { REDIRECT_PATHNAME } from '../../constants/constants';
+import { REDIRECT_PATHNAME } from '../../constants/router.constants';
+import { useApplicationStore } from '../../hooks/useApplicationStore';
import {
getEntityPermissionByFqn,
getEntityPermissionById,
getLoggedInUserPermissions,
getResourcePermission,
} from '../../rest/permissionAPI';
+import { setUrlPathnameExpiryAfterRoute } from '../../utils/AuthProvider.util';
import {
getOperationPermissions,
getUIPermission,
} from '../../utils/PermissionsUtils';
-
-import { useApplicationStore } from '../../hooks/useApplicationStore';
-import { setUrlPathnameExpiryAfterRoute } from '../../utils/AuthProvider.util';
import {
EntityPermissionMap,
PermissionContextType,
diff --git a/openmetadata-ui/src/main/resources/ui/src/hoc/withDomainFilter.test.tsx b/openmetadata-ui/src/main/resources/ui/src/hoc/withDomainFilter.test.tsx
new file mode 100644
index 000000000000..b31cde692332
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/hoc/withDomainFilter.test.tsx
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2026 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { InternalAxiosRequestConfig } from 'axios';
+import { DEFAULT_DOMAIN_VALUE } from '../constants/constants';
+import { SearchIndex } from '../enums/search.enum';
+import { useDomainStore } from '../hooks/useDomainStore';
+import { getPathNameFromWindowLocation } from '../utils/LocationUtils';
+import { withDomainFilter } from './withDomainFilter';
+
+jest.mock('../hooks/useDomainStore');
+jest.mock('../utils/LocationUtils', () => ({
+ getPathNameFromWindowLocation: jest.fn(),
+}));
+
+describe('withDomainFilter', () => {
+ const mockGetState = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ (useDomainStore as unknown as jest.Mock).mockImplementation(() => ({
+ getState: mockGetState,
+ }));
+ (useDomainStore.getState as jest.Mock) = mockGetState;
+ });
+
+ const createMockConfig = (
+ method: string = 'get',
+ url?: string,
+ params?: Record
+ ): InternalAxiosRequestConfig =>
+ ({
+ method,
+ url,
+ params,
+ headers: {},
+ } as InternalAxiosRequestConfig);
+
+ describe('should not intercept requests', () => {
+ it('should return config unchanged when path starts with /domain', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockImplementationOnce(
+ () => '/domain/test'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'testDomain' });
+
+ const config = createMockConfig();
+ const result = withDomainFilter(config);
+
+ expect(result).toBe(config);
+ expect(result.params).toBeUndefined();
+ });
+
+ it('should return config unchanged when path starts with /auth/logout', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/auth/logout'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'testDomain' });
+
+ const config = createMockConfig();
+ const result = withDomainFilter(config);
+
+ expect(result).toBe(config);
+ expect(result.params).toBeUndefined();
+ });
+
+ it('should return config unchanged when path starts with /auth/refresh', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/auth/refresh'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'testDomain' });
+
+ const config = createMockConfig();
+ const result = withDomainFilter(config);
+
+ expect(result).toBe(config);
+ expect(result.params).toBeUndefined();
+ });
+
+ it('should return config unchanged when method is not GET', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/test'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'testDomain' });
+
+ const config = createMockConfig('post');
+ const result = withDomainFilter(config);
+
+ expect(result).toBe(config);
+ expect(result.params).toBeUndefined();
+ });
+
+ it('should return config unchanged when activeDomain is DEFAULT_DOMAIN_VALUE', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/test'
+ );
+ mockGetState.mockReturnValue({ activeDomain: DEFAULT_DOMAIN_VALUE });
+
+ const config = createMockConfig();
+ const result = withDomainFilter(config);
+
+ expect(result).toBe(config);
+ expect(result.params).toBeUndefined();
+ });
+ });
+
+ describe('regular GET requests', () => {
+ it('should add domain parameter for regular GET requests with active domain', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/tables'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const config = createMockConfig('get', '/api/tables');
+ const result = withDomainFilter(config);
+
+ expect(result.params).toEqual({
+ domain: 'engineering',
+ });
+ });
+
+ it('should preserve existing params when adding domain parameter', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/tables'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const config = createMockConfig('get', '/api/tables', {
+ limit: 10,
+ offset: 0,
+ });
+ const result = withDomainFilter(config);
+
+ expect(result.params).toEqual({
+ limit: 10,
+ offset: 0,
+ domain: 'engineering',
+ });
+ });
+ });
+
+ describe('search query requests', () => {
+ it('should add should filter with term and prefix for /search/query with active domain', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TABLE,
+ });
+ const result = withDomainFilter(config);
+
+ expect(result.params).toHaveProperty('query_filter');
+
+ const filter = JSON.parse(result.params?.query_filter as string);
+
+ expect(filter).toEqual({
+ query: {
+ bool: {
+ must: [
+ {
+ bool: {
+ should: [
+ {
+ term: {
+ 'domains.fullyQualifiedName': 'engineering',
+ },
+ },
+ {
+ prefix: {
+ 'domains.fullyQualifiedName': 'engineering.',
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ });
+ });
+
+ it('should return config unchanged for TAG index searches', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TAG,
+ });
+ const result = withDomainFilter(config);
+
+ expect(result).toBe(config);
+ expect(result.params?.query_filter).toBeUndefined();
+ });
+
+ it('should use fullyQualifiedName field for DOMAIN index searches', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.DOMAIN,
+ });
+ const result = withDomainFilter(config);
+ const queryFilter = JSON.parse(result.params?.query_filter as string);
+ const shouldClauses =
+ queryFilter.query.bool.must[queryFilter.query.bool.must.length - 1].bool
+ .should;
+
+ expect(shouldClauses).toEqual([
+ { term: { fullyQualifiedName: 'engineering' } },
+ { prefix: { fullyQualifiedName: 'engineering.' } },
+ ]);
+ });
+
+ it('should preserve existing query_filter and add should filter', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const existingFilter = {
+ query: {
+ bool: {
+ must: [
+ {
+ term: {
+ entityType: 'table',
+ },
+ },
+ ],
+ },
+ },
+ };
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TABLE,
+ query_filter: JSON.stringify(existingFilter),
+ });
+ const result = withDomainFilter(config);
+
+ const filter = JSON.parse(result.params?.query_filter as string);
+
+ expect(filter.query.bool.must).toHaveLength(2);
+ expect(filter.query.bool.must[0]).toEqual({
+ term: {
+ entityType: 'table',
+ },
+ });
+ expect(filter.query.bool.must[1]).toEqual({
+ bool: {
+ should: [
+ {
+ term: {
+ 'domains.fullyQualifiedName': 'engineering',
+ },
+ },
+ {
+ prefix: {
+ 'domains.fullyQualifiedName': 'engineering.',
+ },
+ },
+ ],
+ },
+ });
+ });
+
+ it('should handle invalid JSON in query_filter gracefully', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TABLE,
+ query_filter: 'invalid-json',
+ });
+ const result = withDomainFilter(config);
+
+ const filter = JSON.parse(result.params?.query_filter as string);
+
+ expect(filter).toEqual({
+ query: {
+ bool: {
+ must: [
+ {
+ bool: {
+ should: [
+ {
+ term: {
+ 'domains.fullyQualifiedName': 'engineering',
+ },
+ },
+ {
+ prefix: {
+ 'domains.fullyQualifiedName': 'engineering.',
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ });
+ });
+
+ it('should handle query_filter with empty must array', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const existingFilter = {
+ query: {
+ bool: {},
+ },
+ };
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TABLE,
+ query_filter: JSON.stringify(existingFilter),
+ });
+ const result = withDomainFilter(config);
+
+ const filter = JSON.parse(result.params?.query_filter as string);
+
+ expect(filter.query.bool.must).toHaveLength(1);
+ expect(filter.query.bool.must[0]).toEqual({
+ bool: {
+ should: [
+ {
+ term: {
+ 'domains.fullyQualifiedName': 'engineering',
+ },
+ },
+ {
+ prefix: {
+ 'domains.fullyQualifiedName': 'engineering.',
+ },
+ },
+ ],
+ },
+ });
+ });
+
+ it('should handle empty object query_filter gracefully', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TABLE,
+ query_filter: '{}',
+ });
+ const result = withDomainFilter(config);
+
+ const filter = JSON.parse(result.params?.query_filter as string);
+
+ expect(filter).toEqual({
+ query: {
+ bool: {
+ must: [
+ {
+ bool: {
+ should: [
+ {
+ term: {
+ 'domains.fullyQualifiedName': 'engineering',
+ },
+ },
+ {
+ prefix: {
+ 'domains.fullyQualifiedName': 'engineering.',
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ });
+ });
+
+ it('should preserve existing params when adding query_filter', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TABLE,
+ limit: 10,
+ offset: 0,
+ });
+ const result = withDomainFilter(config);
+
+ expect(result.params).toHaveProperty('index', SearchIndex.TABLE);
+ expect(result.params).toHaveProperty('limit', 10);
+ expect(result.params).toHaveProperty('offset', 0);
+ expect(result.params).toHaveProperty('query_filter');
+ });
+
+ it('should preserve non-bool top-level clauses when adding domain filter', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({ activeDomain: 'engineering' });
+
+ const existingFilter = JSON.stringify({
+ query: {
+ term: { 'some.field': 'someValue' },
+ bool: { must: [{ term: { 'other.field': 'otherValue' } }] },
+ },
+ });
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TABLE,
+ query_filter: existingFilter,
+ });
+ const result = withDomainFilter(config);
+
+ const parsed = JSON.parse(result.params?.query_filter as string);
+
+ expect(parsed.query.bool.must).toContainEqual({
+ term: { 'some.field': 'someValue' },
+ });
+ expect(parsed.query.bool.must).toContainEqual({
+ term: { 'other.field': 'otherValue' },
+ });
+ expect(parsed.query.bool.must).toContainEqual({
+ bool: {
+ should: [
+ {
+ term: {
+ 'domains.fullyQualifiedName': 'engineering',
+ },
+ },
+ {
+ prefix: {
+ 'domains.fullyQualifiedName': 'engineering.',
+ },
+ },
+ ],
+ },
+ });
+ expect(parsed.query.bool.must).toHaveLength(3);
+ });
+ });
+
+ describe('nested domain paths', () => {
+ it('should handle nested domain paths correctly', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/tables'
+ );
+ mockGetState.mockReturnValue({
+ activeDomain: 'engineering.backend.services',
+ });
+
+ const config = createMockConfig('get', '/api/tables');
+ const result = withDomainFilter(config);
+
+ expect(result.params).toEqual({
+ domain: 'engineering.backend.services',
+ });
+ });
+
+ it('should add should filter with nested domain for search queries', () => {
+ (getPathNameFromWindowLocation as jest.Mock).mockReturnValueOnce(
+ '/api/search'
+ );
+ mockGetState.mockReturnValue({
+ activeDomain: 'engineering.backend.services',
+ });
+
+ const config = createMockConfig('get', '/search/query', {
+ index: SearchIndex.TABLE,
+ });
+ const result = withDomainFilter(config);
+
+ const filter = JSON.parse(result.params?.query_filter as string);
+
+ expect(filter.query.bool.must[0]).toEqual({
+ bool: {
+ should: [
+ {
+ term: {
+ 'domains.fullyQualifiedName': 'engineering.backend.services',
+ },
+ },
+ {
+ prefix: {
+ 'domains.fullyQualifiedName': 'engineering.backend.services.',
+ },
+ },
+ ],
+ },
+ });
+ });
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/hoc/withDomainFilter.tsx b/openmetadata-ui/src/main/resources/ui/src/hoc/withDomainFilter.tsx
new file mode 100644
index 000000000000..dac6cbbd787b
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/hoc/withDomainFilter.tsx
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2026 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { InternalAxiosRequestConfig } from 'axios';
+import { DEFAULT_DOMAIN_VALUE } from '../constants/constants';
+import { SearchIndex } from '../enums/search.enum';
+import { useDomainStore } from '../hooks/useDomainStore';
+import {
+ QueryFieldInterface,
+ QueryFilterInterface,
+} from '../pages/ExplorePage/ExplorePage.interface';
+import { getPathNameFromWindowLocation } from '../utils/LocationUtils';
+
+export const withDomainFilter = (
+ config: InternalAxiosRequestConfig
+): InternalAxiosRequestConfig => {
+ const isGetRequest = config.method === 'get';
+ const activeDomain = useDomainStore.getState().activeDomain;
+ const hasActiveDomain = activeDomain !== DEFAULT_DOMAIN_VALUE;
+ const currentPath = getPathNameFromWindowLocation();
+
+ const shouldNotIntercept = [
+ '/domain',
+ '/auth/logout',
+ '/auth/refresh',
+ ].reduce((prev, curr) => {
+ return prev || currentPath.startsWith(curr);
+ }, false);
+
+ if (shouldNotIntercept) {
+ return config;
+ }
+
+ if (isGetRequest && hasActiveDomain) {
+ if (config.url?.includes('/search/query')) {
+ if (config.params?.index === SearchIndex.TAG) {
+ return config;
+ }
+
+ const domainFilterField =
+ config.params?.index === SearchIndex.DOMAIN
+ ? 'fullyQualifiedName'
+ : 'domains.fullyQualifiedName';
+ let filter: QueryFilterInterface = { query: { bool: {} } };
+ if (config.params?.query_filter) {
+ try {
+ const parsed = JSON.parse(config.params.query_filter as string);
+ filter = parsed?.query ? parsed : { query: { bool: {} } };
+ } catch {
+ filter = { query: { bool: {} } };
+ }
+ }
+
+ let mustArray: QueryFieldInterface[] = [];
+ const existingMust = filter.query?.bool?.must;
+ if (Array.isArray(existingMust)) {
+ mustArray = [...existingMust];
+ } else if (existingMust) {
+ mustArray = [existingMust];
+ }
+
+ const { bool: existingBool, ...nonBoolClauses } = filter.query ?? {};
+ for (const [key, value] of Object.entries(nonBoolClauses)) {
+ mustArray.push({ [key]: value } as QueryFieldInterface);
+ }
+
+ filter.query = {
+ bool: {
+ ...existingBool,
+ must: [
+ ...mustArray,
+ {
+ bool: {
+ should: [
+ {
+ term: {
+ [domainFilterField]: activeDomain,
+ },
+ },
+ {
+ prefix: {
+ [domainFilterField]: `${activeDomain}.`,
+ },
+ },
+ ],
+ },
+ } as QueryFieldInterface,
+ ],
+ },
+ };
+
+ config.params = {
+ ...config.params,
+ query_filter: JSON.stringify(filter),
+ };
+ } else {
+ config.params = {
+ ...config.params,
+ domain: activeDomain,
+ };
+ }
+ }
+
+ return config;
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/hooks/currentUserStore/useCurrentUserStore.test.ts b/openmetadata-ui/src/main/resources/ui/src/hooks/currentUserStore/useCurrentUserStore.test.ts
index 55a5604fe9fa..6fbad8742ca1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/hooks/currentUserStore/useCurrentUserStore.test.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/hooks/currentUserStore/useCurrentUserStore.test.ts
@@ -11,7 +11,6 @@
* limitations under the License.
*/
import { renderHook, waitFor } from '@testing-library/react';
-import { SupportedLocales } from '../../utils/i18next/LocalUtil.interface';
import { useApplicationStore } from '../useApplicationStore';
import {
useCurrentUserPreferences,
@@ -23,11 +22,6 @@ jest.mock('../useApplicationStore', () => ({
useApplicationStore: jest.fn(),
}));
-// Mock the detectBrowserLanguage function
-jest.mock('../../utils/i18next/LocalUtil', () => ({
- detectBrowserLanguage: jest.fn(() => 'en-US'),
-}));
-
jest.mock('../../constants/constants', () => ({
PAGE_SIZE_BASE: 15,
}));
@@ -57,7 +51,6 @@ describe('useCurrentUserStore', () => {
const defaultPreferences = {
isSidebarCollapsed: false,
- language: SupportedLocales.English,
selectedEntityTableColumns: {},
globalPageSize: 15,
recentlySearched: [],
@@ -78,14 +71,12 @@ describe('useCurrentUserStore', () => {
result.current.setPreference({
isSidebarCollapsed: true,
- language: SupportedLocales.简体中文,
});
// Preferences should remain default since no user
expect(result.current.preferences).toEqual({
...defaultPreferences,
isSidebarCollapsed: false,
- language: SupportedLocales.English,
});
});
@@ -104,14 +95,14 @@ describe('useCurrentUserStore', () => {
// Set preferences directly through the setPreference method
await waitFor(async () => {
result.current.setPreference({
- language: SupportedLocales.简体中文,
+ isSidebarCollapsed: true,
});
});
// Direct check without waitFor
expect(result.current.preferences).toEqual({
...defaultPreferences,
- language: SupportedLocales.简体中文,
+ isSidebarCollapsed: true,
});
});
@@ -142,7 +133,6 @@ describe('useCurrentUserStore', () => {
// Should spread language from defaultPreferences since it's missing
expect(result.current.preferences).toEqual({
isSidebarCollapsed: true,
- language: SupportedLocales.English, // From defaultPreferences
selectedEntityTableColumns: { table1: ['col1', 'col2'] },
globalPageSize: 15,
recentlySearched: [],
@@ -167,7 +157,6 @@ describe('useCurrentUserStore', () => {
preferences: {
userWithLanguage: {
isSidebarCollapsed: false,
- language: SupportedLocales.简体中文,
selectedEntityTableColumns: {},
globalPageSize: 15,
recentlySearched: [],
@@ -183,7 +172,6 @@ describe('useCurrentUserStore', () => {
// Should preserve the existing language preference
expect(result.current.preferences).toEqual({
isSidebarCollapsed: false,
- language: SupportedLocales.简体中文, // User's existing preference preserved
selectedEntityTableColumns: {},
globalPageSize: 15,
recentlySearched: [],
diff --git a/openmetadata-ui/src/main/resources/ui/src/hooks/currentUserStore/useCurrentUserStore.ts b/openmetadata-ui/src/main/resources/ui/src/hooks/currentUserStore/useCurrentUserStore.ts
index 5a788490c3d1..c0b326109614 100644
--- a/openmetadata-ui/src/main/resources/ui/src/hooks/currentUserStore/useCurrentUserStore.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/hooks/currentUserStore/useCurrentUserStore.ts
@@ -15,8 +15,6 @@ import { RecentlySearchedData, RecentlyViewedData } from 'Models';
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
import { PAGE_SIZE_BASE } from '../../constants/constants';
-import { detectBrowserLanguage } from '../../utils/i18next/LocalUtil';
-import { SupportedLocales } from '../../utils/i18next/LocalUtil.interface';
import { useApplicationStore } from '../useApplicationStore';
export interface MarketplaceRecentSearchEntry {
@@ -26,7 +24,6 @@ export interface MarketplaceRecentSearchEntry {
export interface UserPreferences {
isSidebarCollapsed: boolean;
- language: SupportedLocales;
selectedEntityTableColumns: Record;
globalPageSize: number;
recentlyViewed: RecentlyViewedData[];
@@ -47,7 +44,6 @@ interface Store {
const defaultPreferences: UserPreferences = {
isSidebarCollapsed: false,
- language: detectBrowserLanguage(),
selectedEntityTableColumns: {},
globalPageSize: PAGE_SIZE_BASE,
recentlyViewed: [],
diff --git a/openmetadata-ui/src/main/resources/ui/src/index.tsx b/openmetadata-ui/src/main/resources/ui/src/index.tsx
index 09913d94229d..aa68632d0c79 100644
--- a/openmetadata-ui/src/main/resources/ui/src/index.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/index.tsx
@@ -13,7 +13,7 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
-import App from './App';
+import AppRoot from './AppRoot';
import './styles/index';
import { getBasePath } from './utils/HistoryUtils';
@@ -25,11 +25,11 @@ const root = createRoot(container);
root.render(
-
+
);
-if ('serviceWorker' in navigator && 'indexedDB' in window) {
+if ('serviceWorker' in navigator && 'indexedDB' in globalThis) {
window.addEventListener('load', () => {
const basePath = getBasePath();
const serviceWorkerPath = basePath
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddGlossary/AddGlossaryPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddGlossary/AddGlossaryPage.component.tsx
index b484e2d08d35..1b24770558b9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/AddGlossary/AddGlossaryPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddGlossary/AddGlossaryPage.component.tsx
@@ -30,7 +30,7 @@ import { CreateGlossary } from '../../generated/api/data/createGlossary';
import { Operation } from '../../generated/entity/policies/policy';
import { withPageLayout } from '../../hoc/withPageLayout';
import { addGlossaries } from '../../rest/glossaryAPI';
-import { getIsErrorMatch } from '../../utils/CommonUtils';
+import { getIsErrorMatch } from '../../utils/APIUtils';
import { checkPermission } from '../../utils/PermissionsUtils';
import { getGlossaryPath } from '../../utils/RouterUtils';
import { getClassifications, getTaglist } from '../../utils/TagsUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPageSettings/CustomPageSettings.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPageSettings/CustomPageSettings.test.tsx
index a7f754061674..1c966dd1527e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPageSettings/CustomPageSettings.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPageSettings/CustomPageSettings.test.tsx
@@ -62,10 +62,6 @@ jest.mock('../../components/common/NextPrevious/NextPrevious', () => {
return jest.fn().mockImplementation(() => NextPrevious
);
});
-jest.mock('../../utils/CommonUtils', () => ({
- Transi18next: jest.fn().mockReturnValue(Transi18next
),
-}));
-
jest.mock('../../components/PageHeader/PageHeader.component', () => {
return jest.fn().mockImplementation(({ children, data }) => (
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPageSettings/CustomPageSettings.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPageSettings/CustomPageSettings.tsx
index 9b6739190623..e760cf138c0f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPageSettings/CustomPageSettings.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPageSettings/CustomPageSettings.tsx
@@ -37,13 +37,15 @@ import { Paging } from '../../generated/type/paging';
import { usePaging } from '../../hooks/paging/usePaging';
import { useApplicationStore } from '../../hooks/useApplicationStore';
import { getAllPersonas } from '../../rest/PersonaAPI';
-import { Transi18next } from '../../utils/CommonUtils';
import { getEntityName } from '../../utils/EntityUtils';
import {
getCustomizePagePath,
getSettingPageEntityBreadCrumb,
} from '../../utils/GlobalSettingsUtils';
-import { translateWithNestedKeys } from '../../utils/i18next/LocalUtil';
+import {
+ Transi18next,
+ translateWithNestedKeys,
+} from '../../utils/i18next/LocalUtil';
import { getSettingPath } from '../../utils/RouterUtils';
import { showErrorToast } from '../../utils/ToastUtils';
import './custom-page-settings.less';
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.test.tsx
index d0c47476f3ae..042a819f2aa7 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.test.tsx
@@ -11,7 +11,8 @@
* limitations under the License.
*/
-import { act, render, screen } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import { act } from 'react';
import { useParams } from 'react-router-dom';
import { Page, PageType } from '../../generated/system/ui/page';
import {
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.tsx
index 42f84dc9226a..6654e309474b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.tsx
@@ -43,7 +43,7 @@ import {
updateDocument,
} from '../../rest/DocStoreAPI';
import { getPersonaByName } from '../../rest/PersonaAPI';
-import { Transi18next } from '../../utils/CommonUtils';
+import { Transi18next } from '../../utils/i18next/LocalUtil';
import { getSettingPath } from '../../utils/RouterUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
import { useRequiredParams } from '../../utils/useRequiredParams';
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/LoginPage/LoginCarousel.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/LoginPage/LoginCarousel.test.tsx
index b245860f0635..1b84c6e9b18a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/LoginPage/LoginCarousel.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/LoginPage/LoginCarousel.test.tsx
@@ -11,7 +11,8 @@
* limitations under the License.
*/
-import { act, render, screen } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import loginClassBase from '../../constants/LoginClassBase';
import LoginCarousel from './LoginCarousel';
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/my-data.less b/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/my-data.less
index dd1ff49ff7e9..7bc860d0f1e7 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/my-data.less
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/my-data.less
@@ -70,3 +70,9 @@
scrollbar-color: @grey-300 transparent;
}
}
+
+.grid-wrapper {
+ .grid-container {
+ margin-top: -220px;
+ }
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/AddPolicyPage/AddPolicyPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/AddPolicyPage/AddPolicyPage.tsx
index dde5c4a1caf8..de344d089090 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/AddPolicyPage/AddPolicyPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/AddPolicyPage/AddPolicyPage.tsx
@@ -31,8 +31,8 @@ import {
import { withPageLayout } from '../../../hoc/withPageLayout';
import { FieldProp, FieldTypes } from '../../../interface/FormUtils.interface';
import { addPolicy } from '../../../rest/rolesAPIV1';
+import { getIsErrorMatch } from '../../../utils/APIUtils';
import brandClassBase from '../../../utils/BrandData/BrandClassBase';
-import { getIsErrorMatch } from '../../../utils/CommonUtils';
import { getField } from '../../../utils/formUtils';
import { translateWithNestedKeys } from '../../../utils/i18next/LocalUtil';
import { getPath, getPolicyWithFqnPath } from '../../../utils/RouterUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.test.tsx
index adcf94566158..48fd6bfa7fb4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.test.tsx
@@ -66,9 +66,6 @@ jest.mock('../../../components/common/ResizablePanels/ResizablePanels', () =>
>
))
);
-jest.mock('../../../utils/CommonUtils', () => ({
- getIsErrorMatch: jest.fn(),
-}));
jest.mock('../../../utils/BrandData/BrandClassBase', () => ({
__esModule: true,
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.tsx
index e26b8a73295d..7252d82c9674 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.tsx
@@ -28,8 +28,8 @@ import { Policy } from '../../../generated/entity/policies/policy';
import { withPageLayout } from '../../../hoc/withPageLayout';
import { FieldProp, FieldTypes } from '../../../interface/FormUtils.interface';
import { addRole, getPolicies } from '../../../rest/rolesAPIV1';
+import { getIsErrorMatch } from '../../../utils/APIUtils';
import brandClassBase from '../../../utils/BrandData/BrandClassBase';
-import { getIsErrorMatch } from '../../../utils/CommonUtils';
import { getField } from '../../../utils/formUtils';
import { translateWithNestedKeys } from '../../../utils/i18next/LocalUtil';
import { getPath, getRoleWithFqnPath } from '../../../utils/RouterUtils';
@@ -70,7 +70,6 @@ const AddRolePage = () => {
const data = {
name: trim(name),
description,
- // TODO the policies should be names instead of ID
policies: selectedPolicies.map((policy) => policy),
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SignUp/SignUpPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SignUp/SignUpPage.test.tsx
index d887a748be17..6189d8352393 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/SignUp/SignUpPage.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/SignUp/SignUpPage.test.tsx
@@ -11,9 +11,10 @@
* limitations under the License.
*/
-import { act, fireEvent, render, screen } from '@testing-library/react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { act } from 'react';
import { createUser } from '../../rest/userAPI';
-import { getImages } from '../../utils/CommonUtils';
+import { getImages } from '../../utils/UserDataUtils';
import { mockChangedFormData, mockCreateUser } from './mocks/SignupData.mock';
import SignUp from './SignUpPage';
@@ -50,13 +51,12 @@ jest.mock('../../utils/ToastUtils', () => ({
showErrorToast: jest.fn().mockImplementation(() => mockShowErrorToast),
}));
-jest.mock('../../utils/CommonUtils', () => ({
+jest.mock('../../utils/UserDataUtils', () => ({
getImages: jest
.fn()
.mockResolvedValue(
'https://lh3.googleusercontent.com/a/ALm5wu0HwEPhAbyRha16cUHrEum-zxTDzj6KZiqYsT5Y=s96-c'
),
- Transi18next: jest.fn().mockReturnValue('text'),
}));
jest.mock('../../utils/AuthProvider.util', () => ({
@@ -84,7 +84,7 @@ describe('SignUp page', () => {
const emailInput = screen.getByTestId('email-input');
const selectTeamLabel = screen.getByTestId('select-team-label');
const createButton = screen.getByTestId('create-button');
- const loadingContent = await screen.queryByTestId('loading-content');
+ const loadingContent = screen.queryByTestId('loading-content');
const submitButton = screen.getByTestId('create-button');
expect(logo).toBeInTheDocument();
@@ -109,13 +109,9 @@ describe('SignUp page', () => {
render(
);
const form = screen.getByTestId('create-user-form');
- const fullNameInput = screen.getByTestId(
- 'full-name-input'
- ) as HTMLInputElement;
- const userNameInput = screen.getByTestId(
- 'username-input'
- ) as HTMLInputElement;
- const emailInput = screen.getByTestId('email-input') as HTMLInputElement;
+ const fullNameInput = screen.getByTestId('full-name-input');
+ const userNameInput = screen.getByTestId('username-input');
+ const emailInput = screen.getByTestId('email-input');
const submitButton = screen.getByTestId('create-button');
expect(form).toBeInTheDocument();
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SignUp/SignUpPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SignUp/SignUpPage.tsx
index a81d2a114d2b..da76a19f29a6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/SignUp/SignUpPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/SignUp/SignUpPage.tsx
@@ -19,11 +19,8 @@ import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { UserProfile } from '../../components/Auth/AuthProviders/AuthProvider.interface';
import TeamsSelectable from '../../components/Settings/Team/TeamsSelectable/TeamsSelectable';
-import {
- REDIRECT_PATHNAME,
- ROUTES,
- VALIDATION_MESSAGES,
-} from '../../constants/constants';
+import { ROUTES, VALIDATION_MESSAGES } from '../../constants/constants';
+import { REDIRECT_PATHNAME } from '../../constants/router.constants';
import { ClientType } from '../../generated/configuration/authenticationConfiguration';
import { EntityReference } from '../../generated/entity/type';
import { useApplicationStore } from '../../hooks/useApplicationStore';
@@ -33,8 +30,9 @@ import {
setUrlPathnameExpiryAfterRoute,
} from '../../utils/AuthProvider.util';
import brandClassBase from '../../utils/BrandData/BrandClassBase';
-import { getImages, Transi18next } from '../../utils/CommonUtils';
+import { Transi18next } from '../../utils/i18next/LocalUtil';
import { showErrorToast } from '../../utils/ToastUtils';
+import { getImages } from '../../utils/UserDataUtils';
const cookieStorage = new CookieStorage();
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/UserListPage/UserListPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/UserListPage/UserListPageV1.tsx
index 1d0d3190557a..725ec93f5e30 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/UserListPage/UserListPageV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/UserListPage/UserListPageV1.tsx
@@ -51,9 +51,9 @@ import { usePaging } from '../../hooks/paging/usePaging';
import { useTableFilters } from '../../hooks/useTableFilters';
import { searchQuery } from '../../rest/searchAPI';
import { getUsers, restoreUser, UsersQueryParams } from '../../rest/userAPI';
-import { Transi18next } from '../../utils/CommonUtils';
import { getEntityName } from '../../utils/EntityUtils';
import { getSettingPageEntityBreadCrumb } from '../../utils/GlobalSettingsUtils';
+import { Transi18next } from '../../utils/i18next/LocalUtil';
import { getSettingPath } from '../../utils/RouterUtils';
import { getTermQuery } from '../../utils/SearchUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx
index 430a32ea829e..c58f8e971e07 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx
@@ -29,7 +29,7 @@ import { Include } from '../../generated/type/include';
import { useApplicationStore } from '../../hooks/useApplicationStore';
import { useFqn } from '../../hooks/useFqn';
import { getUserByName, updateUserDetail } from '../../rest/userAPI';
-import { Transi18next } from '../../utils/CommonUtils';
+import { Transi18next } from '../../utils/i18next/LocalUtil';
import { getTermQuery } from '../../utils/SearchUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/miscAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/miscAPI.ts
index 27faf7d1d549..97d29aef00f1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/rest/miscAPI.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/rest/miscAPI.ts
@@ -12,8 +12,10 @@
*/
import { AxiosResponse } from 'axios';
+import { isEmpty } from 'lodash';
import { Edge } from '../components/Entity/EntityLineage/EntityLineage.interface';
import { ExploreSearchIndex } from '../components/Explore/ExplorePage.interface';
+import { WILD_CARD_CHAR } from '../constants/char.constants';
import { PAGE_SIZE } from '../constants/constants';
import { AsyncDeleteJob } from '../context/AsyncDeleteProvider/AsyncDeleteProvider.interface';
import { SearchIndex } from '../enums/search.enum';
@@ -23,10 +25,58 @@ import { SearchRequest } from '../generated/search/searchRequest';
import { ValidationResponse } from '../generated/system/validationResponse';
import { Paging } from '../generated/type/paging';
import { SearchResponse } from '../interface/search.interface';
-import { getSearchAPIQueryParams } from '../utils/SearchUtils';
-import { escapeESReservedCharacters } from '../utils/StringsUtils';
+import {
+ escapeESReservedCharacters,
+ getEncodedFqn,
+} from '../utils/StringsUtils';
import APIClient from './index';
+export const getSearchAPIQueryParams = (
+ queryString: string,
+ from: number,
+ size: number,
+ filters: string,
+ sortField: string,
+ sortOrder: string,
+ searchIndex: SearchIndex | SearchIndex[],
+ onlyDeleted = false,
+ trackTotalHits = false,
+ wildcard = true
+): Record
=> {
+ const start = (from - 1) * size;
+
+ const encodedQueryString = queryString
+ ? getEncodedFqn(escapeESReservedCharacters(queryString))
+ : '';
+
+ const query =
+ wildcard && encodedQueryString !== WILD_CARD_CHAR
+ ? `*${encodedQueryString}*`
+ : encodedQueryString;
+
+ const params: Record = {
+ q: query + (filters ? ` AND ${filters}` : ''),
+ from: start,
+ size,
+ index: searchIndex,
+ deleted: onlyDeleted,
+ };
+
+ if (!isEmpty(sortField)) {
+ params.sort_field = sortField;
+ }
+
+ if (!isEmpty(sortOrder)) {
+ params.sort_order = sortOrder;
+ }
+
+ if (trackTotalHits) {
+ params.track_total_hits = trackTotalHits;
+ }
+
+ return params;
+};
+
export const searchData = (
queryString: string,
from: number,
diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/searchAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/searchAPI.ts
index 232a99ac2260..a40c31700b4d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/rest/searchAPI.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/rest/searchAPI.ts
@@ -23,7 +23,7 @@ import {
SearchResponse,
} from '../interface/search.interface';
import { omitDeep } from '../utils/APIUtils';
-import { getQueryWithSlash } from '../utils/SearchUtils';
+import { getQueryWithSlash } from '../utils/StringsUtils';
import APIClient from './index';
const getSearchIndexParam: (
diff --git a/openmetadata-ui/src/main/resources/ui/src/setupTests.js b/openmetadata-ui/src/main/resources/ui/src/setupTests.js
index fa4c15921153..b7ff1e84e5e4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/setupTests.js
+++ b/openmetadata-ui/src/main/resources/ui/src/setupTests.js
@@ -89,28 +89,6 @@ window.IntersectionObserver = jest.fn().mockImplementation(() => ({
disconnect: jest.fn(),
}));
-/**
- * mock i18next
- */
-
-jest.mock('i18next', () => ({
- ...jest.requireActual('i18next'),
- use: jest.fn(),
- init: jest.fn(),
- t: jest.fn().mockImplementation((key) => key),
-}));
-
-jest.mock('utils/i18next/LocalUtil', () => ({
- useTranslation: jest.fn().mockReturnValue({
- t: (key) => key,
- }),
- detectBrowserLanguage: jest.fn().mockReturnValue('en-US'),
- t: (key) => key,
- translateWithNestedKeys: jest.fn((key, params) => {
- return params ? `${key}_${JSON.stringify(params)}` : key;
- }),
- dir: jest.fn().mockReturnValue('ltr'),
-}));
/**
* mock react-i18next
*/
@@ -228,3 +206,30 @@ jest.mock('@mui/material', () => {
styled,
};
});
+
+jest.mock('./utils/i18next/LocalUtil', () => {
+ const React = require('react');
+
+ return {
+ Transi18next: jest
+ .fn()
+ .mockImplementation(({ i18nKey, renderElement, values }) => {
+ const valueArr = Object.values(values ?? {});
+
+ return React.createElement('div', { 'data-testid': i18nKey }, [
+ i18nKey,
+ renderElement,
+ valueArr,
+ ]);
+ }),
+ __esModule: true,
+ default: {
+ t: jest.fn().mockImplementation((key) => key),
+ on: jest.fn(),
+ },
+ t: jest.fn().mockImplementation((key) => key),
+ translateWithNestedKeys: jest.fn().mockImplementation((key, params) => {
+ return params ? `${key}_${JSON.stringify(params)}` : key;
+ }),
+ };
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/APIServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/APIServiceUtils.ts
index 9e6a2b6b2067..9a0c1581c729 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/APIServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/APIServiceUtils.ts
@@ -11,21 +11,15 @@
* limitations under the License.
*/
import { cloneDeep } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { APIServiceType } from '../generated/entity/services/apiService';
import restConnection from '../jsons/connectionSchemas/connections/api/restConnection.json';
export const getAPIConfig = (type: APIServiceType) => {
let schema = {};
const uiSchema = { ...COMMON_UI_SCHEMA };
- switch (type) {
- case APIServiceType.REST:
- schema = restConnection;
-
- break;
-
- default:
- break;
+ if (type === APIServiceType.REST) {
+ schema = restConnection;
}
return cloneDeep({ schema, uiSchema });
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/APIUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/APIUtils.ts
index a56d3195cfd6..edd35871042c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/APIUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/APIUtils.ts
@@ -12,7 +12,7 @@
*/
import { AxiosError } from 'axios';
-import { isArray, isObject, isString, transform } from 'lodash';
+import { get, isArray, isObject, isString, transform } from 'lodash';
import { SearchIndex } from '../enums/search.enum';
import { DataProduct } from '../generated/entity/domains/dataProduct';
import { Domain } from '../generated/entity/domains/domain';
@@ -152,3 +152,21 @@ export const omitDeep = (
}
});
};
+
+export const getIsErrorMatch = (error: AxiosError, key: string): boolean => {
+ let errorMessage = '';
+
+ if (error) {
+ errorMessage = get(error, 'response.data.message', '');
+ if (!errorMessage) {
+ // if error text is undefined or null or empty, try responseMessage in data
+ errorMessage = get(error, 'response.data.responseMessage', '');
+ }
+ if (!errorMessage) {
+ errorMessage = get(error, 'response.data', '') as string;
+ errorMessage = typeof errorMessage === 'string' ? errorMessage : '';
+ }
+ }
+
+ return errorMessage.includes(key);
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts
index 927930dc8654..39db2517477e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts
@@ -10,20 +10,78 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { FC } from 'react';
-import AuthenticatedAppRouter from '../components/AppRouter/AuthenticatedAppRouter';
-import { ApplicationRoutesClassBase } from './ApplicationRoutesClassBase';
+import { render, waitFor } from '@testing-library/react';
+import React, { FC } from 'react';
+import { UnAuthenticatedAppRouter } from '../components/AppRouter/UnAuthenticatedAppRouter';
+import { APP_ROUTER_ROUTES } from '../constants/router.constants';
+import applicationRoutesClassBase, {
+ ApplicationRoutesClassBase,
+} from './ApplicationRoutesClassBase';
+
+jest.mock('../components/AppRouter/AuthenticatedAppRouter', () => ({
+ __esModule: true,
+ default: function AuthenticatedAppRouter() {
+ return React.createElement(
+ 'div',
+ {
+ 'data-testid': 'authenticated-app-router',
+ },
+ 'Authenticated'
+ );
+ },
+}));
describe('ApplicationRoutesClassBase', () => {
- let applicationRoutesClassBase: ApplicationRoutesClassBase;
+ let instance: ApplicationRoutesClassBase;
beforeEach(() => {
- applicationRoutesClassBase = new ApplicationRoutesClassBase();
+ instance = new ApplicationRoutesClassBase();
+ });
+
+ describe('getRouteElements', () => {
+ it('should return a lazy-loaded AuthenticatedAppRouter wrapped with Suspense', async () => {
+ const RouterComponent = instance.getRouteElements();
+
+ const { getByTestId } = render(
+ React.createElement(RouterComponent, null)
+ );
+
+ await waitFor(() => {
+ expect(getByTestId('authenticated-app-router')).toBeInTheDocument();
+ });
+ });
+
+ it('should return the same component reference as the default export', () => {
+ const result = instance.getRouteElements();
+ const defaultResult = applicationRoutesClassBase.getRouteElements();
+
+ expect(result).toBe(defaultResult);
+ });
+ });
+
+ describe('getUnAuthenticatedRouteElements', () => {
+ it('should return UnAuthenticatedAppRouter', () => {
+ const result: FC = instance.getUnAuthenticatedRouteElements();
+
+ expect(result).toBe(UnAuthenticatedAppRouter);
+ });
});
- it('should return AuthenticatedAppRouter from getRouteElements', () => {
- const result: FC = applicationRoutesClassBase.getRouteElements();
+ describe('isProtectedRoute', () => {
+ it('should identify protected routes correctly', () => {
+ expect(instance.isProtectedRoute('/dashboard')).toBe(true);
+ expect(instance.isProtectedRoute('/settings')).toBe(true);
+ expect(instance.isProtectedRoute('/explore')).toBe(true);
+ });
- expect(result).toBe(AuthenticatedAppRouter);
+ it('should identify unprotected routes correctly', () => {
+ expect(instance.isProtectedRoute(APP_ROUTER_ROUTES.SIGNIN)).toBe(false);
+ expect(instance.isProtectedRoute(APP_ROUTER_ROUTES.SIGNUP)).toBe(false);
+ expect(instance.isProtectedRoute(APP_ROUTER_ROUTES.HOME)).toBe(false);
+ expect(instance.isProtectedRoute(APP_ROUTER_ROUTES.FORGOT_PASSWORD)).toBe(
+ false
+ );
+ expect(instance.isProtectedRoute(APP_ROUTER_ROUTES.CALLBACK)).toBe(false);
+ });
});
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.ts
index 1c22cea5d864..cf8250ab69d6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.ts
@@ -11,10 +11,14 @@
* limitations under the License.
*/
-import { FC } from 'react';
-import AuthenticatedAppRouter from '../components/AppRouter/AuthenticatedAppRouter';
+import { FC, lazy } from 'react';
import { UnAuthenticatedAppRouter } from '../components/AppRouter/UnAuthenticatedAppRouter';
-import { ROUTES } from '../constants/constants';
+import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback';
+import { UNPROTECTED_ROUTES } from '../constants/router.constants';
+
+const AuthenticatedAppRouter = withSuspenseFallback(
+ lazy(() => import('../components/AppRouter/AuthenticatedAppRouter'))
+);
class ApplicationRoutesClassBase {
public getRouteElements(): FC {
@@ -26,22 +30,7 @@ class ApplicationRoutesClassBase {
}
public isProtectedRoute(pathname: string): boolean {
- return (
- [
- ROUTES.SIGNUP,
- ROUTES.SIGNIN,
- ROUTES.FORGOT_PASSWORD,
- ROUTES.CALLBACK,
- ROUTES.SILENT_CALLBACK,
- ROUTES.REGISTER,
- ROUTES.RESET_PASSWORD,
- ROUTES.ACCOUNT_ACTIVATION,
- ROUTES.HOME,
- ROUTES.AUTH_CALLBACK,
- ROUTES.NOT_FOUND,
- ROUTES.LOGOUT,
- ].indexOf(pathname) === -1
- );
+ return !UNPROTECTED_ROUTES.has(pathname);
}
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts b/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts
index ae6dd95cc1c6..513f43c7dfda 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts
@@ -11,12 +11,7 @@
* limitations under the License.
*/
-import {
- AuthenticationResult,
- BrowserCacheLocation,
- Configuration,
- PopupRequest,
-} from '@azure/msal-browser';
+import type { AuthenticationResult, Configuration } from '@azure/msal-browser';
import { CookieStorage } from 'cookie-storage';
import jwtDecode, { JwtPayload } from 'jwt-decode';
import { first, get, isEmpty, isNil } from 'lodash';
@@ -26,8 +21,9 @@ import {
OidcUser,
UserProfile,
} from '../components/Auth/AuthProviders/AuthProvider.interface';
-import { REDIRECT_PATHNAME, ROUTES } from '../constants/constants';
+import { ROUTES } from '../constants/constants';
import { EMAIL_REG_EX } from '../constants/regex.constants';
+import { REDIRECT_PATHNAME } from '../constants/router.constants';
import {
AuthenticationConfiguration,
ClientType,
@@ -188,7 +184,7 @@ export const getAuthConfig = (
postLogoutRedirectUri: '/',
},
cache: {
- cacheLocation: BrowserCacheLocation.LocalStorage,
+ cacheLocation: 'localStorage',
},
provider,
enableSelfSignup,
@@ -207,7 +203,7 @@ export const getAuthConfig = (
postLogoutRedirectUri: '/',
},
cache: {
- cacheLocation: BrowserCacheLocation.LocalStorage,
+ cacheLocation: 'localStorage',
},
provider,
clientType,
@@ -222,9 +218,9 @@ export const getAuthConfig = (
};
// Add here scopes for id token to be used at MS Identity Platform endpoints.
-export const msalLoginRequest: PopupRequest = {
+export const msalLoginRequest = {
scopes: ['openid', 'profile', 'email', 'offline_access'],
-};
+} as const;
export const getNameFromEmail = (email: string) => {
if (new RegExp(EMAIL_REG_EX).exec(email)) {
@@ -279,8 +275,8 @@ export const extractNameFromUserProfile = (user: UserProfile): string => {
return user.name.trim();
}
- const givenName = get(user, 'given_name', '');
- const familyName = get(user, 'family_name', '');
+ const givenName: string = get(user, 'given_name', '');
+ const familyName: string = get(user, 'family_name', '');
if (givenName && familyName) {
return `${givenName.trim()} ${familyName.trim()}`;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
index a1031c9711e3..0ffebc873e1e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
@@ -34,10 +34,8 @@ import {
RecentlyViewedData,
} from 'Models';
import { ReactNode } from 'react';
-import { Trans } from 'react-i18next';
import Loader from '../components/common/Loader/Loader';
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
-import { imageTypes } from '../constants/constants';
import { BASE_COLORS } from '../constants/DataInsight.constants';
import { FEED_COUNT_INITIAL_DATA } from '../constants/entity.constants';
import { VALIDATE_ESCAPE_START_END_REGEX } from '../constants/regex.constants';
@@ -52,7 +50,7 @@ import { getFeedCount } from '../rest/feedsAPI';
import brandClassBase from './BrandData/BrandClassBase';
import { getEntityFeedLink } from './EntityUtils';
import Fqn from './Fqn';
-import i18n, { t } from './i18next/LocalUtil';
+import i18n, { t, Transi18next } from './i18next/LocalUtil';
import serviceUtilClassBase from './ServiceUtilClassBase';
import { showErrorToast } from './ToastUtils';
@@ -344,18 +342,6 @@ export const requiredField = (label: string, excludeSpace = false) => (
>
);
-export const getImages = (imageUri: string) => {
- const imagesObj: typeof imageTypes = imageTypes;
- for (const type in imageTypes) {
- imagesObj[type as keyof typeof imageTypes] = imageUri.replace(
- 's96-c',
- imageTypes[type as keyof typeof imageTypes]
- );
- }
-
- return imagesObj;
-};
-
export const getServiceLogo = (
serviceType: string,
className = ''
@@ -647,21 +633,6 @@ export const getTrimmedContent = (content: string, limit: number) => {
return refinedContent.join(' ');
};
-export const Transi18next = ({
- i18nKey,
- values,
- renderElement,
- ...otherProps
-}: {
- i18nKey: string;
- values?: object;
- renderElement: ReactNode;
-}): JSX.Element => (
-
- {renderElement}
-
-);
-
export const getEntityDeleteMessage = (entity: string, dependents: string) => {
if (dependents) {
return t('message.permanently-delete-metadata-and-dependents', {
@@ -703,24 +674,6 @@ export const reducerWithoutAction = (state: S, action: A) => {
*/
export const getBase64EncodedString = (text: string): string => btoa(text);
-export const getIsErrorMatch = (error: AxiosError, key: string): boolean => {
- let errorMessage = '';
-
- if (error) {
- errorMessage = get(error, 'response.data.message', '');
- if (!errorMessage) {
- // if error text is undefined or null or empty, try responseMessage in data
- errorMessage = get(error, 'response.data.responseMessage', '');
- }
- if (!errorMessage) {
- errorMessage = get(error, 'response.data', '') as string;
- errorMessage = typeof errorMessage === 'string' ? errorMessage : '';
- }
- }
-
- return errorMessage.includes(key);
-};
-
/**
* @param color hex have color code
* @param opacity take opacity how much to reduce it
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CoverImageUploadUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CoverImageUploadUtils.tsx
index d7adcb088186..2888c29274aa 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CoverImageUploadUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/CoverImageUploadUtils.tsx
@@ -18,7 +18,7 @@ import imageClassBase from '../components/BlockEditor/Extensions/image/ImageClas
import { CoverImageFileValue } from '../components/common/CoverImageUpload/CoverImageUpload.interface';
import { ERROR_MESSAGE } from '../constants/constants';
import { EntityType } from '../enums/entity.enum';
-import { getIsErrorMatch } from './CommonUtils';
+import { getIsErrorMatch } from './APIUtils';
import {
showNotistackError,
showNotistackSuccess,
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx
index eba886ea6c43..713ebe74530f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx
@@ -12,7 +12,6 @@
*/
import Icon from '@ant-design/icons';
-import i18next from 'i18next';
import { capitalize, isUndefined, uniqBy, uniqueId } from 'lodash';
import { DOMAttributes } from 'react';
import { Layout } from 'react-grid-layout';
@@ -22,6 +21,7 @@ import { LandingPageWidgetKeys } from '../enums/CustomizablePage.enum';
import { Document } from '../generated/entity/docStore/document';
import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface';
import customizeMyDataPageClassBase from './CustomizeMyDataPageClassBase';
+import i18n from './i18next/LocalUtil';
/**
* Ensures widget width doesn't exceed the maximum allowed width of 2
@@ -371,11 +371,11 @@ export const getRemoveWidgetHandler =
export const getWidgetWidthLabelFromKey = (widgetKey: string): string => {
switch (widgetKey) {
case 'large':
- return i18next.t('label.large');
+ return i18n.t('label.large');
case 'medium':
- return i18next.t('label.medium');
+ return i18n.t('label.medium');
case 'small':
- return i18next.t('label.small');
+ return i18n.t('label.small');
default:
return capitalize(widgetKey);
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardServiceUtils.ts
index 5c71999498c6..8a1135061852 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardServiceUtils.ts
@@ -12,7 +12,7 @@
*/
import { cloneDeep, isEmpty, isUndefined } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import {
DashboardConnection,
DashboardServiceType,
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetSummaryPanelUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetSummaryPanelUtils.test.tsx
new file mode 100644
index 000000000000..cf4c15964c84
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetSummaryPanelUtils.test.tsx
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2026 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ExplorePageTabs } from '../enums/Explore.enum';
+import { MOCK_CHART_DATA } from '../mocks/Chart.mock';
+import { MOCK_TABLE, MOCK_TIER_DATA } from '../mocks/TableData.mock';
+import { getEntityOverview } from './DataAssetSummaryPanelUtils';
+import { getTierTags } from './TableUtils';
+
+jest.mock('./TableUtils', () => ({
+ getTierTags: jest.fn(),
+ getUsagePercentile: jest.fn().mockImplementation((value) => value + 'th'),
+}));
+
+jest.mock('./CommonUtils', () => ({
+ getPartialNameFromTableFQN: jest.fn().mockImplementation((value) => value),
+ getTableFQNFromColumnFQN: jest.fn().mockImplementation((value) => value),
+ formatNumberWithComma: jest.fn().mockImplementation((value) => value),
+}));
+
+describe('getEntityOverview', () => {
+ it('should call getChartOverview and get ChartData if ExplorePageTabs is charts', () => {
+ const result = JSON.stringify(
+ getEntityOverview(ExplorePageTabs.CHARTS, {
+ ...MOCK_CHART_DATA,
+ dataProducts: [],
+ })
+ );
+
+ expect(result).toContain('label.owner-plural');
+ expect(result).toContain('label.chart');
+ expect(result).toContain('label.url-uppercase');
+ expect(result).toContain('Are you an ethnic minority in your city?');
+ expect(result).toContain(
+ `http://localhost:8088/superset/explore/?form_data=%7B%22slice_id%22%3A%20127%7D`
+ );
+ expect(result).toContain('label.service');
+ expect(result).toContain('sample_superset');
+ expect(result).toContain('Other');
+ expect(result).toContain('label.service-type');
+ expect(result).toContain('Superset');
+ });
+
+ it('should call getChartOverview and get TableData if ExplorePageTabs is table', () => {
+ const result = JSON.stringify(
+ getEntityOverview(ExplorePageTabs.TABLES, {
+ ...MOCK_TABLE,
+ tags: [MOCK_TIER_DATA],
+ dataProducts: [],
+ })
+ );
+
+ expect(result).toContain('label.owner-plural');
+ expect(result).toContain('label.type');
+ expect(result).toContain('label.service');
+ expect(result).toContain('label.database');
+ expect(result).toContain('label.schema');
+ expect(result).toContain('label.tier');
+ expect(result).toContain('label.usage');
+ expect(result).toContain('label.query-plural');
+ expect(result).toContain('label.column-plural');
+ expect(result).toContain('label.row-plural');
+ expect(getTierTags).toHaveBeenCalledWith([MOCK_TIER_DATA]);
+ expect(result).toContain('Regular');
+ expect(result).toContain('sample_data');
+ expect(result).toContain('ecommerce_db');
+ expect(result).toContain('shopify');
+ expect(result).toContain('0th');
+ expect(result).toContain('4');
+ expect(result).toContain('14567');
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetSummaryPanelUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetSummaryPanelUtils.tsx
new file mode 100644
index 000000000000..f1771765fb2d
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetSummaryPanelUtils.tsx
@@ -0,0 +1,1417 @@
+/*
+ * Copyright 2026 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { isEmpty, isNil, isObject, isUndefined } from 'lodash';
+import { DomainLabel } from '../components/common/DomainLabel/DomainLabel.component';
+import { OwnerLabel } from '../components/common/OwnerLabel/OwnerLabel.component';
+import QueryCount from '../components/common/QueryCount/QueryCount.component';
+import { DataAssetSummaryPanelProps } from '../components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.interface';
+import { ProfilerTabPath } from '../components/Database/Profiler/ProfilerDashboard/profilerDashboard.interface';
+import { EntityServiceUnion } from '../components/Explore/ExplorePage.interface';
+import TagsV1 from '../components/Tag/TagsV1/TagsV1.component';
+import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
+import { NO_DATA } from '../constants/constants';
+import { TAG_START_WITH } from '../constants/Tag.constants';
+import { EntityTabs, EntityType, FqnPart } from '../enums/entity.enum';
+import { ExplorePageTabs } from '../enums/Explore.enum';
+import { ServiceCategory } from '../enums/service.enum';
+import { APICollection } from '../generated/entity/data/apiCollection';
+import { APIEndpoint } from '../generated/entity/data/apiEndpoint';
+import { Chart } from '../generated/entity/data/chart';
+import { Container } from '../generated/entity/data/container';
+import { Dashboard } from '../generated/entity/data/dashboard';
+import { DashboardDataModel } from '../generated/entity/data/dashboardDataModel';
+import { Database } from '../generated/entity/data/database';
+import { DatabaseSchema } from '../generated/entity/data/databaseSchema';
+import { Directory } from '../generated/entity/data/directory';
+import { File } from '../generated/entity/data/file';
+import { Metric } from '../generated/entity/data/metric';
+import { Mlmodel } from '../generated/entity/data/mlmodel';
+import { SearchIndex } from '../generated/entity/data/searchIndex';
+import { Spreadsheet } from '../generated/entity/data/spreadsheet';
+import {
+ StoredProcedure,
+ StoredProcedureCodeObject,
+} from '../generated/entity/data/storedProcedure';
+import { Table, TableType, TagLabel } from '../generated/entity/data/table';
+import { Topic } from '../generated/entity/data/topic';
+import { Worksheet } from '../generated/entity/data/worksheet';
+
+import { Pipeline } from '../generated/entity/data/pipeline';
+import { EntityReference } from '../generated/entity/type';
+import { UsageDetails } from '../generated/type/usageDetails';
+import {
+ formatNumberWithComma,
+ getPartialNameFromTableFQN,
+} from './CommonUtils';
+import { DRAWER_NAVIGATION_OPTIONS, getEntityName } from './EntityUtils';
+import { BasicEntityOverviewInfo } from './EntityUtils.interface';
+import i18n from './i18next/LocalUtil';
+import { getEntityDetailsPath, getServiceDetailsPath } from './RouterUtils';
+import { bytesToSize, stringToHTML } from './StringsUtils';
+import { getTierTags, getUsagePercentile } from './TableUtils';
+
+interface ColumnSearchResult {
+ dataType?: string;
+ dataTypeDisplay?: string;
+ constraint?: string;
+ table?: {
+ name?: string;
+ displayName?: string;
+ fullyQualifiedName?: string;
+ };
+ service?: {
+ name?: string;
+ displayName?: string;
+ fullyQualifiedName?: string;
+ type?: string;
+ };
+ database?: {
+ name?: string;
+ displayName?: string;
+ fullyQualifiedName?: string;
+ };
+ databaseSchema?: {
+ name?: string;
+ displayName?: string;
+ fullyQualifiedName?: string;
+ };
+ owners?: EntityReference[];
+ domains?: EntityReference[];
+}
+
+const entityTierRenderer = (tier?: TagLabel) => {
+ return tier ? (
+
+ ) : (
+ NO_DATA
+ );
+};
+
+const getUsageData = (usageSummary: UsageDetails | undefined) =>
+ isNil(usageSummary?.weeklyStats?.percentileRank)
+ ? NO_DATA
+ : getUsagePercentile(usageSummary?.weeklyStats?.percentileRank ?? 0);
+
+const getTableFieldsFromTableDetails = (tableDetails: Table) => {
+ const {
+ fullyQualifiedName,
+ owners,
+ tags,
+ usageSummary,
+ profile,
+ columns,
+ tableType,
+ service,
+ database,
+ databaseSchema,
+ domains,
+ } = tableDetails;
+ const [serviceName, databaseName, schemaName] = getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
+ FQN_SEPARATOR_CHAR
+ ).split(FQN_SEPARATOR_CHAR);
+
+ const serviceDisplayName = getEntityName(service) || serviceName;
+ const databaseDisplayName = getEntityName(database) || databaseName;
+ const schemaDisplayName = getEntityName(databaseSchema) || schemaName;
+
+ const tier = getTierTags(tags ?? []);
+
+ return {
+ fullyQualifiedName,
+ owners,
+ service: serviceDisplayName,
+ database: databaseDisplayName,
+ schema: schemaDisplayName,
+ tier,
+ usage: getUsageData(usageSummary),
+ profile,
+ columns,
+ tableType,
+ domains,
+ };
+};
+
+const getCommonOverview = (
+ {
+ owners,
+ domains,
+ }: {
+ owners?: EntityReference[];
+ domains?: EntityReference[];
+ },
+ showOwner = true
+) => {
+ return [
+ ...(showOwner
+ ? [
+ {
+ name: i18n.t('label.owner-plural'),
+ value: (
+
+ ),
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ ]
+ : []),
+ {
+ name: i18n.t('label.domain-plural'),
+ value: (
+
+ ),
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ ];
+};
+
+const getTableOverview = (
+ tableDetails: Table,
+ additionalInfo?: Record
+) => {
+ const {
+ fullyQualifiedName,
+ owners,
+ profile,
+ columns,
+ tableType,
+ service,
+ database,
+ schema,
+ tier,
+ usage,
+ domains,
+ } = getTableFieldsFromTableDetails(tableDetails);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.type'),
+ value: tableType ?? TableType.Regular,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: service || NO_DATA,
+ url: getServiceDetailsPath(service, ServiceCategory.DATABASE_SERVICES),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.database'),
+ value: database || NO_DATA,
+ url: getEntityDetailsPath(
+ EntityType.DATABASE,
+ getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database],
+ FQN_SEPARATOR_CHAR
+ )
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.schema'),
+ value: schema || NO_DATA,
+ url: getEntityDetailsPath(
+ EntityType.DATABASE_SCHEMA,
+ getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
+ FQN_SEPARATOR_CHAR
+ )
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.usage'),
+ value: usage || NO_DATA,
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.query-plural'),
+ value: ,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.column-plural'),
+ value: columns ? columns.length : NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.row-plural'),
+ value:
+ !isUndefined(profile) && profile?.rowCount
+ ? formatNumberWithComma(profile.rowCount)
+ : NO_DATA,
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.incident-plural'),
+ value: additionalInfo?.incidentCount ?? 0,
+ isLink: true,
+ linkProps: {
+ pathname: getEntityDetailsPath(
+ EntityType.TABLE,
+ fullyQualifiedName ?? '',
+ EntityTabs.PROFILER,
+ ProfilerTabPath.INCIDENTS
+ ),
+ },
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ ];
+
+ return overview;
+};
+
+const getTopicOverview = (topicDetails: Topic) => {
+ const {
+ domains,
+ partitions,
+ replicationFactor,
+ retentionSize,
+ cleanupPolicies,
+ maximumMessageSize,
+ messageSchema,
+ } = topicDetails;
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ domains, owners: topicDetails.owners }),
+ {
+ name: i18n.t('label.partition-plural'),
+ value: partitions ?? NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.replication-factor'),
+ value: replicationFactor,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.retention-size'),
+ value: bytesToSize(retentionSize ?? 0),
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.clean-up-policy-plural'),
+ value: cleanupPolicies ? cleanupPolicies.join(', ') : NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.max-message-size'),
+ value: bytesToSize(maximumMessageSize ?? 0),
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.schema-type'),
+ value: messageSchema?.schemaType ?? NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ ];
+
+ return overview;
+};
+
+const getPipelineOverview = (pipelineDetails: Pipeline) => {
+ const { owners, tags, sourceUrl, service, displayName, domains } =
+ pipelineDetails;
+ const tier = getTierTags(tags ?? []);
+ const serviceDisplayName = getEntityName(service);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: `${i18n.t('label.pipeline')} ${i18n.t('label.url-uppercase')}`,
+ dataTestId: 'pipeline-url-label',
+ value: stringToHTML(displayName ?? '') || NO_DATA,
+ url: sourceUrl,
+ isLink: true,
+ isExternal: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: serviceDisplayName || NO_DATA,
+ url: getServiceDetailsPath(
+ service?.name ?? '',
+ ServiceCategory.PIPELINE_SERVICES
+ ),
+ isLink: true,
+ isExternal: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ ];
+
+ return overview;
+};
+
+const getDashboardOverview = (dashboardDetails: Dashboard) => {
+ const { owners, tags, sourceUrl, service, displayName, project, domains } =
+ dashboardDetails;
+ const tier = getTierTags(tags ?? []);
+ const serviceDisplayName = getEntityName(service);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: `${i18n.t('label.dashboard')} ${i18n.t('label.url-uppercase')}`,
+ value: stringToHTML(displayName ?? '') || NO_DATA,
+ url: sourceUrl,
+ isLink: true,
+ isExternal: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: serviceDisplayName || NO_DATA,
+ url: getServiceDetailsPath(
+ service?.name ?? '',
+ ServiceCategory.DASHBOARD_SERVICES
+ ),
+ isExternal: false,
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ isExternal: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.project'),
+ value: project ?? NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ ];
+
+ return overview;
+};
+
+export const getSearchIndexOverview = (searchIndexDetails: SearchIndex) => {
+ const { owners, tags, service, domains } = searchIndexDetails;
+ const tier = getTierTags(tags ?? []);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ isExternal: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: service?.fullyQualifiedName ?? NO_DATA,
+ url: getServiceDetailsPath(
+ service?.name ?? '',
+ ServiceCategory.SEARCH_SERVICES
+ ),
+ isExternal: false,
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ ];
+
+ return overview;
+};
+
+const getMlModelOverview = (mlModelDetails: Mlmodel) => {
+ const { algorithm, target, server, dashboard, owners, domains } =
+ mlModelDetails;
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.algorithm'),
+ value: algorithm || NO_DATA,
+ url: '',
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.target'),
+ value: target ?? NO_DATA,
+ url: '',
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.server'),
+ value: server ?? NO_DATA,
+ url: server,
+ isLink: Boolean(server),
+ isExternal: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.dashboard'),
+ value: getEntityName(dashboard) || NO_DATA,
+ url: getEntityDetailsPath(
+ EntityType.DASHBOARD,
+ dashboard?.fullyQualifiedName ?? ''
+ ),
+ isLink: true,
+ isExternal: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ ];
+
+ return overview;
+};
+
+const getContainerOverview = (containerDetails: Container) => {
+ const { numberOfObjects, serviceType, dataModel, owners, domains } =
+ containerDetails;
+
+ const visible = [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ];
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.object-plural'),
+ value: numberOfObjects,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.service-type'),
+ value: serviceType,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.column-plural'),
+ value:
+ !isUndefined(dataModel) && dataModel.columns
+ ? dataModel.columns.length
+ : NO_DATA,
+ isLink: false,
+ visible,
+ },
+ ];
+
+ return overview;
+};
+
+const getChartOverview = (chartDetails: Chart) => {
+ const {
+ owners,
+ sourceUrl,
+ chartType,
+ service,
+ serviceType,
+ displayName,
+ domains,
+ } = chartDetails;
+ const serviceDisplayName = getEntityName(service);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: `${i18n.t('label.chart')} ${i18n.t('label.url-uppercase')}`,
+ value: stringToHTML(displayName ?? '') || NO_DATA,
+ url: sourceUrl,
+ isLink: true,
+ isExternal: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: serviceDisplayName || NO_DATA,
+ url: getServiceDetailsPath(
+ service?.name ?? '',
+ ServiceCategory.DASHBOARD_SERVICES
+ ),
+ isExternal: false,
+ isLink: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.chart-type'),
+ value: chartType ?? NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ {
+ name: i18n.t('label.service-type'),
+ value: serviceType ?? NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ ];
+
+ return overview;
+};
+
+const getDataModelOverview = (dataModelDetails: DashboardDataModel) => {
+ const {
+ owners,
+ tags,
+ service,
+ domains,
+ displayName,
+ dataModelType,
+ fullyQualifiedName,
+ } = dataModelDetails;
+ const tier = getTierTags(tags ?? []);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: `${i18n.t('label.data-model')} ${i18n.t('label.url-uppercase')}`,
+ value: stringToHTML(displayName ?? '') || NO_DATA,
+ url: getEntityDetailsPath(
+ EntityType.DASHBOARD_DATA_MODEL,
+ fullyQualifiedName ?? ''
+ ),
+ isLink: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: service?.fullyQualifiedName ?? NO_DATA,
+ url: getServiceDetailsPath(
+ service?.name ?? '',
+ ServiceCategory.DASHBOARD_SERVICES
+ ),
+ isExternal: false,
+ isLink: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ isExternal: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.data-model-type'),
+ value: dataModelType,
+ isLink: false,
+ isExternal: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ ];
+
+ return overview;
+};
+
+const getStoredProcedureOverview = (
+ storedProcedureDetails: StoredProcedure
+) => {
+ const { fullyQualifiedName, owners, tags, domains, storedProcedureCode } =
+ storedProcedureDetails;
+ const [service, database, schema] = getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
+ FQN_SEPARATOR_CHAR
+ ).split(FQN_SEPARATOR_CHAR);
+
+ const tier = getTierTags(tags ?? []);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.service'),
+ value: service || NO_DATA,
+ url: getServiceDetailsPath(service, ServiceCategory.DATABASE_SERVICES),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.database'),
+ value: database || NO_DATA,
+ url: getEntityDetailsPath(
+ EntityType.DATABASE,
+ getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database],
+ FQN_SEPARATOR_CHAR
+ )
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18n.t('label.schema'),
+ value: schema || NO_DATA,
+ url: getEntityDetailsPath(
+ EntityType.DATABASE_SCHEMA,
+ getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
+ FQN_SEPARATOR_CHAR
+ )
+ ),
+ isLink: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ ...(isObject(storedProcedureCode)
+ ? [
+ {
+ name: i18n.t('label.language'),
+ value:
+ (storedProcedureCode as StoredProcedureCodeObject).language ??
+ NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ ]
+ : []),
+ ];
+
+ return overview;
+};
+
+const getDatabaseOverview = (databaseDetails: Database) => {
+ const { owners, service, domains, tags, usageSummary } = databaseDetails;
+
+ const tier = getTierTags(tags ?? []);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ {
+ name: i18n.t('label.owner-plural'),
+ value: ,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ ...getCommonOverview({ domains }, false),
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: service?.fullyQualifiedName || NO_DATA,
+ url: getServiceDetailsPath(
+ service?.fullyQualifiedName ?? '',
+ ServiceCategory.DATABASE_SERVICES
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+
+ {
+ name: i18n.t('label.usage'),
+ value: getUsageData(usageSummary),
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ ];
+
+ return overview;
+};
+
+const getDatabaseSchemaOverview = (databaseSchemaDetails: DatabaseSchema) => {
+ const { owners, service, tags, domains, usageSummary, database } =
+ databaseSchemaDetails;
+
+ const tier = getTierTags(tags ?? []);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ {
+ name: i18n.t('label.owner-plural'),
+ value: ,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ ...getCommonOverview({ domains }, false),
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: service?.fullyQualifiedName ?? NO_DATA,
+ url: getServiceDetailsPath(
+ service?.fullyQualifiedName ?? '',
+ ServiceCategory.DATABASE_SERVICES
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ {
+ name: i18n.t('label.database'),
+ value: database?.fullyQualifiedName ?? NO_DATA,
+ url: getEntityDetailsPath(
+ EntityType.DATABASE,
+ database?.fullyQualifiedName ?? ''
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ {
+ name: i18n.t('label.usage'),
+ value: getUsageData(usageSummary),
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ ];
+
+ return overview;
+};
+
+const getEntityServiceOverview = (serviceDetails: EntityServiceUnion) => {
+ const { owners, domains, tags, serviceType } = serviceDetails;
+
+ const tier = getTierTags(tags ?? []);
+
+ const overview: BasicEntityOverviewInfo[] = [
+ {
+ name: i18n.t('label.owner-plural'),
+ value: ,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ ...getCommonOverview({ domains }, false),
+ {
+ name: i18n.t('label.tier'),
+ value: entityTierRenderer(tier),
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ {
+ name: i18n.t('label.service-type'),
+ value: serviceType,
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ ];
+
+ return overview;
+};
+
+const getApiCollectionOverview = (apiCollection: APICollection) => {
+ if (isNil(apiCollection) || isEmpty(apiCollection)) {
+ return [];
+ }
+
+ const { service, domains } = apiCollection;
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ domains }, false),
+ {
+ name: i18n.t('label.endpoint-url'),
+ value: apiCollection.endpointURL || NO_DATA,
+ url: apiCollection.endpointURL,
+ isLink: true,
+ isExternal: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: service?.fullyQualifiedName ?? NO_DATA,
+ url: getServiceDetailsPath(
+ service?.fullyQualifiedName ?? '',
+ ServiceCategory.API_SERVICES
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.explore],
+ },
+ ];
+
+ return overview;
+};
+const getApiEndpointOverview = (apiEndpoint: APIEndpoint) => {
+ if (isNil(apiEndpoint) || isEmpty(apiEndpoint)) {
+ return [];
+ }
+ const { service, apiCollection, domains } = apiEndpoint;
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ domains }, false),
+ {
+ name: i18n.t('label.endpoint-url'),
+ value: apiEndpoint.endpointURL || NO_DATA,
+ url: apiEndpoint.endpointURL,
+ isLink: true,
+ isExternal: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ {
+ name: i18n.t('label.api-collection'),
+ value: apiEndpoint.apiCollection?.fullyQualifiedName ?? '',
+ url: getEntityDetailsPath(
+ EntityType.API_COLLECTION,
+ apiCollection?.fullyQualifiedName ?? ''
+ ),
+ isLink: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: service?.fullyQualifiedName ?? '',
+ url: getServiceDetailsPath(
+ service?.fullyQualifiedName ?? '',
+ ServiceCategory.API_SERVICES
+ ),
+ isLink: true,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ {
+ name: i18n.t('label.request-method'),
+ value: apiEndpoint.requestMethod || NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ ];
+
+ return overview;
+};
+const getMetricOverview = (metric: Metric) => {
+ if (isNil(metric) || isEmpty(metric)) {
+ return [];
+ }
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ domains: metric.domains }, false),
+ {
+ name: i18n.t('label.metric-type'),
+ value: metric.metricType || NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ {
+ name: i18n.t('label.unit-of-measurement'),
+ value: metric.unitOfMeasurement || NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ {
+ name: i18n.t('label.granularity'),
+ value: metric.granularity || NO_DATA,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ ],
+ },
+ ];
+
+ return overview;
+};
+
+const getDirectoryOverview = (directoryDetails: Directory) => {
+ const {
+ numberOfSubDirectories,
+ numberOfFiles,
+ serviceType,
+ owners,
+ domains,
+ } = directoryDetails;
+
+ const visible = [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ];
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.directory-plural'),
+ value: numberOfSubDirectories ?? NO_DATA,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.file-plural'),
+ value: numberOfFiles ?? NO_DATA,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.service-type'),
+ value: serviceType,
+ isLink: false,
+ visible,
+ },
+ ];
+
+ return overview;
+};
+
+const getFileOverview = (fileDetails: File) => {
+ const { fileExtension, fileType, fileVersion, serviceType, owners, domains } =
+ fileDetails;
+
+ const visible = [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ];
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.file-extension'),
+ value: fileExtension ?? NO_DATA,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.file-type'),
+ value: fileType ?? NO_DATA,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.file-version'),
+ value: fileVersion ?? NO_DATA,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.service-type'),
+ value: serviceType,
+ isLink: false,
+ visible,
+ },
+ ];
+
+ return overview;
+};
+
+const getSpreadsheetOverview = (spreadsheetDetails: Spreadsheet) => {
+ const { fileVersion, serviceType, owners, domains } = spreadsheetDetails;
+
+ const visible = [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ];
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.file-version'),
+ value: fileVersion ?? NO_DATA,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.service-type'),
+ value: serviceType,
+ isLink: false,
+ visible,
+ },
+ ];
+
+ return overview;
+};
+
+const getWorksheetOverview = (worksheetDetails: Worksheet) => {
+ const { columnCount, rowCount, serviceType, owners, domains } =
+ worksheetDetails;
+
+ const visible = [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ];
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.column-plural'),
+ value: columnCount ?? NO_DATA,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.row-plural'),
+ value: rowCount ?? NO_DATA,
+ isLink: false,
+ visible,
+ },
+ {
+ name: i18n.t('label.service-type'),
+ value: serviceType,
+ isLink: false,
+ visible,
+ },
+ ];
+
+ return overview;
+};
+
+const getColumnOverview = (
+ columnDetails: ColumnSearchResult
+): BasicEntityOverviewInfo[] => {
+ const {
+ dataType,
+ dataTypeDisplay,
+ constraint,
+ table,
+ service,
+ database,
+ databaseSchema,
+ owners,
+ domains,
+ } = columnDetails;
+
+ const overview: BasicEntityOverviewInfo[] = [
+ ...getCommonOverview({ owners, domains }),
+ {
+ name: i18n.t('label.data-type'),
+ value: dataTypeDisplay || dataType || '--',
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.table'),
+ value: table?.displayName || table?.name || '--',
+ url: table?.fullyQualifiedName
+ ? getEntityDetailsPath(EntityType.TABLE, table.fullyQualifiedName)
+ : undefined,
+ isLink: !!table?.fullyQualifiedName,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.service'),
+ value: service?.displayName || service?.name || '--',
+ url: service?.fullyQualifiedName
+ ? getServiceDetailsPath(service.fullyQualifiedName, service.type || '')
+ : undefined,
+ isLink: !!service?.fullyQualifiedName,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.database'),
+ value: database?.displayName || database?.name || '--',
+ url: database?.fullyQualifiedName
+ ? getEntityDetailsPath(EntityType.DATABASE, database.fullyQualifiedName)
+ : undefined,
+ isLink: !!database?.fullyQualifiedName,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ {
+ name: i18n.t('label.schema'),
+ value: databaseSchema?.displayName || databaseSchema?.name || '--',
+ url: databaseSchema?.fullyQualifiedName
+ ? getEntityDetailsPath(
+ EntityType.DATABASE_SCHEMA,
+ databaseSchema.fullyQualifiedName
+ )
+ : undefined,
+ isLink: !!databaseSchema?.fullyQualifiedName,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ },
+ ];
+
+ if (constraint) {
+ overview.push({
+ name: i18n.t('label.constraint'),
+ value: constraint,
+ isLink: false,
+ visible: [
+ DRAWER_NAVIGATION_OPTIONS.lineage,
+ DRAWER_NAVIGATION_OPTIONS.explore,
+ ],
+ });
+ }
+
+ return overview;
+};
+
+export const getEntityOverview = (
+ type: string,
+ entityDetail: DataAssetSummaryPanelProps['dataAsset'],
+ additionalInfo?: Record
+): Array => {
+ switch (type) {
+ case ExplorePageTabs.TABLES:
+ case EntityType.TABLE: {
+ return getTableOverview(entityDetail as Table, additionalInfo);
+ }
+
+ case ExplorePageTabs.COLUMNS:
+ case EntityType.TABLE_COLUMN: {
+ return getColumnOverview(entityDetail as unknown as ColumnSearchResult);
+ }
+
+ case ExplorePageTabs.TOPICS:
+ case EntityType.TOPIC: {
+ return getTopicOverview(entityDetail as Topic);
+ }
+
+ case ExplorePageTabs.PIPELINES:
+ case EntityType.PIPELINE: {
+ return getPipelineOverview(entityDetail as Pipeline);
+ }
+
+ case ExplorePageTabs.DASHBOARDS:
+ case EntityType.DASHBOARD: {
+ return getDashboardOverview(entityDetail as Dashboard);
+ }
+
+ case ExplorePageTabs.SEARCH_INDEX:
+ case EntityType.SEARCH_INDEX: {
+ return getSearchIndexOverview(entityDetail as SearchIndex);
+ }
+
+ case ExplorePageTabs.MLMODELS:
+ case EntityType.MLMODEL: {
+ return getMlModelOverview(entityDetail as Mlmodel);
+ }
+ case ExplorePageTabs.CONTAINERS:
+ case EntityType.CONTAINER: {
+ return getContainerOverview(entityDetail as Container);
+ }
+ case ExplorePageTabs.CHARTS:
+ case EntityType.CHART: {
+ return getChartOverview(entityDetail as Chart);
+ }
+
+ case ExplorePageTabs.DASHBOARD_DATA_MODEL:
+ case EntityType.DASHBOARD_DATA_MODEL: {
+ return getDataModelOverview(entityDetail as DashboardDataModel);
+ }
+
+ case ExplorePageTabs.STORED_PROCEDURE:
+ case EntityType.STORED_PROCEDURE: {
+ return getStoredProcedureOverview(entityDetail as StoredProcedure);
+ }
+
+ case ExplorePageTabs.DATABASE:
+ case EntityType.DATABASE: {
+ return getDatabaseOverview(entityDetail as Database);
+ }
+
+ case ExplorePageTabs.DATABASE_SCHEMA:
+ case EntityType.DATABASE_SCHEMA: {
+ return getDatabaseSchemaOverview(entityDetail as DatabaseSchema);
+ }
+
+ case ExplorePageTabs.API_COLLECTION:
+ case EntityType.API_COLLECTION: {
+ return getApiCollectionOverview(entityDetail as APICollection);
+ }
+
+ case ExplorePageTabs.API_ENDPOINT:
+ case EntityType.API_ENDPOINT: {
+ return getApiEndpointOverview(entityDetail as APIEndpoint);
+ }
+
+ case ExplorePageTabs.METRIC:
+ case EntityType.METRIC: {
+ return getMetricOverview(entityDetail as Metric);
+ }
+
+ case ExplorePageTabs.DIRECTORIES:
+ case EntityType.DIRECTORY: {
+ return getDirectoryOverview(entityDetail as Directory);
+ }
+
+ case ExplorePageTabs.FILES:
+ case EntityType.FILE: {
+ return getFileOverview(entityDetail as unknown as File);
+ }
+
+ case ExplorePageTabs.SPREADSHEETS:
+ case EntityType.SPREADSHEET: {
+ return getSpreadsheetOverview(entityDetail as Spreadsheet);
+ }
+
+ case ExplorePageTabs.WORKSHEETS:
+ case EntityType.WORKSHEET: {
+ return getWorksheetOverview(entityDetail as Worksheet);
+ }
+
+ case ExplorePageTabs.DATABASE_SERVICE:
+ case ExplorePageTabs.MESSAGING_SERVICE:
+ case ExplorePageTabs.DASHBOARD_SERVICE:
+ case ExplorePageTabs.ML_MODEL_SERVICE:
+ case ExplorePageTabs.PIPELINE_SERVICE:
+ case ExplorePageTabs.SEARCH_INDEX_SERVICE:
+ case ExplorePageTabs.API_SERVICE:
+ case EntityType.DATABASE_SERVICE:
+ case EntityType.MESSAGING_SERVICE:
+ case EntityType.DASHBOARD_SERVICE:
+ case EntityType.MLMODEL_SERVICE:
+ case EntityType.PIPELINE_SERVICE:
+ case EntityType.SEARCH_SERVICE:
+ case EntityType.API_SERVICE: {
+ return getEntityServiceOverview(entityDetail as EntityServiceUnion);
+ }
+
+ default:
+ return [];
+ }
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
index ecdb3ad4c2cc..61df51113828 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
@@ -15,7 +15,6 @@
import Icon from '@ant-design/icons';
import { Divider, Tooltip, Typography } from 'antd';
import classNames from 'classnames';
-import { t } from 'i18next';
import { isArray, isEmpty, isObject, isUndefined } from 'lodash';
import React, { ReactNode } from 'react';
import { ReactComponent as IconExternalLink } from '../assets/svg/external-links.svg';
@@ -70,10 +69,13 @@ import {
getBreadcrumbForTable,
getEntityBreadcrumbs,
} from './EntityUtils';
+import i18n from './i18next/LocalUtil';
import { getEntityDetailsPath } from './RouterUtils';
import { bytesToSize } from './StringsUtils';
import { getUsagePercentile } from './TableUtils';
+const { t } = i18n;
+
export const ExtraInfoLabel = ({
label,
value,
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataContract/DataContractUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DataContract/DataContractUtils.test.ts
index 99ccde2b7bb2..79009802716d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DataContract/DataContractUtils.test.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataContract/DataContractUtils.test.ts
@@ -43,15 +43,6 @@ jest.mock('js-yaml', () => ({
dump: jest.fn((data) => JSON.stringify(data)),
}));
-jest.mock('../i18next/LocalUtil', () => ({
- __esModule: true,
- default: {
- t: jest.fn((key: string) => key),
- },
- t: jest.fn((key: string) => key),
- detectBrowserLanguage: jest.fn(() => 'en-US'),
-}));
-
// Import after mocks are set up
import { DataContractProcessedResultCharts } from '../../components/DataContract/ContractExecutionChart/ContractExecutionChart.interface';
import { DataContract } from '../../generated/entity/data/dataContract';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseServiceUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseServiceUtils.tsx
index f5a06466f765..0f0722346bb2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseServiceUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseServiceUtils.tsx
@@ -18,7 +18,7 @@ import { ReactComponent as ImportIcon } from '../assets/svg/ic-import.svg';
import { ManageButtonItemLabel } from '../components/common/ManageButtonContentItem/ManageButtonContentItem.component';
import { useEntityExportModalProvider } from '../components/Entity/EntityExportModalProvider/EntityExportModalProvider.component';
import { ExportTypes } from '../constants/Export.constants';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { OperationPermission } from '../context/PermissionProvider/PermissionProvider.interface';
import { EntityType } from '../enums/entity.enum';
import { DatabaseServiceType } from '../generated/entity/services/databaseService';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.test.tsx
index c13c9de444e3..643136ae8ea4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.test.tsx
@@ -10,18 +10,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { InternalAxiosRequestConfig } from 'axios';
-import { DEFAULT_DOMAIN_VALUE } from '../constants/constants';
import { EntityType } from '../enums/entity.enum';
-import { SearchIndex } from '../enums/search.enum';
import { Domain, DomainType } from '../generated/entity/domains/domain';
-import { useDomainStore } from '../hooks/useDomainStore';
import {
getQueryFilterToIncludeDomain,
isDomainExist,
- withDomainFilter,
} from '../utils/DomainUtils';
-import { getPathNameFromWindowLocation } from './RouterUtils';
jest.mock('../hooks/useDomainStore');
jest.mock('./RouterUtils');
@@ -260,460 +254,3 @@ describe('isDomainExist', () => {
});
});
});
-
-describe('withDomainFilter', () => {
- const mockGetState = jest.fn();
- const mockGetPathName = getPathNameFromWindowLocation as jest.Mock;
-
- beforeEach(() => {
- jest.clearAllMocks();
- (useDomainStore as unknown as jest.Mock).mockImplementation(() => ({
- getState: mockGetState,
- }));
- (useDomainStore.getState as jest.Mock) = mockGetState;
- });
-
- const createMockConfig = (
- method: string = 'get',
- url?: string,
- params?: Record
- ): InternalAxiosRequestConfig =>
- ({
- method,
- url,
- params,
- headers: {},
- } as InternalAxiosRequestConfig);
-
- describe('should not intercept requests', () => {
- it('should return config unchanged when path starts with /domain', () => {
- mockGetPathName.mockReturnValue('/domain/test');
- mockGetState.mockReturnValue({ activeDomain: 'testDomain' });
-
- const config = createMockConfig();
- const result = withDomainFilter(config);
-
- expect(result).toBe(config);
- expect(result.params).toBeUndefined();
- });
-
- it('should return config unchanged when path starts with /auth/logout', () => {
- mockGetPathName.mockReturnValue('/auth/logout');
- mockGetState.mockReturnValue({ activeDomain: 'testDomain' });
-
- const config = createMockConfig();
- const result = withDomainFilter(config);
-
- expect(result).toBe(config);
- expect(result.params).toBeUndefined();
- });
-
- it('should return config unchanged when path starts with /auth/refresh', () => {
- mockGetPathName.mockReturnValue('/auth/refresh');
- mockGetState.mockReturnValue({ activeDomain: 'testDomain' });
-
- const config = createMockConfig();
- const result = withDomainFilter(config);
-
- expect(result).toBe(config);
- expect(result.params).toBeUndefined();
- });
-
- it('should return config unchanged when method is not GET', () => {
- mockGetPathName.mockReturnValue('/api/test');
- mockGetState.mockReturnValue({ activeDomain: 'testDomain' });
-
- const config = createMockConfig('post');
- const result = withDomainFilter(config);
-
- expect(result).toBe(config);
- expect(result.params).toBeUndefined();
- });
-
- it('should return config unchanged when activeDomain is DEFAULT_DOMAIN_VALUE', () => {
- mockGetPathName.mockReturnValue('/api/test');
- mockGetState.mockReturnValue({ activeDomain: DEFAULT_DOMAIN_VALUE });
-
- const config = createMockConfig();
- const result = withDomainFilter(config);
-
- expect(result).toBe(config);
- expect(result.params).toBeUndefined();
- });
- });
-
- describe('regular GET requests', () => {
- it('should add domain parameter for regular GET requests with active domain', () => {
- mockGetPathName.mockReturnValue('/api/tables');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const config = createMockConfig('get', '/api/tables');
- const result = withDomainFilter(config);
-
- expect(result.params).toEqual({
- domain: 'engineering',
- });
- });
-
- it('should preserve existing params when adding domain parameter', () => {
- mockGetPathName.mockReturnValue('/api/tables');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const config = createMockConfig('get', '/api/tables', {
- limit: 10,
- offset: 0,
- });
- const result = withDomainFilter(config);
-
- expect(result.params).toEqual({
- limit: 10,
- offset: 0,
- domain: 'engineering',
- });
- });
- });
-
- describe('search query requests', () => {
- it('should add should filter with term and prefix for /search/query with active domain', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TABLE,
- });
- const result = withDomainFilter(config);
-
- expect(result.params).toHaveProperty('query_filter');
-
- const filter = JSON.parse(result.params?.query_filter as string);
-
- expect(filter).toEqual({
- query: {
- bool: {
- must: [
- {
- bool: {
- should: [
- {
- term: {
- 'domains.fullyQualifiedName': 'engineering',
- },
- },
- {
- prefix: {
- 'domains.fullyQualifiedName': 'engineering.',
- },
- },
- ],
- },
- },
- ],
- },
- },
- });
- });
-
- it('should return config unchanged for TAG index searches', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TAG,
- });
- const result = withDomainFilter(config);
-
- expect(result).toBe(config);
- expect(result.params?.query_filter).toBeUndefined();
- });
-
- it('should use fullyQualifiedName field for DOMAIN index searches', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.DOMAIN,
- });
- const result = withDomainFilter(config);
- const queryFilter = JSON.parse(result.params?.query_filter as string);
- const shouldClauses =
- queryFilter.query.bool.must[queryFilter.query.bool.must.length - 1].bool
- .should;
-
- expect(shouldClauses).toEqual([
- { term: { fullyQualifiedName: 'engineering' } },
- { prefix: { fullyQualifiedName: 'engineering.' } },
- ]);
- });
-
- it('should preserve existing query_filter and add should filter', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const existingFilter = {
- query: {
- bool: {
- must: [
- {
- term: {
- entityType: 'table',
- },
- },
- ],
- },
- },
- };
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TABLE,
- query_filter: JSON.stringify(existingFilter),
- });
- const result = withDomainFilter(config);
-
- const filter = JSON.parse(result.params?.query_filter as string);
-
- expect(filter.query.bool.must).toHaveLength(2);
- expect(filter.query.bool.must[0]).toEqual({
- term: {
- entityType: 'table',
- },
- });
- expect(filter.query.bool.must[1]).toEqual({
- bool: {
- should: [
- {
- term: {
- 'domains.fullyQualifiedName': 'engineering',
- },
- },
- {
- prefix: {
- 'domains.fullyQualifiedName': 'engineering.',
- },
- },
- ],
- },
- });
- });
-
- it('should handle invalid JSON in query_filter gracefully', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TABLE,
- query_filter: 'invalid-json',
- });
- const result = withDomainFilter(config);
-
- const filter = JSON.parse(result.params?.query_filter as string);
-
- expect(filter).toEqual({
- query: {
- bool: {
- must: [
- {
- bool: {
- should: [
- {
- term: {
- 'domains.fullyQualifiedName': 'engineering',
- },
- },
- {
- prefix: {
- 'domains.fullyQualifiedName': 'engineering.',
- },
- },
- ],
- },
- },
- ],
- },
- },
- });
- });
-
- it('should handle query_filter with empty must array', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const existingFilter = {
- query: {
- bool: {},
- },
- };
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TABLE,
- query_filter: JSON.stringify(existingFilter),
- });
- const result = withDomainFilter(config);
-
- const filter = JSON.parse(result.params?.query_filter as string);
-
- expect(filter.query.bool.must).toHaveLength(1);
- expect(filter.query.bool.must[0]).toEqual({
- bool: {
- should: [
- {
- term: {
- 'domains.fullyQualifiedName': 'engineering',
- },
- },
- {
- prefix: {
- 'domains.fullyQualifiedName': 'engineering.',
- },
- },
- ],
- },
- });
- });
-
- it('should handle empty object query_filter gracefully', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TABLE,
- query_filter: '{}',
- });
- const result = withDomainFilter(config);
-
- const filter = JSON.parse(result.params?.query_filter as string);
-
- expect(filter).toEqual({
- query: {
- bool: {
- must: [
- {
- bool: {
- should: [
- {
- term: {
- 'domains.fullyQualifiedName': 'engineering',
- },
- },
- {
- prefix: {
- 'domains.fullyQualifiedName': 'engineering.',
- },
- },
- ],
- },
- },
- ],
- },
- },
- });
- });
-
- it('should preserve existing params when adding query_filter', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TABLE,
- limit: 10,
- offset: 0,
- });
- const result = withDomainFilter(config);
-
- expect(result.params).toHaveProperty('index', SearchIndex.TABLE);
- expect(result.params).toHaveProperty('limit', 10);
- expect(result.params).toHaveProperty('offset', 0);
- expect(result.params).toHaveProperty('query_filter');
- });
-
- it('should preserve non-bool top-level clauses when adding domain filter', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({ activeDomain: 'engineering' });
-
- const existingFilter = JSON.stringify({
- query: {
- term: { 'some.field': 'someValue' },
- bool: { must: [{ term: { 'other.field': 'otherValue' } }] },
- },
- });
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TABLE,
- query_filter: existingFilter,
- });
- const result = withDomainFilter(config);
-
- const parsed = JSON.parse(result.params?.query_filter as string);
-
- expect(parsed.query.bool.must).toContainEqual({
- term: { 'some.field': 'someValue' },
- });
- expect(parsed.query.bool.must).toContainEqual({
- term: { 'other.field': 'otherValue' },
- });
- expect(parsed.query.bool.must).toContainEqual({
- bool: {
- should: [
- {
- term: {
- 'domains.fullyQualifiedName': 'engineering',
- },
- },
- {
- prefix: {
- 'domains.fullyQualifiedName': 'engineering.',
- },
- },
- ],
- },
- });
- expect(parsed.query.bool.must).toHaveLength(3);
- });
- });
-
- describe('nested domain paths', () => {
- it('should handle nested domain paths correctly', () => {
- mockGetPathName.mockReturnValue('/api/tables');
- mockGetState.mockReturnValue({
- activeDomain: 'engineering.backend.services',
- });
-
- const config = createMockConfig('get', '/api/tables');
- const result = withDomainFilter(config);
-
- expect(result.params).toEqual({
- domain: 'engineering.backend.services',
- });
- });
-
- it('should add should filter with nested domain for search queries', () => {
- mockGetPathName.mockReturnValue('/api/search');
- mockGetState.mockReturnValue({
- activeDomain: 'engineering.backend.services',
- });
-
- const config = createMockConfig('get', '/search/query', {
- index: SearchIndex.TABLE,
- });
- const result = withDomainFilter(config);
-
- const filter = JSON.parse(result.params?.query_filter as string);
-
- expect(filter.query.bool.must[0]).toEqual({
- bool: {
- should: [
- {
- term: {
- 'domains.fullyQualifiedName': 'engineering.backend.services',
- },
- },
- {
- prefix: {
- 'domains.fullyQualifiedName': 'engineering.backend.services.',
- },
- },
- ],
- },
- });
- });
- });
-});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx
index 388c9fbc25dd..2934d81423d2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx
@@ -13,7 +13,6 @@
import { Tooltip, TooltipTrigger } from '@openmetadata/ui-core-components';
import { InfoCircle } from '@untitledui/icons';
import { Divider, Space, Tooltip as AntDTooltip, Typography } from 'antd';
-import { InternalAxiosRequestConfig } from 'axios';
import classNames from 'classnames';
import { get, isEmpty, isUndefined, noop } from 'lodash';
import { Fragment, ReactNode } from 'react';
@@ -36,20 +35,14 @@ import SubDomainsTable from '../components/Domain/SubDomainsTable/SubDomainsTabl
import EntitySummaryPanel from '../components/Explore/EntitySummaryPanel/EntitySummaryPanel.component';
import AssetsTabs from '../components/Glossary/GlossaryTerms/tabs/AssetsTabs.component';
import { AssetsOfEntity } from '../components/Glossary/GlossaryTerms/tabs/AssetsTabs.interface';
-import {
- DEFAULT_DOMAIN_VALUE,
- DE_ACTIVE_COLOR,
- NO_DATA_PLACEHOLDER,
-} from '../constants/constants';
+import { DE_ACTIVE_COLOR, NO_DATA_PLACEHOLDER } from '../constants/constants';
import { DOMAIN_TYPE_DATA } from '../constants/Domain.constants';
import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
import { EntityTabs, EntityType } from '../enums/entity.enum';
-import { SearchIndex } from '../enums/search.enum';
import { Domain } from '../generated/entity/domains/domain';
import { Operation } from '../generated/entity/policies/policy';
import { EntityReference } from '../generated/entity/type';
import { PageType } from '../generated/system/ui/page';
-import { useDomainStore } from '../hooks/useDomainStore';
import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface';
import {
QueryFieldInterface,
@@ -64,99 +57,7 @@ import {
getPrioritizedEditPermission,
getPrioritizedViewPermission,
} from './PermissionsUtils';
-import { getDomainPath, getPathNameFromWindowLocation } from './RouterUtils';
-
-export const withDomainFilter = (
- config: InternalAxiosRequestConfig
-): InternalAxiosRequestConfig => {
- const isGetRequest = config.method === 'get';
- const activeDomain = useDomainStore.getState().activeDomain;
- const hasActiveDomain = activeDomain !== DEFAULT_DOMAIN_VALUE;
- const currentPath = getPathNameFromWindowLocation();
- const shouldNotIntercept = [
- '/domain',
- '/auth/logout',
- '/auth/refresh',
- ].reduce((prev, curr) => {
- return prev || currentPath.startsWith(curr);
- }, false);
-
- if (shouldNotIntercept) {
- return config;
- }
-
- if (isGetRequest && hasActiveDomain) {
- if (config.url?.includes('/search/query')) {
- if (config.params?.index === SearchIndex.TAG) {
- return config;
- }
-
- const domainFilterField =
- config.params?.index === SearchIndex.DOMAIN
- ? 'fullyQualifiedName'
- : 'domains.fullyQualifiedName';
- let filter: QueryFilterInterface = { query: { bool: {} } };
- if (config.params?.query_filter) {
- try {
- const parsed = JSON.parse(config.params.query_filter as string);
- filter = parsed?.query ? parsed : { query: { bool: {} } };
- } catch {
- filter = { query: { bool: {} } };
- }
- }
-
- let mustArray: QueryFieldInterface[] = [];
- const existingMust = filter.query?.bool?.must;
- if (Array.isArray(existingMust)) {
- mustArray = [...existingMust];
- } else if (existingMust) {
- mustArray = [existingMust];
- }
-
- const { bool: existingBool, ...nonBoolClauses } = filter.query ?? {};
- for (const [key, value] of Object.entries(nonBoolClauses)) {
- mustArray.push({ [key]: value } as QueryFieldInterface);
- }
-
- filter.query = {
- bool: {
- ...existingBool,
- must: [
- ...mustArray,
- {
- bool: {
- should: [
- {
- term: {
- [domainFilterField]: activeDomain,
- },
- },
- {
- prefix: {
- [domainFilterField]: `${activeDomain}.`,
- },
- },
- ],
- },
- } as QueryFieldInterface,
- ],
- },
- };
-
- config.params = {
- ...config.params,
- query_filter: JSON.stringify(filter),
- };
- } else {
- config.params = {
- ...config.params,
- domain: activeDomain,
- };
- }
- }
-
- return config;
-};
+import { getDomainPath } from './RouterUtils';
export const getOwner = (
hasPermission: boolean,
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DriveServiceUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DriveServiceUtils.test.ts
index 7fd96a15bc82..c22ecf825695 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DriveServiceUtils.test.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DriveServiceUtils.test.ts
@@ -12,7 +12,7 @@
*/
import { cloneDeep } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { DriveServiceType } from '../generated/entity/services/driveService';
import customDriveConnection from '../jsons/connectionSchemas/connections/drive/customDriveConnection.json';
import googleDriveConnection from '../jsons/connectionSchemas/connections/drive/googleDriveConnection.json';
@@ -22,7 +22,7 @@ jest.mock('lodash', () => ({
cloneDeep: jest.fn(),
}));
-jest.mock('../constants/Services.constant', () => ({
+jest.mock('../constants/ServiceUISchema.constant', () => ({
COMMON_UI_SCHEMA: {
connection: {
'ui:field': 'collapsible',
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DriveServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DriveServiceUtils.ts
index dcdc1b20def6..d66f9e87c768 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DriveServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DriveServiceUtils.ts
@@ -12,7 +12,7 @@
*/
import { cloneDeep } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { DriveServiceType } from '../generated/entity/services/driveService';
import customDriveConnection from '../jsons/connectionSchemas/connections/drive/customDriveConnection.json';
import googleDriveConnection from '../jsons/connectionSchemas/connections/drive/googleDriveConnection.json';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx
index 599f427825e8..99f91c8f4432 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx
@@ -14,11 +14,8 @@ import { render } from '@testing-library/react';
import { startCase } from 'lodash';
import { DEFAULT_DOMAIN_VALUE } from '../constants/constants';
import { EntityTabs, EntityType } from '../enums/entity.enum';
-import { ExplorePageTabs } from '../enums/Explore.enum';
import { ServiceCategory } from '../enums/service.enum';
import { TestSuite } from '../generated/tests/testCase';
-import { MOCK_CHART_DATA } from '../mocks/Chart.mock';
-import { MOCK_TABLE, MOCK_TIER_DATA } from '../mocks/TableData.mock';
import {
columnSorter,
getBreadcrumbForTestSuite,
@@ -26,7 +23,6 @@ import {
getDomainDisplayName,
getEntityBreadcrumbs,
getEntityLinkFromType,
- getEntityOverview,
hasCustomPropertiesTab,
hasLineageTab,
hasSchemaTab,
@@ -55,7 +51,6 @@ import {
getSettingPath,
} from './RouterUtils';
import { getServiceRouteFromServiceType } from './ServiceUtils';
-import { getTierTags } from './TableUtils';
jest.mock('../constants/constants', () => ({
DEFAULT_DOMAIN_VALUE: 'All Domains',
@@ -218,59 +213,6 @@ describe('EntityUtils unit tests', () => {
});
});
- describe('getEntityOverview', () => {
- it('should call getChartOverview and get ChartData if ExplorePageTabs is charts', () => {
- const result = JSON.stringify(
- getEntityOverview(ExplorePageTabs.CHARTS, {
- ...MOCK_CHART_DATA,
- dataProducts: [],
- })
- );
-
- expect(result).toContain('label.owner-plural');
- expect(result).toContain('label.chart');
- expect(result).toContain('label.url-uppercase');
- expect(result).toContain('Are you an ethnic minority in your city?');
- expect(result).toContain(
- `http://localhost:8088/superset/explore/?form_data=%7B%22slice_id%22%3A%20127%7D`
- );
- expect(result).toContain('label.service');
- expect(result).toContain('sample_superset');
- expect(result).toContain('Other');
- expect(result).toContain('label.service-type');
- expect(result).toContain('Superset');
- });
-
- it('should call getChartOverview and get TableData if ExplorePageTabs is table', () => {
- const result = JSON.stringify(
- getEntityOverview(ExplorePageTabs.TABLES, {
- ...MOCK_TABLE,
- tags: [MOCK_TIER_DATA],
- dataProducts: [],
- })
- );
-
- expect(result).toContain('label.owner-plural');
- expect(result).toContain('label.type');
- expect(result).toContain('label.service');
- expect(result).toContain('label.database');
- expect(result).toContain('label.schema');
- expect(result).toContain('label.tier');
- expect(result).toContain('label.usage');
- expect(result).toContain('label.query-plural');
- expect(result).toContain('label.column-plural');
- expect(result).toContain('label.row-plural');
- expect(getTierTags).toHaveBeenCalledWith([MOCK_TIER_DATA]);
- expect(result).toContain('Regular');
- expect(result).toContain('sample_data');
- expect(result).toContain('ecommerce_db');
- expect(result).toContain('shopify');
- expect(result).toContain('0th');
- expect(result).toContain('4');
- expect(result).toContain('14567');
- });
- });
-
describe('getColumnSorter', () => {
type TestType = { name: string };
@@ -635,10 +577,10 @@ describe('EntityUtils unit tests', () => {
expect(result).toBe('Engineering');
});
- it('should return translated "All Domains" when activeDomain is DEFAULT_DOMAIN_VALUE', () => {
+ it('should return translated "label.all-domain-plural" when activeDomain is DEFAULT_DOMAIN_VALUE', () => {
const result = getDomainDisplayName(undefined, DEFAULT_DOMAIN_VALUE);
- expect(result).toBe('All Domains');
+ expect(result).toBe('label.all-domain-plural');
});
it('should return custom domain name when activeDomain is a custom value', () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
index 8f41b86d50a3..069ed1b32baf 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
@@ -12,45 +12,27 @@
*/
import { Popover, Space, Typography } from 'antd';
-import i18next, { t } from 'i18next';
-import {
- isEmpty,
- isNil,
- isObject,
- isUndefined,
- lowerCase,
- startCase,
-} from 'lodash';
+import { isEmpty, isUndefined, lowerCase, startCase } from 'lodash';
import { EntityDetailUnion } from 'Models';
import { Fragment } from 'react';
import { Link } from 'react-router-dom';
import { Node } from 'reactflow';
-import { DomainLabel } from '../components/common/DomainLabel/DomainLabel.component';
-import { OwnerLabel } from '../components/common/OwnerLabel/OwnerLabel.component';
-import QueryCount from '../components/common/QueryCount/QueryCount.component';
import { TitleLink } from '../components/common/TitleBreadcrumb/TitleBreadcrumb.interface';
import { DataAssetsWithoutServiceField } from '../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface';
-import { DataAssetSummaryPanelProps } from '../components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.interface';
-import { ProfilerTabPath } from '../components/Database/Profiler/ProfilerDashboard/profilerDashboard.interface';
import { QueryVoteType } from '../components/Database/TableQueries/TableQueries.interface';
import {
CUSTOM_PROPERTIES_TABS_SET,
LINEAGE_TABS_SET,
SCHEMA_TABS_SET,
} from '../components/Entity/EntityRightPanel/EntityRightPanelVerticalNav.constants';
-import {
- EntityServiceUnion,
- EntityWithServices,
-} from '../components/Explore/ExplorePage.interface';
+import { EntityWithServices } from '../components/Explore/ExplorePage.interface';
import {
SearchedDataProps,
SourceType,
} from '../components/SearchedData/SearchedData.interface';
-import TagsV1 from '../components/Tag/TagsV1/TagsV1.component';
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
import {
DEFAULT_DOMAIN_VALUE,
- NO_DATA,
PLACEHOLDER_ROUTE_ENTITY_TYPE,
PLACEHOLDER_ROUTE_FQN,
ROUTES,
@@ -59,14 +41,12 @@ import {
GlobalSettingOptions,
GlobalSettingsMenuCategory,
} from '../constants/GlobalSettings.constants';
-import { TAG_START_WITH } from '../constants/Tag.constants';
import {
EntityLineageNodeType,
EntityTabs,
EntityType,
FqnPart,
} from '../enums/entity.enum';
-import { ExplorePageTabs } from '../enums/Explore.enum';
import { ServiceCategory, ServiceCategoryPlural } from '../enums/service.enum';
import { Kpi } from '../generated/dataInsight/kpi/kpi';
import { Classification } from '../generated/entity/classification/classification';
@@ -75,32 +55,23 @@ import { APICollection } from '../generated/entity/data/apiCollection';
import { APIEndpoint } from '../generated/entity/data/apiEndpoint';
import { Chart } from '../generated/entity/data/chart';
import { Container } from '../generated/entity/data/container';
-import { Dashboard } from '../generated/entity/data/dashboard';
import { DashboardDataModel } from '../generated/entity/data/dashboardDataModel';
import { Database } from '../generated/entity/data/database';
import { DatabaseSchema } from '../generated/entity/data/databaseSchema';
import { Directory } from '../generated/entity/data/directory';
import { File } from '../generated/entity/data/file';
import { GlossaryTerm } from '../generated/entity/data/glossaryTerm';
-import { Metric } from '../generated/entity/data/metric';
-import { Mlmodel } from '../generated/entity/data/mlmodel';
-import { Pipeline } from '../generated/entity/data/pipeline';
import {
SearchIndex as SearchIndexAsset,
- SearchIndex as SearchIndexEntity,
SearchIndexField,
} from '../generated/entity/data/searchIndex';
import { Spreadsheet } from '../generated/entity/data/spreadsheet';
-import {
- StoredProcedure,
- StoredProcedureCodeObject,
-} from '../generated/entity/data/storedProcedure';
+import { StoredProcedure } from '../generated/entity/data/storedProcedure';
import {
Column,
ColumnJoins,
JoinedWith,
Table,
- TableType,
} from '../generated/entity/data/table';
import { Topic } from '../generated/entity/data/topic';
import { Worksheet } from '../generated/entity/data/worksheet';
@@ -113,7 +84,6 @@ import {
import { TestCase, TestSuite } from '../generated/tests/testCase';
import { EntityReference } from '../generated/type/entityUsage';
import { TagLabel } from '../generated/type/tagLabel';
-import { UsageDetails } from '../generated/type/usageDetails';
import { Votes } from '../generated/type/votes';
import { DataInsightTabs } from '../interface/data-insight.interface';
import {
@@ -122,14 +92,13 @@ import {
} from '../interface/search.interface';
import { DataQualityPageTabs } from '../pages/DataQuality/DataQualityPage.interface';
import {
- formatNumberWithComma,
getPartialNameFromTableFQN,
getTableFQNFromColumnFQN,
} from './CommonUtils';
import { getDataInsightPathWithFqn } from './DataInsightUtils';
import EntityLink from './EntityLink';
-import { BasicEntityOverviewInfo } from './EntityUtils.interface';
import Fqn from './Fqn';
+import i18n from './i18next/LocalUtil';
import {
getApplicationDetailsPath,
getBotsPagePath,
@@ -155,15 +124,12 @@ import {
getTestCaseDetailPagePath,
} from './RouterUtils';
import { getServiceRouteFromServiceType } from './ServiceUtils';
-import { bytesToSize, getEncodedFqn, stringToHTML } from './StringsUtils';
-import {
- getDataTypeString,
- getTagsWithoutTier,
- getTierTags,
- getUsagePercentile,
-} from './TableUtils';
+import { getEncodedFqn } from './StringsUtils';
+import { getDataTypeString, getTagsWithoutTier } from './TableUtils';
import { getTableTags } from './TagsUtils';
+const { t } = i18n;
+
export enum DRAWER_NAVIGATION_OPTIONS {
explore = 'Explore',
lineage = 'Lineage',
@@ -239,1370 +205,6 @@ export const getEntityTags = (
}
};
-const entityTierRenderer = (tier?: TagLabel) => {
- return tier ? (
-
- ) : (
- NO_DATA
- );
-};
-
-const getUsageData = (usageSummary: UsageDetails | undefined) =>
- !isNil(usageSummary?.weeklyStats?.percentileRank)
- ? getUsagePercentile(usageSummary?.weeklyStats?.percentileRank ?? 0)
- : NO_DATA;
-
-const getTableFieldsFromTableDetails = (tableDetails: Table) => {
- const {
- fullyQualifiedName,
- owners,
- tags,
- usageSummary,
- profile,
- columns,
- tableType,
- service,
- database,
- databaseSchema,
- domains,
- } = tableDetails;
- const [serviceName, databaseName, schemaName] = getPartialNameFromTableFQN(
- fullyQualifiedName ?? '',
- [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
- FQN_SEPARATOR_CHAR
- ).split(FQN_SEPARATOR_CHAR);
-
- const serviceDisplayName = getEntityName(service) || serviceName;
- const databaseDisplayName = getEntityName(database) || databaseName;
- const schemaDisplayName = getEntityName(databaseSchema) || schemaName;
-
- const tier = getTierTags(tags ?? []);
-
- return {
- fullyQualifiedName,
- owners,
- service: serviceDisplayName,
- database: databaseDisplayName,
- schema: schemaDisplayName,
- tier,
- usage: getUsageData(usageSummary),
- profile,
- columns,
- tableType,
- domains,
- };
-};
-
-const getCommonOverview = (
- {
- owners,
- domains,
- }: {
- owners?: EntityReference[];
- domains?: EntityReference[];
- },
- showOwner = true
-) => {
- return [
- ...(showOwner
- ? [
- {
- name: i18next.t('label.owner-plural'),
- value: (
-
- ),
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- ]
- : []),
- {
- name: i18next.t('label.domain-plural'),
- value: (
-
- ),
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- ];
-};
-
-interface ColumnSearchResult {
- dataType?: string;
- dataTypeDisplay?: string;
- constraint?: string;
- table?: {
- name?: string;
- displayName?: string;
- fullyQualifiedName?: string;
- };
- service?: {
- name?: string;
- displayName?: string;
- fullyQualifiedName?: string;
- type?: string;
- };
- database?: {
- name?: string;
- displayName?: string;
- fullyQualifiedName?: string;
- };
- databaseSchema?: {
- name?: string;
- displayName?: string;
- fullyQualifiedName?: string;
- };
- owners?: EntityReference[];
- domains?: EntityReference[];
-}
-
-const getColumnOverview = (
- columnDetails: ColumnSearchResult
-): BasicEntityOverviewInfo[] => {
- const {
- dataType,
- dataTypeDisplay,
- constraint,
- table,
- service,
- database,
- databaseSchema,
- owners,
- domains,
- } = columnDetails;
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.data-type'),
- value: dataTypeDisplay || dataType || '--',
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.table'),
- value: table?.displayName || table?.name || '--',
- url: table?.fullyQualifiedName
- ? getEntityDetailsPath(EntityType.TABLE, table.fullyQualifiedName)
- : undefined,
- isLink: !!table?.fullyQualifiedName,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.service'),
- value: service?.displayName || service?.name || '--',
- url: service?.fullyQualifiedName
- ? getServiceDetailsPath(service.fullyQualifiedName, service.type || '')
- : undefined,
- isLink: !!service?.fullyQualifiedName,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.database'),
- value: database?.displayName || database?.name || '--',
- url: database?.fullyQualifiedName
- ? getEntityDetailsPath(EntityType.DATABASE, database.fullyQualifiedName)
- : undefined,
- isLink: !!database?.fullyQualifiedName,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.schema'),
- value: databaseSchema?.displayName || databaseSchema?.name || '--',
- url: databaseSchema?.fullyQualifiedName
- ? getEntityDetailsPath(
- EntityType.DATABASE_SCHEMA,
- databaseSchema.fullyQualifiedName
- )
- : undefined,
- isLink: !!databaseSchema?.fullyQualifiedName,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- ];
-
- if (constraint) {
- overview.push({
- name: i18next.t('label.constraint'),
- value: constraint,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- });
- }
-
- return overview;
-};
-
-const getTableOverview = (
- tableDetails: Table,
- additionalInfo?: Record
-) => {
- const {
- fullyQualifiedName,
- owners,
- profile,
- columns,
- tableType,
- service,
- database,
- schema,
- tier,
- usage,
- domains,
- } = getTableFieldsFromTableDetails(tableDetails);
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.type'),
- value: tableType ?? TableType.Regular,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.service'),
- value: service || NO_DATA,
- url: getServiceDetailsPath(service, ServiceCategory.DATABASE_SERVICES),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.database'),
- value: database || NO_DATA,
- url: getEntityDetailsPath(
- EntityType.DATABASE,
- getPartialNameFromTableFQN(
- fullyQualifiedName ?? '',
- [FqnPart.Service, FqnPart.Database],
- FQN_SEPARATOR_CHAR
- )
- ),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.schema'),
- value: schema || NO_DATA,
- url: getEntityDetailsPath(
- EntityType.DATABASE_SCHEMA,
- getPartialNameFromTableFQN(
- fullyQualifiedName ?? '',
- [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
- FQN_SEPARATOR_CHAR
- )
- ),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.usage'),
- value: usage || NO_DATA,
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.query-plural'),
- value: ,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.column-plural'),
- value: columns ? columns.length : NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.row-plural'),
- value:
- !isUndefined(profile) && profile?.rowCount
- ? formatNumberWithComma(profile.rowCount)
- : NO_DATA,
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.incident-plural'),
- value: additionalInfo?.incidentCount ?? 0,
- isLink: true,
- linkProps: {
- pathname: getEntityDetailsPath(
- EntityType.TABLE,
- fullyQualifiedName ?? '',
- EntityTabs.PROFILER,
- ProfilerTabPath.INCIDENTS
- ),
- },
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- ];
-
- return overview;
-};
-
-const getTopicOverview = (topicDetails: Topic) => {
- const {
- domains,
- partitions,
- replicationFactor,
- retentionSize,
- cleanupPolicies,
- maximumMessageSize,
- messageSchema,
- } = topicDetails;
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ domains, owners: topicDetails.owners }),
- {
- name: i18next.t('label.partition-plural'),
- value: partitions ?? NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.replication-factor'),
- value: replicationFactor,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.retention-size'),
- value: bytesToSize(retentionSize ?? 0),
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.clean-up-policy-plural'),
- value: cleanupPolicies ? cleanupPolicies.join(', ') : NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.max-message-size'),
- value: bytesToSize(maximumMessageSize ?? 0),
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.schema-type'),
- value: messageSchema?.schemaType ?? NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- ];
-
- return overview;
-};
-
-const getPipelineOverview = (pipelineDetails: Pipeline) => {
- const { owners, tags, sourceUrl, service, displayName, domains } =
- pipelineDetails;
- const tier = getTierTags(tags ?? []);
- const serviceDisplayName = getEntityName(service);
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: `${i18next.t('label.pipeline')} ${i18next.t(
- 'label.url-uppercase'
- )}`,
- dataTestId: 'pipeline-url-label',
- value: stringToHTML(displayName ?? '') || NO_DATA,
- url: sourceUrl,
- isLink: true,
- isExternal: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.service'),
- value: serviceDisplayName || NO_DATA,
- url: getServiceDetailsPath(
- service?.name ?? '',
- ServiceCategory.PIPELINE_SERVICES
- ),
- isLink: true,
- isExternal: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- ];
-
- return overview;
-};
-
-const getDashboardOverview = (dashboardDetails: Dashboard) => {
- const { owners, tags, sourceUrl, service, displayName, project, domains } =
- dashboardDetails;
- const tier = getTierTags(tags ?? []);
- const serviceDisplayName = getEntityName(service);
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: `${i18next.t('label.dashboard')} ${i18next.t(
- 'label.url-uppercase'
- )}`,
- value: stringToHTML(displayName ?? '') || NO_DATA,
- url: sourceUrl,
- isLink: true,
- isExternal: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.service'),
- value: serviceDisplayName || NO_DATA,
- url: getServiceDetailsPath(
- service?.name ?? '',
- ServiceCategory.DASHBOARD_SERVICES
- ),
- isExternal: false,
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- isExternal: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.project'),
- value: project ?? NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- ];
-
- return overview;
-};
-
-export const getSearchIndexOverview = (
- searchIndexDetails: SearchIndexEntity
-) => {
- const { owners, tags, service, domains } = searchIndexDetails;
- const tier = getTierTags(tags ?? []);
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- isExternal: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.service'),
- value: service?.fullyQualifiedName ?? NO_DATA,
- url: getServiceDetailsPath(
- service?.name ?? '',
- ServiceCategory.SEARCH_SERVICES
- ),
- isExternal: false,
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- ];
-
- return overview;
-};
-
-const getMlModelOverview = (mlModelDetails: Mlmodel) => {
- const { algorithm, target, server, dashboard, owners, domains } =
- mlModelDetails;
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.algorithm'),
- value: algorithm || NO_DATA,
- url: '',
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.target'),
- value: target ?? NO_DATA,
- url: '',
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.server'),
- value: server ?? NO_DATA,
- url: server,
- isLink: Boolean(server),
- isExternal: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.dashboard'),
- value: getEntityName(dashboard) || NO_DATA,
- url: getEntityDetailsPath(
- EntityType.DASHBOARD,
- dashboard?.fullyQualifiedName ?? ''
- ),
- isLink: true,
- isExternal: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- ];
-
- return overview;
-};
-
-const getContainerOverview = (containerDetails: Container) => {
- const { numberOfObjects, serviceType, dataModel, owners, domains } =
- containerDetails;
-
- const visible = [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ];
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.object-plural'),
- value: numberOfObjects,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.service-type'),
- value: serviceType,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.column-plural'),
- value:
- !isUndefined(dataModel) && dataModel.columns
- ? dataModel.columns.length
- : NO_DATA,
- isLink: false,
- visible,
- },
- ];
-
- return overview;
-};
-
-const getChartOverview = (chartDetails: Chart) => {
- const {
- owners,
- sourceUrl,
- chartType,
- service,
- serviceType,
- displayName,
- domains,
- } = chartDetails;
- const serviceDisplayName = getEntityName(service);
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: `${i18next.t('label.chart')} ${i18next.t('label.url-uppercase')}`,
- value: stringToHTML(displayName ?? '') || NO_DATA,
- url: sourceUrl,
- isLink: true,
- isExternal: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.service'),
- value: serviceDisplayName || NO_DATA,
- url: getServiceDetailsPath(
- service?.name ?? '',
- ServiceCategory.DASHBOARD_SERVICES
- ),
- isExternal: false,
- isLink: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.chart-type'),
- value: chartType ?? NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- {
- name: i18next.t('label.service-type'),
- value: serviceType ?? NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- ];
-
- return overview;
-};
-
-const getDataModelOverview = (dataModelDetails: DashboardDataModel) => {
- const {
- owners,
- tags,
- service,
- domains,
- displayName,
- dataModelType,
- fullyQualifiedName,
- } = dataModelDetails;
- const tier = getTierTags(tags ?? []);
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: `${i18next.t('label.data-model')} ${i18next.t(
- 'label.url-uppercase'
- )}`,
- value: stringToHTML(displayName ?? '') || NO_DATA,
- url: getEntityDetailsPath(
- EntityType.DASHBOARD_DATA_MODEL,
- fullyQualifiedName ?? ''
- ),
- isLink: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.service'),
- value: service?.fullyQualifiedName ?? NO_DATA,
- url: getServiceDetailsPath(
- service?.name ?? '',
- ServiceCategory.DASHBOARD_SERVICES
- ),
- isExternal: false,
- isLink: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
-
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- isExternal: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.data-model-type'),
- value: dataModelType,
- isLink: false,
- isExternal: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- ];
-
- return overview;
-};
-
-const getStoredProcedureOverview = (
- storedProcedureDetails: StoredProcedure
-) => {
- const { fullyQualifiedName, owners, tags, domains, storedProcedureCode } =
- storedProcedureDetails;
- const [service, database, schema] = getPartialNameFromTableFQN(
- fullyQualifiedName ?? '',
- [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
- FQN_SEPARATOR_CHAR
- ).split(FQN_SEPARATOR_CHAR);
-
- const tier = getTierTags(tags ?? []);
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.service'),
- value: service || NO_DATA,
- url: getServiceDetailsPath(service, ServiceCategory.DATABASE_SERVICES),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.database'),
- value: database || NO_DATA,
- url: getEntityDetailsPath(
- EntityType.DATABASE,
- getPartialNameFromTableFQN(
- fullyQualifiedName ?? '',
- [FqnPart.Service, FqnPart.Database],
- FQN_SEPARATOR_CHAR
- )
- ),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
- },
- {
- name: i18next.t('label.schema'),
- value: schema || NO_DATA,
- url: getEntityDetailsPath(
- EntityType.DATABASE_SCHEMA,
- getPartialNameFromTableFQN(
- fullyQualifiedName ?? '',
- [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
- FQN_SEPARATOR_CHAR
- )
- ),
- isLink: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- ...(isObject(storedProcedureCode)
- ? [
- {
- name: i18next.t('label.language'),
- value:
- (storedProcedureCode as StoredProcedureCodeObject).language ??
- NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ],
- },
- ]
- : []),
- ];
-
- return overview;
-};
-
-const getDatabaseOverview = (databaseDetails: Database) => {
- const { owners, service, domains, tags, usageSummary } = databaseDetails;
-
- const tier = getTierTags(tags ?? []);
-
- const overview: BasicEntityOverviewInfo[] = [
- {
- name: i18next.t('label.owner-plural'),
- value: ,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- ...getCommonOverview({ domains }, false),
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- {
- name: i18next.t('label.service'),
- value: service?.fullyQualifiedName || NO_DATA,
- url: getServiceDetailsPath(
- service?.fullyQualifiedName ?? '',
- ServiceCategory.DATABASE_SERVICES
- ),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
-
- {
- name: i18next.t('label.usage'),
- value: getUsageData(usageSummary),
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- ];
-
- return overview;
-};
-
-const getDatabaseSchemaOverview = (databaseSchemaDetails: DatabaseSchema) => {
- const { owners, service, tags, domains, usageSummary, database } =
- databaseSchemaDetails;
-
- const tier = getTierTags(tags ?? []);
-
- const overview: BasicEntityOverviewInfo[] = [
- {
- name: i18next.t('label.owner-plural'),
- value: ,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- ...getCommonOverview({ domains }, false),
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- {
- name: i18next.t('label.service'),
- value: service?.fullyQualifiedName ?? NO_DATA,
- url: getServiceDetailsPath(
- service?.fullyQualifiedName ?? '',
- ServiceCategory.DATABASE_SERVICES
- ),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- {
- name: i18next.t('label.database'),
- value: database?.fullyQualifiedName ?? NO_DATA,
- url: getEntityDetailsPath(
- EntityType.DATABASE,
- database?.fullyQualifiedName ?? ''
- ),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- {
- name: i18next.t('label.usage'),
- value: getUsageData(usageSummary),
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- ];
-
- return overview;
-};
-
-const getEntityServiceOverview = (serviceDetails: EntityServiceUnion) => {
- const { owners, domains, tags, serviceType } = serviceDetails;
-
- const tier = getTierTags(tags ?? []);
-
- const overview: BasicEntityOverviewInfo[] = [
- {
- name: i18next.t('label.owner-plural'),
- value: ,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- ...getCommonOverview({ domains }, false),
- {
- name: i18next.t('label.tier'),
- value: entityTierRenderer(tier),
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- {
- name: i18next.t('label.service-type'),
- value: serviceType,
- isLink: false,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- ];
-
- return overview;
-};
-
-const getApiCollectionOverview = (apiCollection: APICollection) => {
- if (isNil(apiCollection) || isEmpty(apiCollection)) {
- return [];
- }
-
- const { service, domains } = apiCollection;
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ domains }, false),
- {
- name: i18next.t('label.endpoint-url'),
- value: apiCollection.endpointURL || NO_DATA,
- url: apiCollection.endpointURL,
- isLink: true,
- isExternal: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- {
- name: i18next.t('label.service'),
- value: service?.fullyQualifiedName ?? NO_DATA,
- url: getServiceDetailsPath(
- service?.fullyQualifiedName ?? '',
- ServiceCategory.API_SERVICES
- ),
- isLink: true,
- visible: [DRAWER_NAVIGATION_OPTIONS.explore],
- },
- ];
-
- return overview;
-};
-const getApiEndpointOverview = (apiEndpoint: APIEndpoint) => {
- if (isNil(apiEndpoint) || isEmpty(apiEndpoint)) {
- return [];
- }
- const { service, apiCollection, domains } = apiEndpoint;
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ domains }, false),
- {
- name: i18next.t('label.endpoint-url'),
- value: apiEndpoint.endpointURL || NO_DATA,
- url: apiEndpoint.endpointURL,
- isLink: true,
- isExternal: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- {
- name: i18next.t('label.api-collection'),
- value: apiEndpoint.apiCollection?.fullyQualifiedName ?? '',
- url: getEntityDetailsPath(
- EntityType.API_COLLECTION,
- apiCollection?.fullyQualifiedName ?? ''
- ),
- isLink: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- {
- name: i18next.t('label.service'),
- value: service?.fullyQualifiedName ?? '',
- url: getServiceDetailsPath(
- service?.fullyQualifiedName ?? '',
- ServiceCategory.API_SERVICES
- ),
- isLink: true,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- {
- name: i18next.t('label.request-method'),
- value: apiEndpoint.requestMethod || NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- ];
-
- return overview;
-};
-const getMetricOverview = (metric: Metric) => {
- if (isNil(metric) || isEmpty(metric)) {
- return [];
- }
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ domains: metric.domains }, false),
- {
- name: i18next.t('label.metric-type'),
- value: metric.metricType || NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- {
- name: i18next.t('label.unit-of-measurement'),
- value: metric.unitOfMeasurement || NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- {
- name: i18next.t('label.granularity'),
- value: metric.granularity || NO_DATA,
- isLink: false,
- visible: [
- DRAWER_NAVIGATION_OPTIONS.explore,
- DRAWER_NAVIGATION_OPTIONS.lineage,
- ],
- },
- ];
-
- return overview;
-};
-
-const getDirectoryOverview = (directoryDetails: Directory) => {
- const {
- numberOfSubDirectories,
- numberOfFiles,
- serviceType,
- owners,
- domains,
- } = directoryDetails;
-
- const visible = [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ];
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.directory-plural'),
- value: numberOfSubDirectories ?? NO_DATA,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.file-plural'),
- value: numberOfFiles ?? NO_DATA,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.service-type'),
- value: serviceType,
- isLink: false,
- visible,
- },
- ];
-
- return overview;
-};
-
-const getFileOverview = (fileDetails: File) => {
- const { fileExtension, fileType, fileVersion, serviceType, owners, domains } =
- fileDetails;
-
- const visible = [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ];
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.file-extension'),
- value: fileExtension ?? NO_DATA,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.file-type'),
- value: fileType ?? NO_DATA,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.file-version'),
- value: fileVersion ?? NO_DATA,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.service-type'),
- value: serviceType,
- isLink: false,
- visible,
- },
- ];
-
- return overview;
-};
-
-const getSpreadsheetOverview = (spreadsheetDetails: Spreadsheet) => {
- const { fileVersion, serviceType, owners, domains } = spreadsheetDetails;
-
- const visible = [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ];
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.file-version'),
- value: fileVersion ?? NO_DATA,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.service-type'),
- value: serviceType,
- isLink: false,
- visible,
- },
- ];
-
- return overview;
-};
-
-const getWorksheetOverview = (worksheetDetails: Worksheet) => {
- const { columnCount, rowCount, serviceType, owners, domains } =
- worksheetDetails;
-
- const visible = [
- DRAWER_NAVIGATION_OPTIONS.lineage,
- DRAWER_NAVIGATION_OPTIONS.explore,
- ];
-
- const overview: BasicEntityOverviewInfo[] = [
- ...getCommonOverview({ owners, domains }),
- {
- name: i18next.t('label.column-plural'),
- value: columnCount ?? NO_DATA,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.row-plural'),
- value: rowCount ?? NO_DATA,
- isLink: false,
- visible,
- },
- {
- name: i18next.t('label.service-type'),
- value: serviceType,
- isLink: false,
- visible,
- },
- ];
-
- return overview;
-};
-
-export const getEntityOverview = (
- type: string,
- entityDetail: DataAssetSummaryPanelProps['dataAsset'],
- additionalInfo?: Record
-): Array => {
- switch (type) {
- case ExplorePageTabs.TABLES:
- case EntityType.TABLE: {
- return getTableOverview(entityDetail as Table, additionalInfo);
- }
-
- case ExplorePageTabs.COLUMNS:
- case EntityType.TABLE_COLUMN: {
- return getColumnOverview(entityDetail as unknown as ColumnSearchResult);
- }
-
- case ExplorePageTabs.TOPICS:
- case EntityType.TOPIC: {
- return getTopicOverview(entityDetail as Topic);
- }
-
- case ExplorePageTabs.PIPELINES:
- case EntityType.PIPELINE: {
- return getPipelineOverview(entityDetail as Pipeline);
- }
-
- case ExplorePageTabs.DASHBOARDS:
- case EntityType.DASHBOARD: {
- return getDashboardOverview(entityDetail as Dashboard);
- }
-
- case ExplorePageTabs.SEARCH_INDEX:
- case EntityType.SEARCH_INDEX: {
- return getSearchIndexOverview(entityDetail as SearchIndexEntity);
- }
-
- case ExplorePageTabs.MLMODELS:
- case EntityType.MLMODEL: {
- return getMlModelOverview(entityDetail as Mlmodel);
- }
- case ExplorePageTabs.CONTAINERS:
- case EntityType.CONTAINER: {
- return getContainerOverview(entityDetail as Container);
- }
- case ExplorePageTabs.CHARTS:
- case EntityType.CHART: {
- return getChartOverview(entityDetail as Chart);
- }
-
- case ExplorePageTabs.DASHBOARD_DATA_MODEL:
- case EntityType.DASHBOARD_DATA_MODEL: {
- return getDataModelOverview(entityDetail as DashboardDataModel);
- }
-
- case ExplorePageTabs.STORED_PROCEDURE:
- case EntityType.STORED_PROCEDURE: {
- return getStoredProcedureOverview(entityDetail as StoredProcedure);
- }
-
- case ExplorePageTabs.DATABASE:
- case EntityType.DATABASE: {
- return getDatabaseOverview(entityDetail as Database);
- }
-
- case ExplorePageTabs.DATABASE_SCHEMA:
- case EntityType.DATABASE_SCHEMA: {
- return getDatabaseSchemaOverview(entityDetail as DatabaseSchema);
- }
-
- case ExplorePageTabs.API_COLLECTION:
- case EntityType.API_COLLECTION: {
- return getApiCollectionOverview(entityDetail as APICollection);
- }
-
- case ExplorePageTabs.API_ENDPOINT:
- case EntityType.API_ENDPOINT: {
- return getApiEndpointOverview(entityDetail as APIEndpoint);
- }
-
- case ExplorePageTabs.METRIC:
- case EntityType.METRIC: {
- return getMetricOverview(entityDetail as Metric);
- }
-
- case ExplorePageTabs.DIRECTORIES:
- case EntityType.DIRECTORY: {
- return getDirectoryOverview(entityDetail as Directory);
- }
-
- case ExplorePageTabs.FILES:
- case EntityType.FILE: {
- return getFileOverview(entityDetail as File);
- }
-
- case ExplorePageTabs.SPREADSHEETS:
- case EntityType.SPREADSHEET: {
- return getSpreadsheetOverview(entityDetail as Spreadsheet);
- }
-
- case ExplorePageTabs.WORKSHEETS:
- case EntityType.WORKSHEET: {
- return getWorksheetOverview(entityDetail as Worksheet);
- }
-
- case ExplorePageTabs.DATABASE_SERVICE:
- case ExplorePageTabs.MESSAGING_SERVICE:
- case ExplorePageTabs.DASHBOARD_SERVICE:
- case ExplorePageTabs.ML_MODEL_SERVICE:
- case ExplorePageTabs.PIPELINE_SERVICE:
- case ExplorePageTabs.SEARCH_INDEX_SERVICE:
- case ExplorePageTabs.API_SERVICE:
- case EntityType.DATABASE_SERVICE:
- case EntityType.MESSAGING_SERVICE:
- case EntityType.DASHBOARD_SERVICE:
- case EntityType.MLMODEL_SERVICE:
- case EntityType.PIPELINE_SERVICE:
- case EntityType.SEARCH_SERVICE:
- case EntityType.API_SERVICE: {
- return getEntityServiceOverview(entityDetail as EntityServiceUnion);
- }
-
- default:
- return [];
- }
-};
-
export const ENTITY_LINK_SEPARATOR = '::';
export const getEntityFeedLink = (
@@ -1687,7 +289,7 @@ export const checkIfJoinsAvailable = (
return (
joins &&
Boolean(joins.length) &&
- Boolean(joins.find((join) => join.columnName === columnName))
+ Boolean(joins.some((join) => join.columnName === columnName))
);
};
@@ -2145,7 +747,7 @@ export function getBreadcrumbForEntityWithParent<
export const getBreadcrumbForTestCase = (entity: TestCase): TitleLink[] => [
{
- name: i18next.t('label.data-quality'),
+ name: i18n.t('label.data-quality'),
url: `${ROUTES.DATA_QUALITY}/${DataQualityPageTabs.TEST_CASES}`,
},
{
@@ -2158,7 +760,7 @@ export const getBreadcrumbForTestCase = (entity: TestCase): TitleLink[] => [
state: {
breadcrumbData: [
{
- name: i18next.t('label.data-quality'),
+ name: i18n.t('label.data-quality'),
url: `${ROUTES.DATA_QUALITY}/${DataQualityPageTabs.TEST_CASES}`,
},
],
@@ -2197,7 +799,7 @@ export const getBreadcrumbForTestSuite = (entity: TestSuite) => {
export const getBreadCrumbForKpi = (entity: Kpi) => {
return [
{
- name: i18next.t('label.kpi-uppercase'),
+ name: i18n.t('label.kpi-uppercase'),
url: getDataInsightPathWithFqn(DataInsightTabs.KPIS),
},
{
@@ -2525,7 +1127,7 @@ export const getEntityBreadcrumbs = (
case EntityType.DOMAIN:
return [
{
- name: i18next.t('label.domain-plural'),
+ name: i18n.t('label.domain-plural'),
url: getDomainPath(),
},
];
@@ -2602,7 +1204,7 @@ export const getEntityBreadcrumbs = (
case EntityType.APPLICATION: {
return [
{
- name: i18next.t('label.application-plural'),
+ name: i18n.t('label.application-plural'),
url: getSettingPath(GlobalSettingsMenuCategory.APPLICATIONS),
},
{
@@ -2615,7 +1217,7 @@ export const getEntityBreadcrumbs = (
case EntityType.PERSONA: {
return [
{
- name: i18next.t('label.persona-plural'),
+ name: i18n.t('label.persona-plural'),
url: getSettingPath(
GlobalSettingsMenuCategory.MEMBERS,
GlobalSettingOptions.PERSONA
@@ -2631,7 +1233,7 @@ export const getEntityBreadcrumbs = (
case EntityType.ROLE: {
return [
{
- name: i18next.t('label.role-plural'),
+ name: i18n.t('label.role-plural'),
url: getSettingPath(
GlobalSettingsMenuCategory.ACCESS,
GlobalSettingOptions.ROLES
@@ -2647,7 +1249,7 @@ export const getEntityBreadcrumbs = (
case EntityType.POLICY: {
return [
{
- name: i18next.t('label.policy-plural'),
+ name: i18n.t('label.policy-plural'),
url: getSettingPath(
GlobalSettingsMenuCategory.ACCESS,
GlobalSettingOptions.POLICIES
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx
index a2c50628c7ba..40e34a29e593 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx
@@ -62,7 +62,6 @@ import {
getPartialNameFromFQN,
getPartialNameFromTableFQN,
getRandomColor,
- Transi18next,
} from './CommonUtils';
import { getRelativeCalendar } from './date-time/DateTimeUtils';
import EntityLink from './EntityLink';
@@ -73,7 +72,7 @@ import {
getEntityName,
} from './EntityUtils';
import Fqn from './Fqn';
-import { t } from './i18next/LocalUtil';
+import { t, Transi18next } from './i18next/LocalUtil';
import {
getImageWithResolutionAndFallback,
ImageQuality,
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/FileDetailsUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/FileDetailsUtils.test.tsx
index 8a202c0a9dab..5d6ddb29fa86 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/FileDetailsUtils.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/FileDetailsUtils.test.tsx
@@ -48,11 +48,6 @@ jest.mock('../components/DataAssets/CommonWidgets/CommonWidgets', () => ({
)),
}));
-jest.mock('../utils/i18next/LocalUtil', () => ({
- t: (key: string) => key,
- detectBrowserLanguage: jest.fn().mockReturnValue('en-US'),
-}));
-
jest.mock('../components/DataContract/ContractTab/ContractTab.tsx', () => {
return jest.fn().mockImplementation(() => DataContractComponent
);
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Fqn.ts b/openmetadata-ui/src/main/resources/ui/src/utils/Fqn.ts
index 2b9924e7a073..2fe0430f7676 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/Fqn.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/Fqn.ts
@@ -13,10 +13,10 @@
import antlr4 from 'antlr4';
import { ParseTreeWalker } from 'antlr4/src/antlr4/tree';
-import i18next from 'i18next';
import SplitListener from '../antlr/SplitListener';
import FqnLexer from '../generated/antlr/FqnLexer';
import FqnParser from '../generated/antlr/FqnParser';
+import i18n from './i18next/LocalUtil';
export default class Fqn {
// Equivalent of Java's FullyQualifiedName#split
@@ -45,8 +45,8 @@ export default class Fqn {
// Equivalent of Java's FullyQualifiedName#quoteName
static quoteName(name: string) {
const matcher = /^(")([^"]+)(")$|^(.*)$/.exec(name);
- if (!matcher || matcher[0].length !== name.length) {
- throw new Error(`${i18next.t('label.invalid-name')} ${name}`);
+ if (matcher?.[0].length !== name.length) {
+ throw new Error(`${i18n.t('label.invalid-name')} ${name}`);
}
// Name matches quoted string "sss".
@@ -64,6 +64,6 @@ export default class Fqn {
return unquotedName.includes('.') ? '"' + name + '"' : unquotedName;
}
- throw new Error(`${i18next.t('label.invalid-name')} ${name}`);
+ throw new Error(`${i18n.t('label.invalid-name')} ${name}`);
}
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsUtils.tsx
index 90e3d8bf8c7f..07ea3e8934be 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsUtils.tsx
@@ -11,7 +11,6 @@
* limitations under the License.
*/
-import i18next from 'i18next';
import { PLACEHOLDER_ROUTE_FQN, ROUTES } from '../constants/constants';
import {
GlobalSettingOptions,
@@ -19,6 +18,7 @@ import {
} from '../constants/GlobalSettings.constants';
import { EntityType } from '../enums/entity.enum';
import globalSettingsClassBase from './GlobalSettingsClassBase';
+import i18n from './i18next/LocalUtil';
import { getSettingPath } from './RouterUtils';
import { getEncodedFqn } from './StringsUtils';
@@ -113,7 +113,7 @@ export const getSettingPageEntityBreadCrumb = (
return [
{
- name: i18next.t('label.setting-plural'),
+ name: i18n.t('label.setting-plural'),
url: ROUTES.SETTINGS,
},
{
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/HistoryUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/HistoryUtils.ts
index 53130b85b4d7..174ae3ce877b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/HistoryUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/HistoryUtils.ts
@@ -11,7 +11,7 @@
* limitations under the License.
*/
export const getBasePath = () => {
- return window.BASE_PATH !== '${basePath}'
- ? window.BASE_PATH?.slice(0, -1) ?? ''
- : '';
+ return globalThis.BASE_PATH === '${basePath}'
+ ? ''
+ : globalThis.BASE_PATH?.slice(0, -1) ?? '';
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/IngestionUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/IngestionUtils.tsx
index 4a269eb07ec4..9ada1f5f2789 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/IngestionUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/IngestionUtils.tsx
@@ -47,8 +47,7 @@ import {
} from '../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { SearchSourceAlias } from '../interface/search.interface';
import { DataObj, ServicesType } from '../interface/service.interface';
-import { Transi18next } from './CommonUtils';
-import i18n from './i18next/LocalUtil';
+import i18n, { Transi18next } from './i18next/LocalUtil';
import { getSchemaByWorkflowType } from './IngestionWorkflowUtils';
import {
getServiceDetailsPath,
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/LocationUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/LocationUtils.ts
new file mode 100644
index 000000000000..eac49336ce74
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/LocationUtils.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2026 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { getBasePath } from './HistoryUtils';
+
+export const getPathNameFromWindowLocation = () => {
+ return globalThis.location.pathname.replace(getBasePath() ?? '', '');
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/MessagingServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/MessagingServiceUtils.ts
index c435fa95110a..dced426db1f3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/MessagingServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/MessagingServiceUtils.ts
@@ -12,7 +12,7 @@
*/
import { cloneDeep, isUndefined } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import {
MessagingConnection,
MessagingServiceType,
@@ -31,7 +31,7 @@ export const getBrokers = (config: MessagingConnection['config']) => {
retVal = config.bootstrapServers;
}
- return !isUndefined(retVal) ? retVal : '--';
+ return isUndefined(retVal) ? '--' : retVal;
};
export const getMessagingConfig = (type: MessagingServiceType) => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/MetadataServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/MetadataServiceUtils.ts
index e20dd14cdc03..d1aa2a63a098 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/MetadataServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/MetadataServiceUtils.ts
@@ -12,7 +12,7 @@
*/
import { cloneDeep } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { MetadataServiceType } from '../generated/entity/services/metadataService';
import alationSinkConnection from '../jsons/connectionSchemas/connections/metadata/alationSinkConnection.json';
import amundsenConnection from '../jsons/connectionSchemas/connections/metadata/amundsenConnection.json';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/MlmodelServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/MlmodelServiceUtils.ts
index 3b3f1e9ea748..7a9f59498b66 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/MlmodelServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/MlmodelServiceUtils.ts
@@ -12,7 +12,7 @@
*/
import { cloneDeep } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { MlModelServiceType } from '../generated/entity/services/mlmodelService';
import customMlModelConnection from '../jsons/connectionSchemas/connections/mlmodel/customMlModelConnection.json';
import mlflowConnection from '../jsons/connectionSchemas/connections/mlmodel/mlflowConnection.json';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/OktaCustomStorage.ts b/openmetadata-ui/src/main/resources/ui/src/utils/OktaCustomStorage.ts
index ffe838858ffc..f63481b108ad 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/OktaCustomStorage.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/OktaCustomStorage.ts
@@ -11,7 +11,7 @@
* limitations under the License.
*/
-import { StorageProvider } from '@okta/okta-auth-js';
+import type { StorageProvider } from '@okta/okta-auth-js';
import { swTokenStorage } from './SwTokenStorage';
import { isServiceWorkerAvailable } from './SwTokenStorageUtils';
@@ -19,8 +19,8 @@ const OKTA_TOKENS_KEY = 'okta_tokens';
export class OktaCustomStorage implements StorageProvider {
private memoryCache: Record = {};
- private isServiceWorkerAvailable: boolean;
- private initPromise: Promise;
+ private readonly isServiceWorkerAvailable: boolean;
+ private readonly initPromise: Promise;
constructor() {
this.isServiceWorkerAvailable = isServiceWorkerAvailable();
@@ -66,9 +66,9 @@ export class OktaCustomStorage implements StorageProvider {
return this.memoryCache[key] || null;
}
- async setItem(key: string, value: string): Promise {
+ setItem(key: string, value: string) {
this.memoryCache[key] = value;
- await this.persistToStorage();
+ this.persistToStorage();
}
removeItem(key: string): void {
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/PipelineServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/PipelineServiceUtils.ts
index cfa7e7c42b21..5b36af83834a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/PipelineServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/PipelineServiceUtils.ts
@@ -12,7 +12,7 @@
*/
import { cloneDeep } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { PipelineServiceType } from '../generated/entity/services/pipelineService';
import airbyteConnection from '../jsons/connectionSchemas/connections/pipeline/airbyteConnection.json';
import airflowConnection from '../jsons/connectionSchemas/connections/pipeline/airflowConnection.json';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts
index 8f276dbc3a08..426666848f6b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts
@@ -51,7 +51,6 @@ import { useMarketplaceStore } from '../hooks/useMarketplaceStore';
import { DataQualityPageTabs } from '../pages/DataQuality/DataQualityPage.interface';
import { TestCasePageTabs } from '../pages/IncidentManager/IncidentManager.interface';
import { getPartialNameFromFQN } from './CommonUtils';
-import { getBasePath } from './HistoryUtils';
import { getServiceRouteFromServiceType } from './ServiceUtils';
import { getEncodedFqn } from './StringsUtils';
@@ -706,9 +705,6 @@ export const getNotificationAlertDetailsPath = (fqn: string, tab?: string) => {
return path;
};
-export const getPathNameFromWindowLocation = () => {
- return window.location.pathname.replace(getBasePath() ?? '', '');
-};
export const getTagsDetailsPath = (entityFQN: string) => {
let path = ROUTES.TAG_DETAILS;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SchedularUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SchedularUtils.tsx
index 59da299ce37c..61508f3e9d6d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/SchedularUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/SchedularUtils.tsx
@@ -13,7 +13,6 @@
import { Select } from 'antd';
import cronstrue from 'cronstrue/i18n';
-import { t } from 'i18next';
import { isUndefined, toNumber, toString } from 'lodash';
import { RuleObject } from 'rc-field-form/es/interface';
import {
@@ -36,6 +35,7 @@ import {
} from '../constants/Schedular.constants';
import { CronTypes } from '../enums/Schedular.enum';
import { FieldTypes, FormItemLayout } from '../interface/FormUtils.interface';
+import i18n from './i18next/LocalUtil';
export const getScheduleOptionsFromSchedules = (
scheduleOptions: string[]
@@ -303,29 +303,35 @@ export const cronValidator = async (_: RuleObject, value: string) => {
// Check if the cron expression has exactly 5 fields (standard Unix cron)
if (cronParts.length !== 5) {
- return Promise.reject(new Error(t('message.cron-invalid-field-count')));
+ return Promise.reject(
+ new Error(i18n.t('message.cron-invalid-field-count'))
+ );
}
// Validate that each field follows standard Unix cron format
const [minute, hour, dayOfMonth, month, dayOfWeek] = cronParts;
if (!MINUTE_PATTERN.test(minute)) {
- return Promise.reject(new Error(t('message.cron-invalid-minute-field')));
+ return Promise.reject(
+ new Error(i18n.t('message.cron-invalid-minute-field'))
+ );
}
if (!HOUR_PATTERN.test(hour)) {
- return Promise.reject(new Error(t('message.cron-invalid-hour-field')));
+ return Promise.reject(new Error(i18n.t('message.cron-invalid-hour-field')));
}
if (!DAY_OF_MONTH_PATTERN.test(dayOfMonth)) {
return Promise.reject(
- new Error(t('message.cron-invalid-day-of-month-field'))
+ new Error(i18n.t('message.cron-invalid-day-of-month-field'))
);
}
if (!MONTH_PATTERN.test(month)) {
- return Promise.reject(new Error(t('message.cron-invalid-month-field')));
+ return Promise.reject(
+ new Error(i18n.t('message.cron-invalid-month-field'))
+ );
}
if (!DAY_OF_WEEK_PATTERN.test(dayOfWeek)) {
return Promise.reject(
- new Error(t('message.cron-invalid-day-of-week-field'))
+ new Error(i18n.t('message.cron-invalid-day-of-week-field'))
);
}
@@ -339,14 +345,14 @@ export const cronValidator = async (_: RuleObject, value: string) => {
if (isFrequencyInMinutes || isFrequencyInSeconds) {
return Promise.reject(
- new Error(t('message.cron-less-than-hour-message'))
+ new Error(i18n.t('message.cron-less-than-hour-message'))
);
}
return Promise.resolve();
} catch {
// If cronstrue fails to parse, it's an invalid cron expression
- return Promise.reject(new Error(t('message.cron-invalid-expression')));
+ return Promise.reject(new Error(i18n.t('message.cron-invalid-expression')));
}
};
@@ -355,7 +361,7 @@ export const getRaiseOnErrorFormField = (
) => {
return {
name: 'raiseOnError',
- label: t('label.raise-on-error'),
+ label: i18n.t('label.raise-on-error'),
type: FieldTypes.SWITCH,
required: false,
formItemProps: {
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SearchServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/SearchServiceUtils.ts
index 0cc1a24d4987..c10d3bbc018d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/SearchServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/SearchServiceUtils.ts
@@ -12,7 +12,7 @@
*/
import { cloneDeep } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { SearchServiceType } from '../generated/entity/services/searchService';
import customSearchConnection from '../jsons/connectionSchemas/connections/search/customSearchConnection.json';
import elasticSearchConnection from '../jsons/connectionSchemas/connections/search/elasticSearchConnection.json';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SearchUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SearchUtils.tsx
index a19fe3045ecd..21161bec89bd 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/SearchUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/SearchUtils.tsx
@@ -13,8 +13,6 @@
import { SearchOutlined } from '@ant-design/icons';
import { Button, Typography } from 'antd';
-import i18next from 'i18next';
-import { isEmpty } from 'lodash';
import { Bucket } from 'Models';
import { Link } from 'react-router-dom';
import { ReactComponent as GlossaryTermIcon } from '../assets/svg/book.svg';
@@ -33,7 +31,6 @@ import { ReactComponent as IconMlModal } from '../assets/svg/mlmodal.svg';
import { ReactComponent as IconPipeline } from '../assets/svg/pipeline-grey.svg';
import { ReactComponent as IconTag } from '../assets/svg/tag-grey.svg';
import { ReactComponent as IconTopic } from '../assets/svg/topic-grey.svg';
-import { WILD_CARD_CHAR } from '../constants/char.constants';
import {
Option,
SearchSuggestions,
@@ -42,179 +39,129 @@ import { EntityType, FqnPart } from '../enums/entity.enum';
import { SearchIndex } from '../enums/search.enum';
import { SearchSourceAlias } from '../interface/search.interface';
import { getPartialNameFromTableFQN } from './CommonUtils';
+import i18n from './i18next/LocalUtil';
import { ElasticsearchQuery } from './QueryBuilderUtils';
import searchClassBase from './SearchClassBase';
import serviceUtilClassBase from './ServiceUtilClassBase';
-import { escapeESReservedCharacters, getEncodedFqn } from './StringsUtils';
-
-export const getSearchAPIQueryParams = (
- queryString: string,
- from: number,
- size: number,
- filters: string,
- sortField: string,
- sortOrder: string,
- searchIndex: SearchIndex | SearchIndex[],
- onlyDeleted = false,
- trackTotalHits = false,
- wildcard = true
-): Record => {
- const start = (from - 1) * size;
-
- const encodedQueryString = queryString
- ? getEncodedFqn(escapeESReservedCharacters(queryString))
- : '';
-
- const query =
- wildcard && encodedQueryString !== WILD_CARD_CHAR
- ? `*${encodedQueryString}*`
- : encodedQueryString;
-
- const params: Record = {
- q: query + (filters ? ` AND ${filters}` : ''),
- from: start,
- size,
- index: searchIndex,
- deleted: onlyDeleted,
- };
-
- if (!isEmpty(sortField)) {
- params.sort_field = sortField;
- }
-
- if (!isEmpty(sortOrder)) {
- params.sort_order = sortOrder;
- }
-
- if (trackTotalHits) {
- params.track_total_hits = trackTotalHits;
- }
-
- return params;
-};
-
-// will add back slash "\" before quote in string if present
-export const getQueryWithSlash = (query: string): string =>
- query.replace(/["']/g, '\\$&');
export const getGroupLabel = (index: string) => {
let label = '';
let GroupIcon;
switch (index) {
case SearchIndex.TOPIC:
- label = i18next.t('label.topic-plural');
+ label = i18n.t('label.topic-plural');
GroupIcon = IconTopic;
break;
case SearchIndex.DATABASE:
- label = i18next.t('label.database-plural');
+ label = i18n.t('label.database-plural');
GroupIcon = IconDatabase;
break;
case SearchIndex.DATABASE_SCHEMA:
- label = i18next.t('label.database-schema-plural');
+ label = i18n.t('label.database-schema-plural');
GroupIcon = IconDatabaseSchema;
break;
case SearchIndex.DASHBOARD:
- label = i18next.t('label.dashboard-plural');
+ label = i18n.t('label.dashboard-plural');
GroupIcon = IconDashboard;
break;
case SearchIndex.PIPELINE:
- label = i18next.t('label.pipeline-plural');
+ label = i18n.t('label.pipeline-plural');
GroupIcon = IconPipeline;
break;
case SearchIndex.MLMODEL:
- label = i18next.t('label.ml-model-plural');
+ label = i18n.t('label.ml-model-plural');
GroupIcon = IconMlModal;
break;
case SearchIndex.GLOSSARY_TERM:
- label = i18next.t('label.glossary-term-plural');
+ label = i18n.t('label.glossary-term-plural');
GroupIcon = GlossaryTermIcon;
break;
case SearchIndex.TAG:
- label = i18next.t('label.tag-plural');
+ label = i18n.t('label.tag-plural');
GroupIcon = IconTag;
break;
case SearchIndex.CONTAINER:
- label = i18next.t('label.container-plural');
+ label = i18n.t('label.container-plural');
GroupIcon = IconContainer;
break;
case SearchIndex.STORED_PROCEDURE:
- label = i18next.t('label.stored-procedure-plural');
+ label = i18n.t('label.stored-procedure-plural');
GroupIcon = IconStoredProcedure;
break;
case SearchIndex.DASHBOARD_DATA_MODEL:
- label = i18next.t('label.data-model-plural');
+ label = i18n.t('label.data-model-plural');
GroupIcon = IconDashboard;
break;
case SearchIndex.SEARCH_INDEX:
- label = i18next.t('label.search-index-plural');
+ label = i18n.t('label.search-index-plural');
GroupIcon = SearchOutlined;
break;
case SearchIndex.DATA_PRODUCT:
- label = i18next.t('label.data-product-plural');
+ label = i18n.t('label.data-product-plural');
GroupIcon = DataProductIcon;
break;
case SearchIndex.CHART:
- label = i18next.t('label.chart-plural');
+ label = i18n.t('label.chart-plural');
GroupIcon = IconChart;
break;
case SearchIndex.API_COLLECTION:
- label = i18next.t('label.api-collection-plural');
+ label = i18n.t('label.api-collection-plural');
GroupIcon = IconApiCollection;
break;
case SearchIndex.API_ENDPOINT:
- label = i18next.t('label.api-endpoint-plural');
+ label = i18n.t('label.api-endpoint-plural');
GroupIcon = IconApiEndpoint;
break;
case SearchIndex.METRIC:
- label = i18next.t('label.metric-plural');
+ label = i18n.t('label.metric-plural');
GroupIcon = MetricIcon;
break;
case SearchIndex.DIRECTORY:
- label = i18next.t('label.directory-plural');
+ label = i18n.t('label.directory-plural');
GroupIcon = MetricIcon;
break;
case SearchIndex.FILE:
- label = i18next.t('label.file-plural');
+ label = i18n.t('label.file-plural');
GroupIcon = MetricIcon;
break;
case SearchIndex.SPREADSHEET:
- label = i18next.t('label.spreadsheet-plural');
+ label = i18n.t('label.spreadsheet-plural');
GroupIcon = MetricIcon;
break;
case SearchIndex.WORKSHEET:
- label = i18next.t('label.worksheet-plural');
+ label = i18n.t('label.worksheet-plural');
GroupIcon = MetricIcon;
break;
case SearchIndex.COLUMN:
- label = i18next.t('label.column-plural');
+ label = i18n.t('label.column-plural');
GroupIcon = ColumnIcon;
break;
@@ -273,7 +220,7 @@ export const getSuggestionElement = (
@@ -446,16 +393,14 @@ export const getTermQuery = (
wildcardMustNotQueries?: Record;
}
) => {
- const termQueries = Object.entries(terms)
- .map(([field, value]) => {
- const nestedPath = getNestedPath(field);
- if (Array.isArray(value)) {
- return value.map((v) => wrapTermQuery(field, v, nestedPath));
- }
+ const termQueries = Object.entries(terms).flatMap(([field, value]) => {
+ const nestedPath = getNestedPath(field);
+ if (Array.isArray(value)) {
+ return value.map((v) => wrapTermQuery(field, v, nestedPath));
+ }
- return wrapTermQuery(field, value, nestedPath);
- })
- .flat();
+ return wrapTermQuery(field, value, nestedPath);
+ });
const wildcardQueries = options?.wildcardTerms
? Object.entries(options.wildcardTerms).map(([field, value]) => ({
@@ -464,16 +409,14 @@ export const getTermQuery = (
: [];
const mustNotQueries = options?.mustNotTerms
- ? Object.entries(options.mustNotTerms)
- .map(([field, value]) => {
- const nestedPath = getNestedPath(field);
- if (Array.isArray(value)) {
- return value.map((v) => wrapTermQuery(field, v, nestedPath));
- }
-
- return wrapTermQuery(field, value, nestedPath);
- })
- .flat()
+ ? Object.entries(options.mustNotTerms).flatMap(([field, value]) => {
+ const nestedPath = getNestedPath(field);
+ if (Array.isArray(value)) {
+ return value.map((v) => wrapTermQuery(field, v, nestedPath));
+ }
+
+ return wrapTermQuery(field, value, nestedPath);
+ })
: [];
const matchQueries = options?.matchTerms
@@ -519,15 +462,15 @@ export const getTermQuery = (
// Handle wildcardMustNotQueries
const wildcardMustNotQueries = options?.wildcardMustNotQueries
- ? Object.entries(options.wildcardMustNotQueries)
- .map(([field, value]) => {
+ ? Object.entries(options.wildcardMustNotQueries).flatMap(
+ ([field, value]) => {
if (Array.isArray(value)) {
return value.map((v) => ({ wildcard: { [field]: v } }));
}
return { wildcard: { [field]: value } };
- })
- .flat()
+ }
+ )
: [];
const allMustNotQueries = [...mustNotQueries, ...wildcardMustNotQueries];
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SecurityServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/SecurityServiceUtils.ts
index b1694cd07925..38df67e0a24c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/SecurityServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/SecurityServiceUtils.ts
@@ -12,22 +12,16 @@
*/
import { cloneDeep } from 'lodash';
-import { COMMON_UI_SCHEMA } from '../constants/Services.constant';
+import { COMMON_UI_SCHEMA } from '../constants/ServiceUISchema.constant';
import { Type } from '../generated/entity/services/securityService';
+import rangerConnection from '../jsons/connectionSchemas/connections/security/rangerConnection.json';
export const getSecurityConfig = (type: Type) => {
let schema = {};
const uiSchema = { ...COMMON_UI_SCHEMA };
- switch (type) {
- case Type.Ranger: {
- // eslint-disable-next-line @typescript-eslint/no-require-imports
- schema = require('../jsons/connectionSchemas/connections/security/rangerConnection.json');
-
- break;
- }
- default:
- break;
+ if (type === Type.Ranger) {
+ schema = rangerConnection;
}
return cloneDeep({ schema, uiSchema });
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceIconUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceIconUtils.ts
new file mode 100644
index 000000000000..2b28006e2168
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceIconUtils.ts
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2022 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import athena from '../assets/img/service-icon-athena.png';
+import azuresql from '../assets/img/service-icon-azuresql.png';
+import bigtable from '../assets/img/service-icon-bigtable.png';
+import burstiq from '../assets/img/service-icon-burstiq.png';
+import cassandra from '../assets/img/service-icon-cassandra.png';
+import clickhouse from '../assets/img/service-icon-clickhouse.png';
+import cockroach from '../assets/img/service-icon-cockroach.png';
+import couchbase from '../assets/img/service-icon-couchbase.svg';
+import databrick from '../assets/img/service-icon-databrick.png';
+import datalake from '../assets/img/service-icon-datalake.png';
+import deltalake from '../assets/img/service-icon-delta-lake.png';
+import doris from '../assets/img/service-icon-doris.png';
+import druid from '../assets/img/service-icon-druid.png';
+import dynamodb from '../assets/img/service-icon-dynamodb.png';
+import exasol from '../assets/img/service-icon-exasol.png';
+import glue from '../assets/img/service-icon-glue.png';
+import greenplum from '../assets/img/service-icon-greenplum.png';
+import hive from '../assets/img/service-icon-hive.png';
+import ibmdb2 from '../assets/img/service-icon-ibmdb2.png';
+import impala from '../assets/img/service-icon-impala.png';
+import iomete from '../assets/img/service-icon-iomete.png';
+import mariadb from '../assets/img/service-icon-mariadb.png';
+import mongodb from '../assets/img/service-icon-mongodb.png';
+import mssql from '../assets/img/service-icon-mssql.png';
+import oracle from '../assets/img/service-icon-oracle.png';
+import pinot from '../assets/img/service-icon-pinot.png';
+import postgres from '../assets/img/service-icon-post.png';
+import presto from '../assets/img/service-icon-presto.png';
+import bigquery from '../assets/img/service-icon-query.png';
+import redshift from '../assets/img/service-icon-redshift.png';
+import salesforce from '../assets/img/service-icon-salesforce.png';
+import saperp from '../assets/img/service-icon-sap-erp.png';
+import saphana from '../assets/img/service-icon-sap-hana.png';
+import sas from '../assets/img/service-icon-sas.svg';
+import singlestore from '../assets/img/service-icon-singlestore.png';
+import snowflake from '../assets/img/service-icon-snowflakes.png';
+import mysql from '../assets/img/service-icon-sql.png';
+import sqlite from '../assets/img/service-icon-sqlite.png';
+import starrocks from '../assets/img/service-icon-starrocks.png';
+import timescale from '../assets/img/service-icon-timescale.png';
+import trino from '../assets/img/service-icon-trino.png';
+import unitycatalog from '../assets/img/service-icon-unitycatalog.svg';
+import vertica from '../assets/img/service-icon-vertica.png';
+import teradata from '../assets/svg/teradata.svg';
+
+// Messaging services
+import kafka from '../assets/img/service-icon-kafka.png';
+import kinesis from '../assets/img/service-icon-kinesis.png';
+import redpanda from '../assets/img/service-icon-redpanda.png';
+import pubsub from '../assets/svg/service-icon-pubsub.svg';
+
+// Dashboard services
+import domo from '../assets/img/service-icon-domo.png';
+import grafana from '../assets/img/service-icon-grafana.png';
+import lightdash from '../assets/img/service-icon-lightdash.png';
+import looker from '../assets/img/service-icon-looker.png';
+import metabase from '../assets/img/service-icon-metabase.png';
+import microstrategy from '../assets/img/service-icon-microstrategy.svg';
+import mode from '../assets/img/service-icon-mode.png';
+import powerbi from '../assets/img/service-icon-power-bi.png';
+import qliksense from '../assets/img/service-icon-qlik-sense.png';
+import quicksight from '../assets/img/service-icon-quicksight.png';
+import redash from '../assets/img/service-icon-redash.png';
+import sigma from '../assets/img/service-icon-sigma.png';
+import superset from '../assets/img/service-icon-superset.png';
+import tableau from '../assets/img/service-icon-tableau.png';
+import hex from '../assets/svg/service-icon-hex.svg';
+
+// Pipeline services
+import airbyte from '../assets/img/Airbyte.png';
+import airflow from '../assets/img/service-icon-airflow.png';
+import dagster from '../assets/img/service-icon-dagster.png';
+import dbt from '../assets/img/service-icon-dbt.png';
+import fivetran from '../assets/img/service-icon-fivetran.png';
+import flink from '../assets/img/service-icon-flink.png';
+import nifi from '../assets/img/service-icon-nifi.png';
+import openlineage from '../assets/img/service-icon-openlineage.svg';
+import spark from '../assets/img/service-icon-spark.png';
+import spline from '../assets/img/service-icon-spline.png';
+
+// ML Model services
+import sagemaker from '../assets/img/service-icon-sagemaker.png';
+import scikit from '../assets/img/service-icon-scikit.png';
+import mlflow from '../assets/svg/service-icon-mlflow.svg';
+
+// Storage services
+import amazons3 from '../assets/img/service-icon-amazon-s3.svg';
+import gcs from '../assets/img/service-icon-gcs.png';
+
+// Search services
+import elasticsearch from '../assets/svg/elasticsearch.svg';
+import opensearch from '../assets/svg/open-search.svg';
+
+// Metadata services
+import alationsink from '../assets/img/service-icon-alation-sink.png';
+import amundsen from '../assets/img/service-icon-amundsen.png';
+import atlas from '../assets/img/service-icon-atlas.svg';
+
+// Drive services
+import googledrive from '../assets/svg/service-icon-google-drive.svg';
+import sftp from '../assets/svg/service-icon-sftp.svg';
+
+// Default icons
+import synapse from '../assets/img/service-icon-synapse.png';
+import dashboarddefault from '../assets/svg/dashboard.svg';
+import defaultservice from '../assets/svg/default-service-icon.svg';
+import databasedefault from '../assets/svg/ic-custom-database.svg';
+import customdrivedefault from '../assets/svg/ic-custom-drive.svg';
+import mlmodeldefault from '../assets/svg/ic-custom-model.svg';
+import searchdefault from '../assets/svg/ic-custom-search.svg';
+import storagedefault from '../assets/svg/ic-custom-storage.svg';
+import drivedefault from '../assets/svg/ic-drive-service.svg';
+import restservice from '../assets/svg/ic-service-rest-api.svg';
+import logo from '../assets/svg/logo-monogram.svg';
+import pipelinedefault from '../assets/svg/pipeline.svg';
+import securitydefault from '../assets/svg/security-safe.svg';
+import topicdefault from '../assets/svg/topic.svg';
+
+const SERVICE_ICON_LOADERS: Record = {
+ // Database services
+ mysql: mysql,
+ sqlite: sqlite,
+ mssql: mssql,
+ redshift: redshift,
+ bigquery: bigquery,
+ bigtable: bigtable,
+ hive: hive,
+ impala: impala,
+ postgres: postgres,
+ oracle: oracle,
+ snowflake: snowflake,
+ athena: athena,
+ presto: presto,
+ trino: trino,
+ glue: glue,
+ mariadb: mariadb,
+ vertica: vertica,
+ azuresql: azuresql,
+ clickhouse: clickhouse,
+ databricks: databrick,
+ unitycatalog: unitycatalog,
+ db2: ibmdb2,
+ doris: doris,
+ starrocks: starrocks,
+ druid: druid,
+ dynamodb: dynamodb,
+ singlestore: singlestore,
+ salesforce: salesforce,
+ saphana: saphana,
+ saperp: saperp,
+ deltalake: deltalake,
+ pinotdb: pinot,
+ datalake: datalake,
+ exasol: exasol,
+ mongodb: mongodb,
+ cassandra: cassandra,
+ couchbase: couchbase,
+ greenplum: greenplum,
+ teradata: teradata,
+ cockroach: cockroach,
+ timescale: timescale,
+ burstiq: burstiq,
+ sas: sas,
+ iomete: iomete,
+ domodatabase: domo,
+ customdatabase: databasedefault,
+
+ // Messaging services
+ kafka: kafka,
+ pubsub: pubsub,
+ redpanda: redpanda,
+ kinesis: kinesis,
+ custommessaging: topicdefault,
+
+ // Dashboard services
+ superset: superset,
+ looker: looker,
+ tableau: tableau,
+ redash: redash,
+ metabase: metabase,
+ powerbi: powerbi,
+ sigma: sigma,
+ mode: mode,
+ domodashboard: domo,
+ quicksight: quicksight,
+ qliksense: qliksense,
+ lightdash: lightdash,
+ microstrategy: microstrategy,
+ grafana: grafana,
+ hex: hex,
+ customdashboard: dashboarddefault,
+
+ // Pipeline services
+ airflow: airflow,
+ airbyte: airbyte,
+ dagster: dagster,
+ dbtcloud: dbt,
+ fivetran: fivetran,
+ nifi: nifi,
+ spark: spark,
+ spline: spline,
+ flink: flink,
+ openlineage: openlineage,
+ domopipeline: domo,
+ kafkaconnect: kafka,
+ databrickspipeline: databrick,
+ gluepipeline: glue,
+ custompipeline: pipelinedefault,
+
+ // ML Model services
+ mlflow: mlflow,
+ scikit: scikit,
+ sagemaker: sagemaker,
+ custommlmodel: mlmodeldefault,
+
+ // Storage services
+ s3: amazons3,
+ gcs: gcs,
+
+ // Search services
+ elasticsearch: elasticsearch,
+ opensearch: opensearch,
+
+ // Metadata services
+ amundsen: amundsen,
+ atlas: atlas,
+ alationsink: alationsink,
+ openmetadata: logo,
+
+ // Drive services
+ googledrive: googledrive,
+ sftp: sftp,
+ customdrive: customdrivedefault,
+
+ // API services
+ rest: restservice,
+
+ // Default icons
+ defaultservice: defaultservice,
+ databasedefault: databasedefault,
+ topicdefault: topicdefault,
+ dashboarddefault: dashboarddefault,
+ pipelinedefault: pipelinedefault,
+ mlmodeldefault: mlmodeldefault,
+ storagedefault: storagedefault,
+ drivedefault: drivedefault,
+ customdrivedefault: customdrivedefault,
+ searchdefault: searchdefault,
+ securitydefault: securitydefault,
+ restservice: restservice,
+ logo: logo,
+ synapse: synapse,
+};
+
+export const getServiceIcon = (iconKey: string): string => {
+ const normalizedKey = iconKey.toLowerCase().replaceAll(/[_-]/g, '');
+ const icon = SERVICE_ICON_LOADERS[normalizedKey];
+
+ return icon;
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceInsightsTabUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceInsightsTabUtils.tsx
index f3945a27c9a0..4e239e13d188 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceInsightsTabUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceInsightsTabUtils.tsx
@@ -42,10 +42,10 @@ import {
} from '../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { DataInsightCustomChartResult } from '../rest/DataInsightAPI';
import i18n from '../utils/i18next/LocalUtil';
-import { Transi18next } from './CommonUtils';
import documentationLinksClassBase from './DocumentationLinksClassBase';
import { getEntityNameLabel } from './EntityUtils';
import Fqn from './Fqn';
+import { Transi18next } from './i18next/LocalUtil';
import { getEntityIcon } from './TableUtils';
const { t } = i18n;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtilClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtilClassBase.ts
index a6c3cab1f8ca..22228d14234f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtilClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtilClassBase.ts
@@ -12,7 +12,7 @@
*/
import { ObjectFieldTemplatePropertyType } from '@rjsf/utils';
-import { get, isEmpty, toLower } from 'lodash';
+import { get, isEmpty } from 'lodash';
import { ServiceTypes } from 'Models';
import GlossaryIcon from '../assets/svg/book.svg';
import ChartIcon from '../assets/svg/chart.svg';
@@ -25,104 +25,6 @@ import AgentsStatusWidget from '../components/ServiceInsights/AgentsStatusWidget
import PlatformInsightsWidget from '../components/ServiceInsights/PlatformInsightsWidget/PlatformInsightsWidget';
import TotalDataAssetsWidget from '../components/ServiceInsights/TotalDataAssetsWidget/TotalDataAssetsWidget';
import MetadataAgentsWidget from '../components/Settings/Services/Ingestion/MetadataAgentsWidget/MetadataAgentsWidget';
-import {
- AIRBYTE,
- AIRFLOW,
- ALATIONSINK,
- AMAZON_S3,
- AMUNDSEN,
- ATHENA,
- ATLAS,
- AZURESQL,
- BIGQUERY,
- BIGTABLE,
- BURSTIQ,
- CASSANDRA,
- CLICKHOUSE,
- COCKROACH,
- COUCHBASE,
- CUSTOM_DRIVE_DEFAULT,
- CUSTOM_SEARCH_DEFAULT,
- CUSTOM_STORAGE_DEFAULT,
- DAGSTER,
- DASHBOARD_DEFAULT,
- DATABASE_DEFAULT,
- DATABRICK,
- DATALAKE,
- DBT,
- DEFAULT_SERVICE,
- DELTALAKE,
- DOMO,
- DORIS,
- DRUID,
- DYNAMODB,
- ELASTIC_SEARCH,
- EXASOL,
- FIVETRAN,
- FLINK,
- GCS,
- GLUE,
- GOOGLE_DRIVE,
- GRAFANA,
- GREENPLUM,
- HEX,
- HIVE,
- IBMDB2,
- IMPALA,
- IOMETE,
- KAFKA,
- KINESIS,
- LIGHT_DASH,
- LOGO,
- LOOKER,
- MARIADB,
- METABASE,
- MICROSTRATEGY,
- MLFLOW,
- ML_MODEL_DEFAULT,
- MODE,
- MONGODB,
- MSSQL,
- MYSQL,
- NIFI,
- OPENLINEAGE,
- OPEN_SEARCH,
- ORACLE,
- PINOT,
- PIPELINE_DEFAULT,
- POSTGRES,
- POWERBI,
- PRESTO,
- QLIK_SENSE,
- QUICKSIGHT,
- REDASH,
- REDPANDA,
- REDSHIFT,
- REST_SERVICE,
- SAGEMAKER,
- SALESFORCE,
- SAP_ERP,
- SAP_HANA,
- SAS,
- SCIKIT,
- SFTP,
- SIGMA,
- SINGLESTORE,
- SNOWFLAKE,
- SPARK,
- SPLINE,
- SQLITE,
- STARROCKS,
- SUPERSET,
- SYNAPSE,
- TABLEAU,
- TERADATA,
- TIMESCALE,
- TOPIC_DEFAULT,
- TRINO,
- UNITYCATALOG,
- VERTICA,
-} from '../constants/Services.constant';
import { EntityType } from '../enums/entity.enum';
import { ExplorePageTabs } from '../enums/Explore.enum';
import {
@@ -169,6 +71,7 @@ import { getMlmodelConfig } from './MlmodelServiceUtils';
import { getPipelineConfig } from './PipelineServiceUtils';
import { getSearchServiceConfig } from './SearchServiceUtils';
import { getSecurityConfig } from './SecurityServiceUtils';
+import { getServiceIcon } from './ServiceIconUtils';
import {
getSearchIndexFromService,
getTestConnectionName,
@@ -414,149 +317,45 @@ class ServiceUtilClassBase {
return EntityType.TABLE;
}
- private readonly serviceLogoMap = new Map([
- [this.DatabaseServiceTypeSmallCase.CustomDatabase, DATABASE_DEFAULT],
- [this.DatabaseServiceTypeSmallCase.Mysql, MYSQL],
- [this.DatabaseServiceTypeSmallCase.Redshift, REDSHIFT],
- [this.DatabaseServiceTypeSmallCase.BigQuery, BIGQUERY],
- [this.DatabaseServiceTypeSmallCase.BigTable, BIGTABLE],
- [this.DatabaseServiceTypeSmallCase.Hive, HIVE],
- [this.DatabaseServiceTypeSmallCase.Impala, IMPALA],
- [this.DatabaseServiceTypeSmallCase.Postgres, POSTGRES],
- [this.DatabaseServiceTypeSmallCase.Oracle, ORACLE],
- [this.DatabaseServiceTypeSmallCase.Snowflake, SNOWFLAKE],
- [this.DatabaseServiceTypeSmallCase.Mssql, MSSQL],
- [this.DatabaseServiceTypeSmallCase.Athena, ATHENA],
- [this.DatabaseServiceTypeSmallCase.Presto, PRESTO],
- [this.DatabaseServiceTypeSmallCase.Trino, TRINO],
- [this.DatabaseServiceTypeSmallCase.Glue, GLUE],
- [this.DatabaseServiceTypeSmallCase.DomoDatabase, DOMO],
- [this.DatabaseServiceTypeSmallCase.MariaDB, MARIADB],
- [this.DatabaseServiceTypeSmallCase.Vertica, VERTICA],
- [this.DatabaseServiceTypeSmallCase.AzureSQL, AZURESQL],
- [this.DatabaseServiceTypeSmallCase.Clickhouse, CLICKHOUSE],
- [this.DatabaseServiceTypeSmallCase.Databricks, DATABRICK],
- [this.DatabaseServiceTypeSmallCase.UnityCatalog, UNITYCATALOG],
- [this.DatabaseServiceTypeSmallCase.Db2, IBMDB2],
- [this.DatabaseServiceTypeSmallCase.Doris, DORIS],
- [this.DatabaseServiceTypeSmallCase.StarRocks, STARROCKS],
- [this.DatabaseServiceTypeSmallCase.Druid, DRUID],
- [this.DatabaseServiceTypeSmallCase.DynamoDB, DYNAMODB],
- [this.DatabaseServiceTypeSmallCase.Exasol, EXASOL],
- [this.DatabaseServiceTypeSmallCase.SingleStore, SINGLESTORE],
- [this.DatabaseServiceTypeSmallCase.SQLite, SQLITE],
- [this.DatabaseServiceTypeSmallCase.Salesforce, SALESFORCE],
- [this.DatabaseServiceTypeSmallCase.SapHana, SAP_HANA],
- [this.DatabaseServiceTypeSmallCase.SapERP, SAP_ERP],
- [this.DatabaseServiceTypeSmallCase.DeltaLake, DELTALAKE],
- [this.DatabaseServiceTypeSmallCase.PinotDB, PINOT],
- [this.DatabaseServiceTypeSmallCase.Datalake, DATALAKE],
- [this.DatabaseServiceTypeSmallCase.MongoDB, MONGODB],
- [this.DatabaseServiceTypeSmallCase.Cassandra, CASSANDRA],
- [this.DatabaseServiceTypeSmallCase.SAS, SAS],
- [this.DatabaseServiceTypeSmallCase.Couchbase, COUCHBASE],
- [this.DatabaseServiceTypeSmallCase.Cockroach, COCKROACH],
- [this.DatabaseServiceTypeSmallCase.Greenplum, GREENPLUM],
- [this.DatabaseServiceTypeSmallCase.Teradata, TERADATA],
- [this.DatabaseServiceTypeSmallCase.Synapse, SYNAPSE],
- [this.DatabaseServiceTypeSmallCase.BurstIQ, BURSTIQ],
- [this.DatabaseServiceTypeSmallCase.Timescale, TIMESCALE],
- [this.MessagingServiceTypeSmallCase.CustomMessaging, TOPIC_DEFAULT],
- [this.MessagingServiceTypeSmallCase.Kafka, KAFKA],
- [this.MessagingServiceTypeSmallCase.Redpanda, REDPANDA],
- [this.MessagingServiceTypeSmallCase.Kinesis, KINESIS],
- [this.DashboardServiceTypeSmallCase.CustomDashboard, DASHBOARD_DEFAULT],
- [this.DashboardServiceTypeSmallCase.Superset, SUPERSET],
- [this.DashboardServiceTypeSmallCase.Looker, LOOKER],
- [this.DashboardServiceTypeSmallCase.Tableau, TABLEAU],
- [this.DashboardServiceTypeSmallCase.Hex, HEX],
- [this.DashboardServiceTypeSmallCase.Redash, REDASH],
- [this.DashboardServiceTypeSmallCase.Metabase, METABASE],
- [this.DashboardServiceTypeSmallCase.PowerBI, POWERBI],
- [this.DashboardServiceTypeSmallCase.QuickSight, QUICKSIGHT],
- [this.DashboardServiceTypeSmallCase.DomoDashboard, DOMO],
- [this.DashboardServiceTypeSmallCase.Mode, MODE],
- [this.DashboardServiceTypeSmallCase.QlikSense, QLIK_SENSE],
- [this.DashboardServiceTypeSmallCase.QlikCloud, QLIK_SENSE],
- [this.DashboardServiceTypeSmallCase.Lightdash, LIGHT_DASH],
- [this.DashboardServiceTypeSmallCase.Sigma, SIGMA],
- [this.DashboardServiceTypeSmallCase.MicroStrategy, MICROSTRATEGY],
- [this.DashboardServiceTypeSmallCase.Grafana, GRAFANA],
- [this.PipelineServiceTypeSmallCase.CustomPipeline, PIPELINE_DEFAULT],
- [this.PipelineServiceTypeSmallCase.Airflow, AIRFLOW],
- [this.PipelineServiceTypeSmallCase.Airbyte, AIRBYTE],
- [this.PipelineServiceTypeSmallCase.Dagster, DAGSTER],
- [this.PipelineServiceTypeSmallCase.Fivetran, FIVETRAN],
- [this.PipelineServiceTypeSmallCase.DBTCloud, DBT],
- [this.PipelineServiceTypeSmallCase.GluePipeline, GLUE],
- [this.PipelineServiceTypeSmallCase.KafkaConnect, KAFKA],
- [this.PipelineServiceTypeSmallCase.Spark, SPARK],
- [this.PipelineServiceTypeSmallCase.Spline, SPLINE],
- [this.PipelineServiceTypeSmallCase.Nifi, NIFI],
- [this.PipelineServiceTypeSmallCase.DomoPipeline, DOMO],
- [this.PipelineServiceTypeSmallCase.DatabricksPipeline, DATABRICK],
- [this.PipelineServiceTypeSmallCase.OpenLineage, OPENLINEAGE],
- [this.PipelineServiceTypeSmallCase.Flink, FLINK],
- [this.MlModelServiceTypeSmallCase.CustomMlModel, ML_MODEL_DEFAULT],
- [this.MlModelServiceTypeSmallCase.Mlflow, MLFLOW],
- [this.MlModelServiceTypeSmallCase.Sklearn, SCIKIT],
- [this.MlModelServiceTypeSmallCase.SageMaker, SAGEMAKER],
- [this.MetadataServiceTypeSmallCase.Amundsen, AMUNDSEN],
- [this.MetadataServiceTypeSmallCase.Atlas, ATLAS],
- [this.MetadataServiceTypeSmallCase.AlationSink, ALATIONSINK],
- [this.MetadataServiceTypeSmallCase.OpenMetadata, LOGO],
- [this.StorageServiceTypeSmallCase.CustomStorage, CUSTOM_STORAGE_DEFAULT],
- [this.StorageServiceTypeSmallCase.S3, AMAZON_S3],
- [this.StorageServiceTypeSmallCase.Gcs, GCS],
- [this.SearchServiceTypeSmallCase.CustomSearch, CUSTOM_SEARCH_DEFAULT],
- [this.SearchServiceTypeSmallCase.ElasticSearch, ELASTIC_SEARCH],
- [this.SearchServiceTypeSmallCase.OpenSearch, OPEN_SEARCH],
- [this.ApiServiceTypeSmallCase.REST, REST_SERVICE],
- [this.DriveServiceTypeSmallCase.CustomDrive, CUSTOM_DRIVE_DEFAULT],
- [this.DriveServiceTypeSmallCase.GoogleDrive, GOOGLE_DRIVE],
- [this.DriveServiceTypeSmallCase.SFTP, SFTP],
- [this.DatabaseServiceTypeSmallCase.Iomete, IOMETE],
- ]);
-
private getDefaultLogoForServiceType(type: string): string {
const serviceTypes = this.getSupportedServiceFromList();
if (serviceTypes.messagingServices.includes(type)) {
- return TOPIC_DEFAULT;
+ return getServiceIcon('topicdefault');
}
if (serviceTypes.dashboardServices.includes(type)) {
- return DASHBOARD_DEFAULT;
+ return getServiceIcon('dashboarddefault');
}
if (serviceTypes.pipelineServices.includes(type)) {
- return PIPELINE_DEFAULT;
+ return getServiceIcon('pipelinedefault');
}
if (serviceTypes.databaseServices.includes(type)) {
- return DATABASE_DEFAULT;
+ return getServiceIcon('databasedefault');
}
if (serviceTypes.mlmodelServices.includes(type)) {
- return ML_MODEL_DEFAULT;
+ return getServiceIcon('mlmodeldefault');
}
if (serviceTypes.storageServices.includes(type)) {
- return CUSTOM_STORAGE_DEFAULT;
+ return getServiceIcon('storagedefault');
}
if (serviceTypes.searchServices.includes(type)) {
- return CUSTOM_SEARCH_DEFAULT;
+ return getServiceIcon('searchdefault');
}
if (serviceTypes.securityServices.includes(type)) {
- return DEFAULT_SERVICE;
+ return getServiceIcon('securitydefault');
}
if (serviceTypes.driveServices.includes(type)) {
- return CUSTOM_DRIVE_DEFAULT;
+ return getServiceIcon('drivedefault');
+ }
+ if (serviceTypes.apiServices.includes(type)) {
+ return getServiceIcon('restservice');
}
- return DEFAULT_SERVICE;
+ return getServiceIcon('defaultservice');
}
- public getServiceLogo(type: string): string {
- const lowerType = toLower(type);
- const logo = this.serviceLogoMap.get(lowerType);
-
- return logo ?? this.getDefaultLogoForServiceType(type);
+ public getServiceLogo(type: string) {
+ return getServiceIcon(type) ?? this.getDefaultLogoForServiceType(type);
}
public getServiceTypeLogo(searchSource: {
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/StorageServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/StorageServiceUtils.ts
index 9ccb4e64c529..1c62f61e0ef5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/StorageServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/StorageServiceUtils.ts
@@ -21,7 +21,7 @@ import s3Connection from '../jsons/connectionSchemas/connections/storage/s3Conne
export const getStorageConfig = (type: StorageServiceType) => {
let schema = {};
const uiSchema = { ...COMMON_UI_SCHEMA };
- switch (type as unknown as StorageServiceType) {
+ switch (type) {
case StorageServiceType.S3: {
schema = s3Connection;
@@ -37,6 +37,9 @@ export const getStorageConfig = (type: StorageServiceType) => {
break;
}
+
+ default:
+ break;
}
return cloneDeep({ schema, uiSchema });
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/StringsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/StringsUtils.ts
index 20dd50021d26..3002ea1ab0b6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/StringsUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/StringsUtils.ts
@@ -17,9 +17,13 @@ import { get, isString } from 'lodash';
import i18n from './i18next/LocalUtil';
export const stringToSlug = (dataString: string, slugString = '') => {
- return dataString.toLowerCase().replace(/ /g, slugString);
+ return dataString.toLowerCase().replaceAll(' ', slugString);
};
+// will add back slash "\" before quote in string if present
+export const getQueryWithSlash = (query: string): string =>
+ query.replaceAll(/["']/g, String.raw`\$&`);
+
/**
* Convert a template string into HTML DOM nodes
* Same as React.createElement(type, options, children)
@@ -66,8 +70,7 @@ export const getJSONFromString = (data: string): string | null => {
try {
// Format string if possible and return valid JSON
return JSON.parse(data);
- } catch (e) {
- // Invalid JSON, return null
+ } catch (_error) {
return null;
}
};
@@ -87,7 +90,7 @@ export const bytesToSize = (bytes: number) => {
} else if (bytes < 0) {
return `N/A`;
} else {
- const i = parseInt(
+ const i = Number.parseInt(
Math.floor(Math.log(bytes) / Math.log(1024)).toString(),
10
);
@@ -158,15 +161,6 @@ export const getDecodedFqn = (fqn: string, plusAsSpace = false) => {
return uri;
};
-/**
- *
- * @param url - Url to be check
- * @returns - True if url is external otherwise false
- */
-export const isExternalUrl = (url = '') => {
- return /^https?:\/\//.test(url);
-};
-
/**
*
* @param a compare value one
@@ -189,41 +183,41 @@ export const customServiceComparator = (a: string, b: string): number => {
export const replacePlus = (fqn: string) => fqn.replaceAll('+', ' ');
export const ES_RESERVED_CHARACTERS: Record = {
- '+': '\\+',
- '-': '\\-',
- '=': '\\=',
- '&': '\\&',
- '&&': '\\&&',
- '||': '\\||',
- '>': '\\>',
- '<': '\\<',
- '!': '\\!',
- '(': '\\(',
- ')': '\\)',
- '{': '\\{',
- '}': '\\}',
- '[': '\\[',
- ']': '\\]',
- '^': '\\^',
- '"': '\\"',
- '~': '\\~',
- '*': '\\*',
- '?': '\\?',
- ':': '\\:',
- '\\': '\\\\',
- '/': '\\/',
+ '+': String.raw`\+`,
+ '-': String.raw`\-`,
+ '=': String.raw`\=`,
+ '&': String.raw`\&`,
+ '&&': String.raw`\&&`,
+ '||': String.raw`\||`,
+ '>': String.raw`\>`,
+ '<': String.raw`\<`,
+ '!': String.raw`\!`,
+ '(': String.raw`\(`,
+ ')': String.raw`\)`,
+ '{': String.raw`\{`,
+ '}': String.raw`\}`,
+ '[': String.raw`\[`,
+ ']': String.raw`\]`,
+ '^': String.raw`\^`,
+ '"': String.raw`\"`,
+ '~': String.raw`\~`,
+ '*': String.raw`\*`,
+ '?': String.raw`\?`,
+ ':': String.raw`\:`,
+ '\\': String.raw`\\`,
+ '/': String.raw`\/`,
};
export const escapeESReservedCharacters = (text?: string) => {
const reUnescapedHtml = /[\\[\]#+=&|> {
return ES_RESERVED_CHARACTERS[char] ?? char;
};
return text && reHasUnescapedHtml.test(text)
- ? text.replace(reUnescapedHtml, getReplacedChar)
+ ? text.replaceAll(reUnescapedHtml, getReplacedChar)
: text ?? '';
};
@@ -251,7 +245,7 @@ export const formatJsonString = (jsonString: string, indent = '') => {
}
return formattedJson;
- } catch (error) {
+ } catch (_error) {
// Return the original JSON string if parsing fails
return jsonString;
}
@@ -276,7 +270,7 @@ export const replaceCallback = (character: string) => {
* @returns A UUID string
*/
export const generateUUID = () => {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replaceAll(
/[xy]/g,
replaceCallback
);
@@ -315,7 +309,7 @@ export const jsonToCSV = (
}
const escaped =
typeof value === 'string'
- ? value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
+ ? value.replaceAll('\\', '\\\\').replaceAll('"', String.raw`\"`)
: value.toString(); // handle quotes in content
return `"${escaped}"`; // wrap each field in quotes
@@ -350,7 +344,7 @@ export function removeAttachmentsWithoutUrl(htmlString: string): string {
doc.querySelectorAll('div[data-type="file-attachment"]');
attachments.forEach((div: HTMLDivElement) => {
- const url: string | null = div.getAttribute('data-url');
+ const url = div.dataset.url;
if (!url) {
div.remove();
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx
index 6aa877895381..09021e6a38a6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx
@@ -126,6 +126,7 @@ import { ReactComponent as TaskIcon } from '../assets/svg/task-ic.svg';
import { ReactComponent as UserIcon } from '../assets/svg/user.svg';
import { ActivityFeedTab } from '../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.component';
import { ActivityFeedLayoutType } from '../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface';
+import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback';
import { CustomPropertyTable } from '../components/common/CustomPropertyTable/CustomPropertyTable';
import ErrorPlaceHolder from '../components/common/ErrorWithPlaceholder/ErrorPlaceHolder';
import Loader from '../components/common/Loader/Loader';
@@ -135,11 +136,7 @@ import TabsLabel from '../components/common/TabsLabel/TabsLabel.component';
import { TabProps } from '../components/common/TabsLabel/TabsLabel.interface';
import { GenericTab } from '../components/Customization/GenericTab/GenericTab';
import { CommonWidgets } from '../components/DataAssets/CommonWidgets/CommonWidgets';
-import DataObservabilityTab from '../components/Database/Profiler/DataObservability/DataObservabilityTab';
-import SampleDataTableComponent from '../components/Database/SampleDataTable/SampleDataTable.component';
import SchemaTable from '../components/Database/SchemaTable/SchemaTable.component';
-import TableQueries from '../components/Database/TableQueries/TableQueries';
-import { ContractTab } from '../components/DataContract/ContractTab/ContractTab';
import { useEntityExportModalProvider } from '../components/Entity/EntityExportModalProvider/EntityExportModalProvider.component';
import { SourceType } from '../components/SearchedData/SearchedData.interface';
import { NON_SERVICE_TYPE_ASSETS } from '../constants/Assets.constants';
@@ -182,7 +179,6 @@ import {
} from '../pages/TableDetailsPageV1/FrequentlyJoinedTables/FrequentlyJoinedTables.component';
import { PartitionedKeys } from '../pages/TableDetailsPageV1/PartitionedKeys/PartitionedKeys.component';
import ConstraintIcon from '../pages/TableDetailsPageV1/TableConstraints/ConstraintIcon';
-import TableConstraints from '../pages/TableDetailsPageV1/TableConstraints/TableConstraints';
import { exportTableDetailsInCSV } from '../rest/tableAPI';
import { extractApiEndpointFields } from './APIEndpoints/APIEndpointUtils';
import {
@@ -203,16 +199,54 @@ import { ordinalize } from './StringsUtils';
import { TableDetailPageTabProps } from './TableClassBase';
import { TableFieldsInfoCommonEntities } from './TableUtils.interface';
import { extractTopicFields } from './TopicDetailsUtils';
-const KnowledgeGraph = lazy(
- () => import('../components/KnowledgeGraph/KnowledgeGraph')
+
+const SampleDataTableComponent = withSuspenseFallback(
+ lazy(
+ () =>
+ import('../components/Database/SampleDataTable/SampleDataTable.component')
+ )
+);
+
+const TableQueries = withSuspenseFallback(
+ lazy(() => import('../components/Database/TableQueries/TableQueries'))
+);
+
+const ContractTab = withSuspenseFallback(
+ lazy(() =>
+ import('../components/DataContract/ContractTab/ContractTab').then(
+ (module) => ({ default: module.ContractTab })
+ )
+ )
+);
+
+const DataObservabilityTab = withSuspenseFallback(
+ lazy(
+ () =>
+ import(
+ '../components/Database/Profiler/DataObservability/DataObservabilityTab'
+ )
+ )
+);
+
+const EntityLineageTab = withSuspenseFallback(
+ lazy(() =>
+ import('../components/Lineage/EntityLineageTab/EntityLineageTab').then(
+ (module) => ({ default: module.EntityLineageTab })
+ )
+ )
);
-const EntityLineageTab = lazy(() =>
- import('../components/Lineage/EntityLineageTab/EntityLineageTab').then(
- (module) => ({ default: module.EntityLineageTab })
+const TableConstraints = withSuspenseFallback(
+ lazy(
+ () =>
+ import('../pages/TableDetailsPageV1/TableConstraints/TableConstraints')
)
);
+const KnowledgeGraph = withSuspenseFallback(
+ lazy(() => import('../components/KnowledgeGraph/KnowledgeGraph'))
+);
+
export const getUsagePercentile = (pctRank: number, isLiteral = false) => {
const percentile = Math.round(pctRank * 10) / 10;
const ordinalPercentile = ordinalize(percentile);
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TagsUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/TagsUtils.tsx
index c30251434b49..4d98d9e34320 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TagsUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TagsUtils.tsx
@@ -14,7 +14,6 @@
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
import { Space, Tag as AntdTag, Tooltip, Typography } from 'antd';
import { AxiosError } from 'axios';
-import i18next from 'i18next';
import { isString, omit } from 'lodash';
import { EntityTags } from 'Models';
import type { CustomTagProps } from 'rc-select/lib/BaseSelect';
@@ -52,6 +51,7 @@ import {
} from '../rest/tagAPI';
import { getEntityName } from './EntityUtils';
import { getQueryFilterToIncludeApprovedTerm } from './GlossaryUtils';
+import i18n from './i18next/LocalUtil';
import { checkPermissionEntityResource } from './PermissionsUtils';
import {
getClassificationTagPath,
@@ -228,11 +228,11 @@ export const getUsageCountLink = (tagFQN: string) => {
export const getTagPlaceholder = (isGlossaryType: boolean): string =>
isGlossaryType
- ? i18next.t('label.search-entity', {
- entity: i18next.t('label.glossary-term-plural'),
+ ? i18n.t('label.search-entity', {
+ entity: i18n.t('label.glossary-term-plural'),
})
- : i18next.t('label.search-entity', {
- entity: i18next.t('label.tag-plural'),
+ : i18n.t('label.search-entity', {
+ entity: i18n.t('label.tag-plural'),
});
export const tagRender = (customTagProps: CustomTagProps) => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TourUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/TourUtils.tsx
index aab82b3ba336..60827895769c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TourUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TourUtils.tsx
@@ -11,10 +11,9 @@
* limitations under the License.
*/
-import i18next from 'i18next';
import { EntityTabs } from '../enums/entity.enum';
import { CurrentTourPageType } from '../enums/tour.enum';
-import { Transi18next } from './CommonUtils';
+import i18n, { Transi18next } from './i18next/LocalUtil';
interface ArgObject {
searchTerm: string;
@@ -36,7 +35,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-activity-feed"
renderElement={}
values={{
- text: i18next.t('label.activity-feed-plural'),
+ text: i18n.t('label.activity-feed-plural'),
}}
/>
@@ -51,7 +50,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-search-for-matching-dataset"
renderElement={}
values={{
- text: i18next.t('label.search'),
+ text: i18n.t('label.search'),
}}
/>
@@ -69,7 +68,7 @@ export const getTourSteps = ({
renderElement={}
values={{
text: searchTerm,
- enterText: i18next.t('label.enter'),
+ enterText: i18n.t('label.enter'),
}}
/>
@@ -92,7 +91,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-explore-summary-asset"
renderElement={}
values={{
- text: i18next.t('label.explore'),
+ text: i18n.t('label.explore'),
}}
/>
@@ -125,7 +124,7 @@ export const getTourSteps = ({
i18nKey="message.tour-high-level-assets-information-step"
renderElement={}
values={{
- text: i18next.t('label.schema'),
+ text: i18n.t('label.schema'),
}}
/>
@@ -140,7 +139,7 @@ export const getTourSteps = ({
i18nKey="message.tour-owner-step"
renderElement={}
values={{
- text: i18next.t('label.schema'),
+ text: i18n.t('label.schema'),
}}
/>
@@ -155,7 +154,7 @@ export const getTourSteps = ({
i18nKey="message.tour-follow-step"
renderElement={}
values={{
- text: i18next.t('label.schema'),
+ text: i18n.t('label.schema'),
}}
/>
@@ -170,7 +169,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-get-to-know-table-schema"
renderElement={}
values={{
- text: i18next.t('label.schema'),
+ text: i18n.t('label.schema'),
}}
/>
@@ -189,7 +188,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-click-on-entity-tab"
renderElement={}
values={{
- text: i18next.t('label.sample-data'),
+ text: i18n.t('label.sample-data'),
}}
/>
@@ -206,7 +205,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-look-at-sample-data"
renderElement={}
values={{
- text: i18next.t('label.sample-data'),
+ text: i18n.t('label.sample-data'),
}}
/>
@@ -227,7 +226,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-click-on-entity-tab"
renderElement={}
values={{
- text: i18next.t('label.data-observability'),
+ text: i18n.t('label.data-observability'),
}}
/>
@@ -241,8 +240,8 @@ export const getTourSteps = ({
i18nKey="message.tour-step-discover-data-assets-with-data-profile"
renderElement={}
values={{
- text: i18next.t('label.data-entity', {
- entity: i18next.t('label.profiler'),
+ text: i18n.t('label.data-entity', {
+ entity: i18n.t('label.profiler'),
}),
}}
/>
@@ -265,7 +264,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-click-on-entity-tab"
renderElement={}
values={{
- text: i18next.t('label.lineage'),
+ text: i18n.t('label.lineage'),
}}
/>
@@ -279,7 +278,7 @@ export const getTourSteps = ({
i18nKey="message.tour-step-trace-path-across-tables"
renderElement={}
values={{
- text: i18next.t('label.lineage'),
+ text: i18n.t('label.lineage'),
}}
/>
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/UserDataUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/UserDataUtils.ts
index 543adbeaf305..4e4d6005b0fb 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/UserDataUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/UserDataUtils.ts
@@ -17,7 +17,6 @@ import { get, isEqual } from 'lodash';
import { OidcUser } from '../components/Auth/AuthProviders/AuthProvider.interface';
import { updateUserDetail } from '../rest/userAPI';
import { User } from './../generated/entity/teams/user';
-import { getImages } from './CommonUtils';
import i18n from './i18next/LocalUtil';
import {
getImageWithResolutionAndFallback,
@@ -26,6 +25,28 @@ import {
import { showErrorToast } from './ToastUtils';
import userClassBase from './UserClassBase';
+export const imageTypes = {
+ image: 's96-c',
+ image192: 's192-c',
+ image24: 's24-c',
+ image32: 's32-c',
+ image48: 's48-c',
+ image512: 's512-c',
+ image72: 's72-c',
+};
+
+export const getImages = (imageUri: string) => {
+ const imagesObj: typeof imageTypes = imageTypes;
+ for (const type in imageTypes) {
+ imagesObj[type as keyof typeof imageTypes] = imageUri.replace(
+ 's96-c',
+ imageTypes[type as keyof typeof imageTypes]
+ );
+ }
+
+ return imagesObj;
+};
+
export const getUserDataFromOidc = (
userData: User,
oidcUser: OidcUser
@@ -34,10 +55,7 @@ export const getUserDataFromOidc = (
? getImages(oidcUser.profile.picture)
: undefined;
const profileEmail = oidcUser.profile.email;
- const email =
- profileEmail && profileEmail.indexOf('@') !== -1
- ? profileEmail
- : userData.email;
+ const email = profileEmail?.includes('@') ? profileEmail : userData.email;
return {
...userData,
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Users.util.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/Users.util.tsx
index 66b5e0e436f8..fa7779f5e4ec 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/Users.util.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/Users.util.tsx
@@ -27,7 +27,7 @@ import {
} from '../constants/constants';
import { MASKED_EMAIL } from '../constants/User.constants';
import { EntityReference, User } from '../generated/entity/teams/user';
-import { getIsErrorMatch } from './CommonUtils';
+import { getIsErrorMatch } from './APIUtils';
import { getEntityName } from './EntityUtils';
import { t } from './i18next/LocalUtil';
import { LIST_CAP } from './PermissionsUtils';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/WebAnalyticsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/WebAnalyticsUtils.ts
index 3c0610832a16..e17a1ff714b7 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/WebAnalyticsUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/WebAnalyticsUtils.ts
@@ -28,7 +28,7 @@ import {
import { PageViewEvent } from '../generated/analytics/webAnalyticEventType/pageViewEvent';
import { postWebAnalyticEvent } from '../rest/WebAnalyticsAPI';
import { AnalyticsData } from './../components/WebAnalytics/WebAnalytics.interface';
-import { getPathNameFromWindowLocation } from './RouterUtils';
+import { getPathNameFromWindowLocation } from './LocationUtils';
/**
* Check if url is valid or not and return the pathname
@@ -77,7 +77,8 @@ const handlePostAnalytic = async (
// collect the event data
await postWebAnalyticEvent(webAnalyticEventData);
} catch (error) {
- // silently ignore the error
+ // eslint-disable-next-line no-console
+ console.error('Error tracking web analytic event:', error);
}
};
@@ -92,7 +93,7 @@ export const trackPageView = (pageData: AnalyticsData, userId?: string) => {
const { payload } = pageData;
- const { location, navigator, performance, document } = window;
+ const { location, navigator, performance, document } = globalThis;
const { hostname } = location;
const pageLoadTime = getPageLoadTime(performance);
@@ -134,7 +135,7 @@ export const trackCustomEvent = (eventData: AnalyticsData, userId?: string) => {
const { payload } = eventData;
const { meta, event: eventValue } = payload;
- const { location } = window;
+ const { location } = globalThis;
// timestamp for the current event
const timestamp = meta.ts;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtil.ts b/openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtil.tsx
similarity index 66%
rename from openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtil.ts
rename to openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtil.tsx
index c702fa235fd2..840c6b8f54c0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtil.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtil.tsx
@@ -11,36 +11,26 @@
* limitations under the License.
*/
-import i18n, { t as i18nextT } from 'i18next';
+import i18next, { t as i18nextT } from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
-import { initReactI18next } from 'react-i18next';
-import { getInitOptions, languageMap } from './i18nextUtil';
-import { SupportedLocales } from './LocalUtil.interface';
+import { ReactNode } from 'react';
+import { initReactI18next, Trans } from 'react-i18next';
+import { getInitOptions } from './i18nextUtil';
+import localUtilClassBase from './LocalUtilClassBase';
-// Function to detect browser language
-export const detectBrowserLanguage = (): SupportedLocales => {
- const browserLang = navigator.language;
- const browserLangs = navigator.languages || [browserLang];
- let browserLanguage = undefined;
- for (const lang of browserLangs) {
- const langCode = lang.split('-')[0];
-
- if (languageMap[langCode]) {
- browserLanguage = languageMap[langCode];
-
- return browserLanguage;
+i18next
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init(getInitOptions())
+ .then(async () => {
+ if (i18next.language !== i18next.resolvedLanguage) {
+ await i18next.changeLanguage(i18next.language);
}
- }
+ });
- // English is the default language when we don't support browser language
- return browserLanguage ?? SupportedLocales.English;
-};
-
-// Initialize i18next (language)
-i18n
- .use(LanguageDetector) // Detects system language
- .use(initReactI18next)
- .init(getInitOptions());
+i18next.on('languageChanged', async (lng) => {
+ await localUtilClassBase.loadLocales(lng);
+});
export const t = (key: string, options?: Record): string => {
const translation = i18nextT(key, options);
@@ -79,4 +69,19 @@ export const translateWithNestedKeys = (
return t(label, translatedParams);
};
-export default i18n;
+export const Transi18next = ({
+ i18nKey,
+ values,
+ renderElement,
+ ...otherProps
+}: {
+ i18nKey: string;
+ values?: object;
+ renderElement: ReactNode;
+}): JSX.Element => (
+
+ {renderElement}
+
+);
+
+export default i18next;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtilClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtilClassBase.ts
new file mode 100644
index 000000000000..a7840219811a
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/i18next/LocalUtilClassBase.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2024 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import i18next from './LocalUtil';
+
+const LOCALE_LOADERS: Record<
+ string,
+ () => Promise<{ default: Record }>
+> = {
+ 'en-US': () => import('../../locale/languages/en-us.json'),
+ 'ko-KR': () => import('../../locale/languages/ko-kr.json'),
+ 'fr-FR': () => import('../../locale/languages/fr-fr.json'),
+ 'zh-CN': () => import('../../locale/languages/zh-cn.json'),
+ 'zh-TW': () => import('../../locale/languages/zh-tw.json'),
+ 'ja-JP': () => import('../../locale/languages/ja-jp.json'),
+ 'pt-BR': () => import('../../locale/languages/pt-br.json'),
+ 'pt-PT': () => import('../../locale/languages/pt-pt.json'),
+ 'es-ES': () => import('../../locale/languages/es-es.json'),
+ 'gl-ES': () => import('../../locale/languages/gl-es.json'),
+ 'ru-RU': () => import('../../locale/languages/ru-ru.json'),
+ 'de-DE': () => import('../../locale/languages/de-de.json'),
+ 'he-HE': () => import('../../locale/languages/he-he.json'),
+ 'nl-NL': () => import('../../locale/languages/nl-nl.json'),
+ 'pr-PR': () => import('../../locale/languages/pr-pr.json'),
+ 'th-TH': () => import('../../locale/languages/th-th.json'),
+ 'mr-IN': () => import('../../locale/languages/mr-in.json'),
+ 'tr-TR': () => import('../../locale/languages/tr-tr.json'),
+ 'ar-SA': () => import('../../locale/languages/ar-sa.json'),
+};
+
+class LocalUtilClassBase {
+ private static _instance: LocalUtilClassBase;
+
+ async loadLocales(locale: string): Promise {
+ if (i18next.hasResourceBundle(locale, 'translation')) {
+ return;
+ }
+
+ const loader = LOCALE_LOADERS[locale];
+ if (!loader) {
+ return;
+ }
+
+ const translations = await loader();
+ i18next.addResourceBundle(
+ locale,
+ 'translation',
+ translations.default,
+ true
+ );
+ }
+
+ static getInstance(): LocalUtilClassBase {
+ if (!LocalUtilClassBase._instance) {
+ LocalUtilClassBase._instance = new LocalUtilClassBase();
+ }
+
+ return LocalUtilClassBase._instance;
+ }
+}
+
+const localUtilClassBase = LocalUtilClassBase.getInstance();
+
+export { LocalUtilClassBase };
+
+export default localUtilClassBase;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/i18next/i18nextUtil.ts b/openmetadata-ui/src/main/resources/ui/src/utils/i18next/i18nextUtil.ts
index 7d6d527ee82c..b866263496d4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/i18next/i18nextUtil.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/i18next/i18nextUtil.ts
@@ -13,25 +13,7 @@
import i18next, { InitOptions } from 'i18next';
import { map, upperCase } from 'lodash';
-import arSA from '../../locale/languages/ar-sa.json';
-import deDe from '../../locale/languages/de-de.json';
import enUS from '../../locale/languages/en-us.json';
-import esES from '../../locale/languages/es-es.json';
-import frFR from '../../locale/languages/fr-fr.json';
-import glES from '../../locale/languages/gl-es.json';
-import heHE from '../../locale/languages/he-he.json';
-import jaJP from '../../locale/languages/ja-jp.json';
-import koKR from '../../locale/languages/ko-kr.json';
-import mrIN from '../../locale/languages/mr-in.json';
-import nlNL from '../../locale/languages/nl-nl.json';
-import prPR from '../../locale/languages/pr-pr.json';
-import ptBR from '../../locale/languages/pt-br.json';
-import ptPT from '../../locale/languages/pt-pt.json';
-import ruRU from '../../locale/languages/ru-ru.json';
-import thTH from '../../locale/languages/th-th.json';
-import trTR from '../../locale/languages/tr-tr.json';
-import zhCN from '../../locale/languages/zh-cn.json';
-import zhTW from '../../locale/languages/zh-tw.json';
import { SupportedLocales } from './LocalUtil.interface';
export const languageSelectOptions = map(SupportedLocales, (value, key) => ({
@@ -45,24 +27,6 @@ export const getInitOptions = (): InitOptions => {
supportedLngs: Object.values(SupportedLocales),
resources: {
'en-US': { translation: enUS },
- 'ko-KR': { translation: koKR },
- 'fr-FR': { translation: frFR },
- 'zh-CN': { translation: zhCN },
- 'zh-TW': { translation: zhTW },
- 'ja-JP': { translation: jaJP },
- 'pt-BR': { translation: ptBR },
- 'pt-PT': { translation: ptPT },
- 'es-ES': { translation: esES },
- 'gl-ES': { translation: glES },
- 'ru-RU': { translation: ruRU },
- 'de-DE': { translation: deDe },
- 'he-HE': { translation: heHE },
- 'nl-NL': { translation: nlNL },
- 'pr-PR': { translation: prPR },
- 'th-TH': { translation: thTH },
- 'mr-IN': { translation: mrIN },
- 'tr-TR': { translation: trTR },
- 'ar-SA': { translation: arSA },
},
fallbackLng: ['en-US'],
detection: {
@@ -70,12 +34,12 @@ export const getInitOptions = (): InitOptions => {
caches: ['cookie'], // cache user language on
},
interpolation: {
- escapeValue: false, // XSS safety provided by React
+ escapeValue: false,
},
missingKeyHandler: (_lngs, _ns, key) =>
// eslint-disable-next-line no-console
console.error(`i18next: key not found "${key}"`),
- saveMissing: true, // Required for missing key handler
+ saveMissing: true,
};
};
diff --git a/openmetadata-ui/src/main/resources/ui/vite.config.ts b/openmetadata-ui/src/main/resources/ui/vite.config.ts
index 9a02c03e3c6a..3052f0d6b65c 100644
--- a/openmetadata-ui/src/main/resources/ui/vite.config.ts
+++ b/openmetadata-ui/src/main/resources/ui/vite.config.ts
@@ -38,19 +38,19 @@ export default defineConfig(({ mode }) => {
// Don't replace ${basePath} placeholder - it will be replaced at runtime by Java backend
// Add ${basePath} prefix to asset paths (with or without leading slash)
return html
- .replace(
+ .replaceAll(
/(