@@ -37,13 +48,22 @@ jest.mock('../../../common/ResizablePanels/ResizablePanels', () => {
));
});
-describe.skip('GlossaryOverviewTab', () => {
+const onUpdate = jest.fn();
+
+describe('GlossaryOverviewTab', () => {
+ const selectedData = MOCKED_GLOSSARY_TERMS[0];
+ const permissions = MOCK_PERMISSIONS;
+
it('renders the component', async () => {
const { findByText } = render(
,
{ wrapper: MemoryRouter }
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.test.tsx
index 96517ebdfbd1..a38bbdb3af6d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.test.tsx
@@ -18,23 +18,19 @@ import {
} from '../../../../mocks/Glossary.mock';
import GlossaryTermReferences from './GlossaryTermReferences';
-const [mockGlossaryTerm1, mockGlossaryTerm2] = MOCKED_GLOSSARY_TERMS;
-
-const mockContext = {
- data: mockGlossaryTerm1,
- onUpdate: jest.fn(),
- isVersionView: false,
- permissions: MOCK_PERMISSIONS,
-};
-
-jest.mock('../../../GenericProvider/GenericProvider', () => ({
- useGenericContext: jest.fn().mockImplementation(() => mockContext),
-}));
+const mockOnGlossaryTermUpdate = jest.fn();
describe('GlossaryTermReferences', () => {
it('renders glossary term references', async () => {
- mockContext.data = mockGlossaryTerm2;
- const { getByText, getByTestId } = render(
);
+ const mockGlossaryTerm = MOCKED_GLOSSARY_TERMS[1];
+ const mockPermissions = MOCK_PERMISSIONS;
+ const { getByText, getByTestId } = render(
+
+ );
const sectionTitle = getByTestId('section-label.reference-plural');
const editBtn = getByTestId('edit-button');
@@ -57,18 +53,29 @@ describe('GlossaryTermReferences', () => {
});
it('renders add button', async () => {
- mockContext.data = mockGlossaryTerm1;
-
- const { getByTestId } = render(
);
+ const mockGlossaryTerm = MOCKED_GLOSSARY_TERMS[0];
+ const mockPermissions = MOCK_PERMISSIONS;
+ const { getByTestId } = render(
+
+ );
expect(getByTestId('term-references-add-button')).toBeInTheDocument();
});
it('should not render add button if no permission', async () => {
- mockContext.data = mockGlossaryTerm1;
- mockContext.permissions = { ...MOCK_PERMISSIONS, EditAll: false };
-
- const { queryByTestId, findByText } = render(
);
+ const mockGlossaryTerm = MOCKED_GLOSSARY_TERMS[0];
+ const mockPermissions = { ...MOCK_PERMISSIONS, EditAll: false };
+ const { queryByTestId, findByText } = render(
+
+ );
expect(queryByTestId('term-references-add-button')).toBeNull();
@@ -78,7 +85,15 @@ describe('GlossaryTermReferences', () => {
});
it('should not render edit button if no permission', async () => {
- const { queryByTestId } = render(
);
+ const mockGlossaryTerm = MOCKED_GLOSSARY_TERMS[1];
+ const mockPermissions = { ...MOCK_PERMISSIONS, EditAll: false };
+ const { queryByTestId } = render(
+
+ );
expect(queryByTestId('edit-button')).toBeNull();
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx
index a72a5a1a5a92..8f0dff3c8b71 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx
@@ -11,18 +11,26 @@
* limitations under the License.
*/
-import { Button, Space, Tooltip, Typography } from 'antd';
+import Icon from '@ant-design/icons/lib/components/Icon';
+import { Button, Space, Tag, Tooltip, Typography } from 'antd';
+import classNames from 'classnames';
import { t } from 'i18next';
import { cloneDeep, isEmpty, isEqual } from 'lodash';
import React, { useCallback, useEffect, useState } from 'react';
import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg';
+import { ReactComponent as ExternalLinkIcon } from '../../../../assets/svg/external-links.svg';
import { ReactComponent as PlusIcon } from '../../../../assets/svg/plus-primary.svg';
import {
DE_ACTIVE_COLOR,
+ ICON_DIMENSION,
NO_DATA_PLACEHOLDER,
+ SUCCESS_COLOR,
+ TEXT_BODY_COLOR,
+ TEXT_GREY_MUTED,
} from '../../../../constants/constants';
import { EntityField } from '../../../../constants/Feeds.constants';
import { NO_PERMISSION_FOR_ACTION } from '../../../../constants/HelperTextUtil';
+import { OperationPermission } from '../../../../context/PermissionProvider/PermissionProvider.interface';
import {
GlossaryTerm,
TermReference,
@@ -33,20 +41,25 @@ import {
getChangedEntityOldValue,
getDiffByFieldName,
} from '../../../../utils/EntityVersionUtils';
-import { renderReferenceElement } from '../../../../utils/GlossaryUtils';
+import { VersionStatus } from '../../../../utils/EntityVersionUtils.interface';
import TagButton from '../../../common/TagButton/TagButton.component';
-import { useGenericContext } from '../../../GenericProvider/GenericProvider';
import GlossaryTermReferencesModal from '../GlossaryTermReferencesModal.component';
-const GlossaryTermReferences = () => {
+interface GlossaryTermReferencesProps {
+ isVersionView?: boolean;
+ glossaryTerm: GlossaryTerm;
+ permissions: OperationPermission;
+ onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => Promise
;
+}
+
+const GlossaryTermReferences = ({
+ glossaryTerm,
+ permissions,
+ onGlossaryTermUpdate,
+ isVersionView,
+}: GlossaryTermReferencesProps) => {
const [references, setReferences] = useState([]);
const [isViewMode, setIsViewMode] = useState(true);
- const {
- data: glossaryTerm,
- onUpdate: onGlossaryTermUpdate,
- isVersionView,
- permissions,
- } = useGenericContext();
const handleReferencesSave = async (
newReferences: TermReference[],
@@ -78,6 +91,52 @@ const GlossaryTermReferences = () => {
setReferences(glossaryTerm.references ? glossaryTerm.references : []);
}, [glossaryTerm.references]);
+ const getReferenceElement = useCallback(
+ (ref: TermReference, versionStatus?: VersionStatus) => {
+ let iconColor: string;
+ let textClassName: string;
+ if (versionStatus?.added) {
+ iconColor = SUCCESS_COLOR;
+ textClassName = 'text-success';
+ } else if (versionStatus?.removed) {
+ iconColor = TEXT_GREY_MUTED;
+ textClassName = 'text-grey-muted';
+ } else {
+ iconColor = TEXT_BODY_COLOR;
+ textClassName = 'text-body';
+ }
+
+ return (
+
+
+
+
+
+ {ref.name}
+
+
+
+
+ );
+ },
+ []
+ );
+
const getVersionReferenceElements = useCallback(() => {
const changeDescription = glossaryTerm.changeDescription;
const referencesDiff = getDiffByFieldName(
@@ -113,14 +172,12 @@ const GlossaryTermReferences = () => {
return (
- {unchangedReferences.map((reference) =>
- renderReferenceElement(reference)
- )}
+ {unchangedReferences.map((reference) => getReferenceElement(reference))}
{addedReferences.map((reference) =>
- renderReferenceElement(reference, { added: true })
+ getReferenceElement(reference, { added: true })
)}
{deletedReferences.map((reference) =>
- renderReferenceElement(reference, { removed: true })
+ getReferenceElement(reference, { removed: true })
)}
);
@@ -163,7 +220,7 @@ const GlossaryTermReferences = () => {
getVersionReferenceElements()
) : (
- {references.map((ref) => renderReferenceElement(ref))}
+ {references.map((ref) => getReferenceElement(ref))}
{permissions.EditAll && references.length === 0 && (
({
- useGenericContext: jest.fn().mockImplementation(() => mockContext),
-}));
+const onGlossaryTermUpdate = jest.fn();
describe('GlossaryTermSynonyms', () => {
it('renders synonyms and edit button', () => {
- mockContext.data = mockGlossaryTerm2;
- const { getByTestId, getByText } = render( );
+ const glossaryTerm = MOCKED_GLOSSARY_TERMS[1];
+ const permissions = MOCK_PERMISSIONS;
+ const { getByTestId, getByText } = render(
+
+ );
const synonymsContainer = getByTestId('synonyms-container');
const synonymItem = getByText('accessory');
const editBtn = getByTestId('edit-button');
@@ -45,8 +41,15 @@ describe('GlossaryTermSynonyms', () => {
});
it('renders add button', () => {
- mockContext.data = mockGlossaryTerm1;
- const { getByTestId } = render( );
+ const glossaryTerm = MOCKED_GLOSSARY_TERMS[0];
+ const permissions = MOCK_PERMISSIONS;
+ const { getByTestId } = render(
+
+ );
const synonymsContainer = getByTestId('synonyms-container');
const synonymAddBtn = getByTestId('synonym-add-button');
@@ -55,10 +58,14 @@ describe('GlossaryTermSynonyms', () => {
});
it('should not render add button if no permission', async () => {
- mockContext.data = mockGlossaryTerm1;
- mockContext.permissions = { ...MOCK_PERMISSIONS, EditAll: false };
+ const glossaryTerm = MOCKED_GLOSSARY_TERMS[0];
+ const permissions = { ...MOCK_PERMISSIONS, EditAll: false };
const { getByTestId, queryByTestId, findByText } = render(
-
+
);
const synonymsContainer = getByTestId('synonyms-container');
const synonymAddBtn = queryByTestId('synonym-add-button');
@@ -72,9 +79,15 @@ describe('GlossaryTermSynonyms', () => {
});
it('should not render edit button if no permission', () => {
- mockContext.data = mockGlossaryTerm2;
- mockContext.permissions = { ...MOCK_PERMISSIONS, EditAll: false };
- const { getByTestId, queryByTestId } = render( );
+ const glossaryTerm = MOCKED_GLOSSARY_TERMS[1];
+ const permissions = { ...MOCK_PERMISSIONS, EditAll: false };
+ const { getByTestId, queryByTestId } = render(
+
+ );
const synonymsContainer = getByTestId('synonyms-container');
const editBtn = queryByTestId('edit-button');
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx
index a1fb34d9a824..e66fdc58c635 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx
@@ -24,6 +24,7 @@ import {
} from '../../../../constants/constants';
import { EntityField } from '../../../../constants/Feeds.constants';
import { NO_PERMISSION_FOR_ACTION } from '../../../../constants/HelperTextUtil';
+import { OperationPermission } from '../../../../context/PermissionProvider/PermissionProvider.interface';
import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm';
import { ChangeDescription } from '../../../../generated/entity/type';
import {
@@ -32,18 +33,23 @@ import {
getDiffByFieldName,
} from '../../../../utils/EntityVersionUtils';
import TagButton from '../../../common/TagButton/TagButton.component';
-import { useGenericContext } from '../../../GenericProvider/GenericProvider';
-const GlossaryTermSynonyms = () => {
+interface GlossaryTermSynonymsProps {
+ isVersionView?: boolean;
+ permissions: OperationPermission;
+ glossaryTerm: GlossaryTerm;
+ onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => Promise;
+}
+
+const GlossaryTermSynonyms = ({
+ permissions,
+ glossaryTerm,
+ onGlossaryTermUpdate,
+ isVersionView,
+}: GlossaryTermSynonymsProps) => {
const [isViewMode, setIsViewMode] = useState(true);
const [synonyms, setSynonyms] = useState([]);
const [saving, setSaving] = useState(false);
- const {
- data: glossaryTerm,
- onUpdate: onGlossaryTermUpdate,
- isVersionView,
- permissions,
- } = useGenericContext();
const getSynonyms = () => (
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.test.tsx
index 02efd7c68c5e..23a3481a6a89 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.test.tsx
@@ -18,40 +18,57 @@ import {
} from '../../../../mocks/Glossary.mock';
import RelatedTerms from './RelatedTerms';
-const mockContext = {
- data: MOCKED_GLOSSARY_TERMS[2],
- onUpdate: jest.fn(),
- isVersionView: false,
- permissions: MOCK_PERMISSIONS,
-};
+const glossaryTerm = MOCKED_GLOSSARY_TERMS[2];
-jest.mock('../../../GenericProvider/GenericProvider', () => ({
- useGenericContext: jest.fn().mockImplementation(() => mockContext),
-}));
+const permissions = MOCK_PERMISSIONS;
+
+const onGlossaryTermUpdate = jest.fn();
describe('RelatedTerms', () => {
it('should render the component', () => {
- const { container } = render(
);
+ const { container } = render(
+
+ );
expect(container).toBeInTheDocument();
});
it('should show the related terms', () => {
- const { getByText } = render(
);
+ const { getByText } = render(
+
+ );
expect(getByText('Business Customer')).toBeInTheDocument();
});
it('should show the add button if there are no related terms and the user has edit permissions', () => {
- mockContext.data = { ...mockContext.data, relatedTerms: [] };
- const { getByTestId } = render(
);
+ const { getByTestId } = render(
+
+ );
expect(getByTestId('related-term-add-button')).toBeInTheDocument();
});
it('should not show the add button if there are no related terms and the user does not have edit permissions', async () => {
- mockContext.permissions = { ...mockContext.permissions, EditAll: false };
- const { queryByTestId, findByText } = render(
);
+ const { queryByTestId, findByText } = render(
+
+ );
expect(queryByTestId('related-term-add-button')).toBeNull();
@@ -61,16 +78,25 @@ describe('RelatedTerms', () => {
});
it('should show the edit button if there are related terms and the user has edit permissions', () => {
- mockContext.permissions = MOCK_PERMISSIONS;
- mockContext.data = { ...MOCKED_GLOSSARY_TERMS[2] };
- const { getByTestId } = render(
);
+ const { getByTestId } = render(
+
+ );
expect(getByTestId('edit-button')).toBeInTheDocument();
});
it('should not show the edit button if there are no related terms and the user has edit permissions', () => {
- mockContext.data = { ...MOCKED_GLOSSARY_TERMS[2], relatedTerms: [] };
- const { queryByTestId } = render(
);
+ const { queryByTestId } = render(
+
+ );
expect(queryByTestId('edit-button')).toBeNull();
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx
index d9a528bb19e7..d0221548db31 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx
@@ -27,6 +27,7 @@ import {
} from '../../../../constants/constants';
import { EntityField } from '../../../../constants/Feeds.constants';
import { NO_PERMISSION_FOR_ACTION } from '../../../../constants/HelperTextUtil';
+import { OperationPermission } from '../../../../context/PermissionProvider/PermissionProvider.interface';
import { EntityType } from '../../../../enums/entity.enum';
import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm';
import {
@@ -46,17 +47,21 @@ import { VersionStatus } from '../../../../utils/EntityVersionUtils.interface';
import { getGlossaryPath } from '../../../../utils/RouterUtils';
import { SelectOption } from '../../../common/AsyncSelectList/AsyncSelectList.interface';
import TagButton from '../../../common/TagButton/TagButton.component';
-import { useGenericContext } from '../../../GenericProvider/GenericProvider';
-const RelatedTerms = () => {
- const history = useHistory();
- const {
- data: glossaryTerm,
- onUpdate,
- isVersionView,
- permissions,
- } = useGenericContext
();
+interface RelatedTermsProps {
+ isVersionView?: boolean;
+ permissions: OperationPermission;
+ glossaryTerm: GlossaryTerm;
+ onGlossaryTermUpdate: (data: GlossaryTerm) => Promise;
+}
+const RelatedTerms = ({
+ isVersionView,
+ glossaryTerm,
+ permissions,
+ onGlossaryTermUpdate,
+}: RelatedTermsProps) => {
+ const history = useHistory();
const [isIconVisible, setIsIconVisible] = useState(true);
const [selectedOption, setSelectedOption] = useState([]);
@@ -99,7 +104,7 @@ const RelatedTerms = () => {
relatedTerms: newOptions,
};
- await onUpdate(updatedGlossaryTerm);
+ await onGlossaryTermUpdate(updatedGlossaryTerm);
setIsIconVisible(true);
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx
index 87ac05380160..c92528e87b93 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx
@@ -25,12 +25,11 @@ import {
OperationPermission,
ResourceEntity,
} from '../../context/PermissionProvider/PermissionProvider.interface';
-import { EntityAction, EntityTabs } from '../../enums/entity.enum';
+import { EntityAction } from '../../enums/entity.enum';
import {
CreateThread,
ThreadType,
} from '../../generated/api/feed/createThread';
-import { Glossary } from '../../generated/entity/data/glossary';
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
import { VERSION_VIEW_GLOSSARY_PERMISSION } from '../../mocks/Glossary.mock';
import { postThread } from '../../rest/feedsAPI';
@@ -263,7 +262,7 @@ const GlossaryV1 = ({
history.push(
getGlossaryTermDetailsPath(
selectedData.fullyQualifiedName || '',
- EntityTabs.TERMS
+ 'terms'
)
);
}
@@ -341,17 +340,6 @@ const GlossaryV1 = ({
}
};
- const handleGlossaryUpdate = async (newGlossary: Glossary) => {
- const jsonPatch = compare(selectedData, newGlossary);
-
- const shouldRefreshTerms = jsonPatch.some((patch) =>
- patch.path.startsWith('/owners')
- );
-
- await updateGlossary(newGlossary);
- shouldRefreshTerms && loadGlossaryTerms(true);
- };
-
const initPermissions = async () => {
setIsPermissionLoading(true);
const permissionFetch = isGlossaryActive
@@ -393,7 +381,7 @@ const GlossaryV1 = ({
permissions={glossaryPermission}
refreshGlossaryTerms={() => loadGlossaryTerms(true)}
termsLoading={isTermsLoading}
- updateGlossary={handleGlossaryUpdate}
+ updateGlossary={updateGlossary}
updateVote={updateVote}
onAddGlossaryTerm={(term) =>
handleGlossaryTermModalAction(false, term ?? null)
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.component.tsx
index ec9fd1069182..c983a2a6abdc 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.component.tsx
@@ -15,9 +15,9 @@ import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ENTITY_NAME_REGEX } from '../../../constants/regex.constants';
import SanitizedInput from '../../common/SanitizedInput/SanitizedInput';
-import { EntityName, EntityNameModalProps } from './EntityNameModal.interface';
+import { EntityNameModalProps } from './EntityNameModal.interface';
-const EntityNameModal = ({
+const EntityNameModal: React.FC = ({
visible,
entity,
onCancel,
@@ -28,7 +28,7 @@ const EntityNameModal = ({
allowRename = false,
nameValidationRules = [],
additionalFields,
-}: EntityNameModalProps) => {
+}) => {
const { t } = useTranslation();
const [form] = Form.useForm();
const [isLoading, setIsLoading] = useState(false);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.interface.ts
index 2106081055e3..9d217278538e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.interface.ts
@@ -19,14 +19,12 @@ export type EntityNameWithAdditionFields = EntityName & {
constraint: Constraint;
};
-export interface EntityNameModalProps<
- T extends { name: string; displayName?: string }
-> {
+export interface EntityNameModalProps {
visible: boolean;
allowRename?: boolean;
onCancel: () => void;
- onSave: (obj: T) => void | Promise;
- entity: T;
+ onSave: (obj: EntityName) => void | Promise;
+ entity: Partial;
title: string;
nameValidationRules?: Rule[];
additionalFields?: React.ReactNode;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddDetailsPageWidgetModal/AddDetailsPageWidgetModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddDetailsPageWidgetModal/AddDetailsPageWidgetModal.tsx
deleted file mode 100644
index 0b19ee104540..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddDetailsPageWidgetModal/AddDetailsPageWidgetModal.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2023 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 { CheckOutlined } from '@ant-design/icons';
-import { Modal, Space, Tabs, TabsProps } from 'antd';
-import { isEmpty, toString } from 'lodash';
-import { default as React, useCallback, useMemo } from 'react';
-import { useTranslation } from 'react-i18next';
-import { LIGHT_GREEN_COLOR } from '../../../../constants/constants';
-import {
- CommonWidgetType,
- GridSizes,
-} from '../../../../constants/CustomizeWidgets.constants';
-import { ERROR_PLACEHOLDER_TYPE } from '../../../../enums/common.enum';
-import { WidgetWidths } from '../../../../enums/CustomizablePage.enum';
-import { Document } from '../../../../generated/entity/docStore/document';
-import { getWidgetWidthLabelFromKey } from '../../../../utils/CustomizableLandingPageUtils';
-import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
-import { WidgetSizeInfo } from '../AddWidgetModal/AddWidgetModal.interface';
-import AddWidgetTabContent from '../AddWidgetModal/AddWidgetTabContent';
-
-interface Props {
- open: boolean;
- maxGridSizeSupport: number;
- placeholderWidgetKey: string;
- addedWidgetsList: Array;
- handleCloseAddWidgetModal: () => void;
- handleAddWidget: (
- widget: CommonWidgetType,
- widgetKey: string,
- widgetSize: number
- ) => void;
- widgetsList: Array;
-}
-
-function AddDetailsPageWidgetModal({
- open,
- widgetsList,
- addedWidgetsList,
- handleCloseAddWidgetModal,
- handleAddWidget,
- maxGridSizeSupport,
- placeholderWidgetKey,
-}: Readonly) {
- const { t } = useTranslation();
-
- const getAddWidgetHandler = useCallback(
- (widget: Document, widgetSize: number) => () =>
- handleAddWidget(
- widget as unknown as CommonWidgetType,
- placeholderWidgetKey,
- widgetSize
- ),
- [handleAddWidget, placeholderWidgetKey]
- );
-
- const tabItems: TabsProps['items'] = useMemo(
- () =>
- widgetsList?.map((widget) => {
- const widgetSizeOptions: Array =
- widget.data.gridSizes.map((size: GridSizes) => ({
- label: (
-
- {getWidgetWidthLabelFromKey(toString(size))}
-
- ),
- value: WidgetWidths[size],
- }));
-
- return {
- label: (
-
- {widget.name}
- {addedWidgetsList.some(
- (w) =>
- w.startsWith(widget.fullyQualifiedName) &&
- !w.includes('EmptyWidgetPlaceholder')
- ) && (
-
- )}
-
- ),
- key: widget.fullyQualifiedName,
- children: (
-
- ),
- };
- }),
- [widgetsList, addedWidgetsList, getAddWidgetHandler, maxGridSizeSupport]
- );
-
- const widgetsInfo = useMemo(() => {
- if (isEmpty(widgetsList)) {
- return (
-
- {t('message.no-widgets-to-add')}
-
- );
- }
-
- return (
-
- );
- }, [widgetsList, tabItems]);
-
- return (
-
- {widgetsInfo}
-
- );
-}
-
-export default AddDetailsPageWidgetModal;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.test.tsx
index 276df5dba826..22fe5a7a5aa2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.test.tsx
@@ -28,7 +28,7 @@ const mockProps: AddWidgetTabContentProps = {
widgetSizeOptions: mockWidgetSizes,
};
-jest.mock('../../../../utils/CustomizeMyDataPageClassBase', () => ({
+jest.mock('../../../../utils/CustomizePageClassBase', () => ({
getWidgetImageFromKey: jest.fn().mockImplementation(() => ''),
}));
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.tsx
index 50d44a2f95bb..2d02ad294156 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.tsx
@@ -25,7 +25,7 @@ import {
} from 'antd';
import React, { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import customizePageClassBase from '../../../../utils/CustomizeMyDataPageClassBase';
+import customizePageClassBase from '../../../../utils/CustomizePageClassBase';
import { AddWidgetTabContentProps } from './AddWidgetModal.interface';
function AddWidgetTabContent({
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage.tsx
deleted file mode 100644
index 772fcfc3174b..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2023 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 React, { useCallback, useState } from 'react';
-import { useTranslation } from 'react-i18next';
-import gridBgImg from '../../../../assets/img/grid-bg-img.png';
-import { Page } from '../../../../generated/system/ui/page';
-import { useGridLayoutDirection } from '../../../../hooks/useGridLayoutDirection';
-import { WidgetConfig } from '../../../../pages/CustomizablePage/CustomizablePage.interface';
-import { useCustomizeStore } from '../../../../pages/CustomizablePage/CustomizeStore';
-import '../../../../pages/MyDataPage/my-data.less';
-import customizeGlossaryTermPageClassBase from '../../../../utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage';
-import {
- getLayoutWithEmptyWidgetPlaceholder,
- getUniqueFilteredLayout,
-} from '../../../../utils/CustomizableLandingPageUtils';
-import { getEntityName } from '../../../../utils/EntityUtils';
-import { CustomizeTabWidget } from '../../../Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget';
-import { GlossaryHeaderWidget } from '../../../Glossary/GlossaryHeader/GlossaryHeaderWidget';
-import PageLayoutV1 from '../../../PageLayoutV1/PageLayoutV1';
-import { CustomizablePageHeader } from '../CustomizablePageHeader/CustomizablePageHeader';
-import { CustomizeMyDataProps } from '../CustomizeMyData/CustomizeMyData.interface';
-
-function CustomizeGlossaryTermDetailPage({
- personaDetails,
- onSaveLayout,
- isGlossary,
-}: Readonly) {
- const { t } = useTranslation();
- const { currentPage, currentPageType } = useCustomizeStore();
-
- const [layout, setLayout] = useState>(
- (currentPage?.layout as WidgetConfig[]) ??
- customizeGlossaryTermPageClassBase.defaultLayout
- );
-
- const handleReset = useCallback(async () => {
- // Get default layout with the empty widget added at the end
- const newMainPanelLayout = getLayoutWithEmptyWidgetPlaceholder(
- customizeGlossaryTermPageClassBase.defaultLayout,
- 2,
- 4
- );
- setLayout(newMainPanelLayout);
- await onSaveLayout();
- }, []);
-
- const handleSave = async () => {
- await onSaveLayout({
- ...(currentPage ?? ({ pageType: currentPageType } as Page)),
- layout: getUniqueFilteredLayout(layout),
- });
- };
-
- // call the hook to set the direction of the grid layout
- useGridLayoutDirection();
-
- return (
-
-
-
-
-
- );
-}
-
-export default CustomizeGlossaryTermDetailPage;
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
deleted file mode 100644
index 2257b40ed277..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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 { Button, Col, Modal, Space, Typography } from 'antd';
-import { startCase } from 'lodash';
-import React, { useCallback, useMemo } from 'react';
-import { useTranslation } from 'react-i18next';
-import { Link, useHistory } from 'react-router-dom';
-import { useApplicationStore } from '../../../../hooks/useApplicationStore';
-import { useFqn } from '../../../../hooks/useFqn';
-import { useCustomizeStore } from '../../../../pages/CustomizablePage/CustomizeStore';
-import { Transi18next } from '../../../../utils/CommonUtils';
-import { getPersonaDetailsPath } from '../../../../utils/RouterUtils';
-
-export const CustomizablePageHeader = ({
- onReset,
- onSave,
- personaName,
-}: {
- onSave: () => Promise;
- onReset: () => void;
- personaName: string;
-}) => {
- const { t } = useTranslation();
- const { fqn: personaFqn } = useFqn();
- const { currentPageType } = useCustomizeStore();
- const history = useHistory();
- const [isResetModalOpen, setIsResetModalOpen] = React.useState(false);
- const [saving, setSaving] = React.useState(false);
- const { theme } = useApplicationStore();
-
- const handleCancel = () => {
- history.push(getPersonaDetailsPath(personaFqn));
- };
-
- const handleOpenResetModal = useCallback(() => {
- setIsResetModalOpen(true);
- }, []);
-
- const handleCloseResetModal = useCallback(() => {
- setIsResetModalOpen(false);
- }, []);
-
- const handleSave = useCallback(async () => {
- setSaving(true);
- await onSave();
- setSaving(false);
- }, [onSave]);
-
- const handleReset = useCallback(async () => {
- onReset();
- setIsResetModalOpen(false);
- }, [onReset]);
- const i18Values = useMemo(
- () => ({
- persona: personaName,
- pageName: startCase(currentPageType as string) ?? t('label.landing-page'),
- }),
- [personaName]
- );
-
- return (
-
-
-
-
- }
- values={i18Values}
- />
-
-
-
-
- {t('label.cancel')}
-
-
- {t('label.reset')}
-
-
- {t('label.save')}
-
-
- {isResetModalOpen && (
-
- {t('message.reset-layout-confirmation')}
-
- )}
-
- );
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface.ts
index b867f91d0da1..18d402af23a0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface.ts
@@ -11,12 +11,13 @@
* limitations under the License.
*/
+import { Document } from '../../../../generated/entity/docStore/document';
import { Persona } from '../../../../generated/entity/teams/persona';
-import { Page } from '../../../../generated/system/ui/page';
export interface CustomizeMyDataProps {
personaDetails?: Persona;
- isGlossary?: boolean;
- initialPageData: Page | null;
- onSaveLayout: (page?: Page) => Promise;
+ initialPageData: Document;
+ onSaveLayout: () => Promise;
+ handlePageDataChange: (newPageData: Document) => void;
+ handleSaveCurrentPageLayout: (value: boolean) => void;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.test.tsx
index 5a42979f77a9..1791575e2ebc 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.test.tsx
@@ -18,18 +18,22 @@ import { PageType } from '../../../../generated/system/ui/page';
import {
mockActiveAnnouncementData,
mockCustomizePageClassBase,
+ mockDefaultLayout,
mockDocumentData,
mockPersonaName,
mockUserData,
} from '../../../../mocks/MyDataPage.mock';
+import { WidgetConfig } from '../../../../pages/CustomizablePage/CustomizablePage.interface';
import CustomizeMyData from './CustomizeMyData';
import { CustomizeMyDataProps } from './CustomizeMyData.interface';
const mockPush = jest.fn();
const mockProps: CustomizeMyDataProps = {
- initialPageData: mockDocumentData.data.pages[0],
+ initialPageData: mockDocumentData,
onSaveLayout: jest.fn(),
+ handlePageDataChange: jest.fn(),
+ handleSaveCurrentPageLayout: jest.fn(),
};
jest.mock(
@@ -64,7 +68,7 @@ jest.mock(
}
);
-jest.mock('../../../../utils/CustomizeMyDataPageClassBase', () => {
+jest.mock('../../../../utils/CustomizePageClassBase', () => {
return mockCustomizePageClassBase;
});
@@ -161,7 +165,9 @@ describe('CustomizeMyData component', () => {
await act(async () => userEvent.click(cancelButton));
- expect(mockPush).toHaveBeenCalledWith('/settings/persona/testPersona');
+ expect(mockPush).toHaveBeenCalledWith(
+ '/settings/preferences/customizeLandingPage'
+ );
});
it('CustomizeMyData should display reset layout confirmation modal on click of reset button', async () => {
@@ -181,6 +187,9 @@ describe('CustomizeMyData component', () => {
render( );
});
+ // handlePageDataChange is called 1 time on mount
+ expect(mockProps.handlePageDataChange).toHaveBeenCalledTimes(1);
+
const resetButton = screen.getByTestId('reset-button');
await act(async () => userEvent.click(resetButton));
@@ -191,6 +200,19 @@ describe('CustomizeMyData component', () => {
await act(async () => userEvent.click(yesButton));
+ expect(mockProps.handlePageDataChange).toHaveBeenCalledTimes(3);
+ // Check if the handlePageDataChange is passed an object with the default layout
+ expect(mockProps.handlePageDataChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ ...mockDocumentData,
+ data: {
+ page: {
+ layout: expect.arrayContaining(mockDefaultLayout),
+ },
+ },
+ })
+ );
+
expect(screen.queryByTestId('reset-layout-modal')).toBeNull();
});
@@ -199,6 +221,9 @@ describe('CustomizeMyData component', () => {
render( );
});
+ // handlePageDataChange is called 1 time on mount
+ expect(mockProps.handlePageDataChange).toHaveBeenCalledTimes(1);
+
const resetButton = screen.getByTestId('reset-button');
await act(async () => userEvent.click(resetButton));
@@ -209,6 +234,9 @@ describe('CustomizeMyData component', () => {
await act(async () => userEvent.click(noButton));
+ // handlePageDataChange is not called again
+ expect(mockProps.handlePageDataChange).toHaveBeenCalledTimes(1);
+
expect(screen.queryByTestId('reset-layout-modal')).toBeNull();
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.tsx
index b875cfa3aded..5d2fb94458e2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.tsx
@@ -11,24 +11,30 @@
* limitations under the License.
*/
+import { Button, Col, Modal, Space, Typography } from 'antd';
import { AxiosError } from 'axios';
-import { isEmpty } from 'lodash';
+import { isEmpty, isNil } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import RGL, { Layout, WidthProvider } from 'react-grid-layout';
import { useTranslation } from 'react-i18next';
+import { Link, useHistory } from 'react-router-dom';
import gridBgImg from '../../../../assets/img/grid-bg-img.png';
import { KNOWLEDGE_LIST_LENGTH } from '../../../../constants/constants';
+import {
+ GlobalSettingOptions,
+ GlobalSettingsMenuCategory,
+} from '../../../../constants/GlobalSettings.constants';
import { LandingPageWidgetKeys } from '../../../../enums/CustomizablePage.enum';
import { SearchIndex } from '../../../../enums/search.enum';
import { Document } from '../../../../generated/entity/docStore/document';
import { EntityReference } from '../../../../generated/entity/type';
-import { Page } from '../../../../generated/system/ui/page';
-import { PageType } from '../../../../generated/system/ui/uiCustomization';
import { useApplicationStore } from '../../../../hooks/useApplicationStore';
+import { useFqn } from '../../../../hooks/useFqn';
import { useGridLayoutDirection } from '../../../../hooks/useGridLayoutDirection';
import { WidgetConfig } from '../../../../pages/CustomizablePage/CustomizablePage.interface';
import '../../../../pages/MyDataPage/my-data.less';
import { searchQuery } from '../../../../rest/searchAPI';
+import { Transi18next } from '../../../../utils/CommonUtils';
import {
getAddWidgetHandler,
getLayoutUpdateHandler,
@@ -37,13 +43,16 @@ import {
getUniqueFilteredLayout,
getWidgetFromKey,
} from '../../../../utils/CustomizableLandingPageUtils';
-import customizeMyDataPageClassBase from '../../../../utils/CustomizeMyDataPageClassBase';
+import customizePageClassBase from '../../../../utils/CustomizePageClassBase';
import { getEntityName } from '../../../../utils/EntityUtils';
+import {
+ getPersonaDetailsPath,
+ getSettingPath,
+} from '../../../../utils/RouterUtils';
import { showErrorToast } from '../../../../utils/ToastUtils';
import ActivityFeedProvider from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
import PageLayoutV1 from '../../../PageLayoutV1/PageLayoutV1';
import AddWidgetModal from '../AddWidgetModal/AddWidgetModal';
-import { CustomizablePageHeader } from '../CustomizablePageHeader/CustomizablePageHeader';
import './customize-my-data.less';
import { CustomizeMyDataProps } from './CustomizeMyData.interface';
@@ -53,14 +62,17 @@ function CustomizeMyData({
personaDetails,
initialPageData,
onSaveLayout,
+ handlePageDataChange,
+ handleSaveCurrentPageLayout,
}: Readonly) {
const { t } = useTranslation();
- const { currentUser } = useApplicationStore();
-
+ const { currentUser, theme } = useApplicationStore();
+ const history = useHistory();
+ const { fqn: decodedPersonaFQN } = useFqn();
const [layout, setLayout] = useState>(
getLayoutWithEmptyWidgetPlaceholder(
- (initialPageData?.layout as WidgetConfig[]) ??
- customizeMyDataPageClassBase.defaultLayout,
+ initialPageData.data?.page?.layout ??
+ customizePageClassBase.defaultLayout,
2,
4
)
@@ -70,10 +82,11 @@ function CustomizeMyData({
LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER
);
const [isWidgetModalOpen, setIsWidgetModalOpen] = useState(false);
-
+ const [isResetModalOpen, setIsResetModalOpen] = useState(false);
const [followedData, setFollowedData] = useState>([]);
const [followedDataCount, setFollowedDataCount] = useState(0);
const [isLoadingOwnedData, setIsLoadingOwnedData] = useState(false);
+ const [saving, setSaving] = useState(false);
const handlePlaceholderWidgetKey = useCallback((value: string) => {
setPlaceholderWidgetKey(value);
@@ -94,7 +107,7 @@ function CustomizeMyData({
newWidgetData,
placeholderWidgetKey,
widgetSize,
- customizeMyDataPageClassBase.landingPageMaxGridSize
+ customizePageClassBase.landingPageMaxGridSize
)
);
setIsWidgetModalOpen(false);
@@ -111,6 +124,14 @@ function CustomizeMyData({
[layout]
);
+ const handleOpenResetModal = useCallback(() => {
+ setIsResetModalOpen(true);
+ }, []);
+
+ const handleCloseResetModal = useCallback(() => {
+ setIsResetModalOpen(false);
+ }, []);
+
const handleOpenAddWidgetModal = useCallback(() => {
setIsWidgetModalOpen(true);
}, []);
@@ -176,15 +197,44 @@ function CustomizeMyData({
]
);
+ useEffect(() => {
+ handlePageDataChange({
+ ...initialPageData,
+ data: {
+ page: {
+ layout: getUniqueFilteredLayout(layout),
+ },
+ },
+ });
+ }, [layout]);
+
+ const handleCancel = useCallback(() => {
+ history.push(
+ getSettingPath(
+ GlobalSettingsMenuCategory.PREFERENCES,
+ GlobalSettingOptions.CUSTOMIZE_LANDING_PAGE
+ )
+ );
+ }, []);
+
const handleReset = useCallback(() => {
// Get default layout with the empty widget added at the end
const newMainPanelLayout = getLayoutWithEmptyWidgetPlaceholder(
- customizeMyDataPageClassBase.defaultLayout,
+ customizePageClassBase.defaultLayout,
2,
4
);
setLayout(newMainPanelLayout);
- onSaveLayout();
+ handlePageDataChange({
+ ...initialPageData,
+ data: {
+ page: {
+ layout: getUniqueFilteredLayout(newMainPanelLayout),
+ },
+ },
+ });
+ handleSaveCurrentPageLayout(true);
+ setIsResetModalOpen(false);
}, []);
useEffect(() => {
@@ -192,13 +242,10 @@ function CustomizeMyData({
}, []);
const handleSave = async () => {
- await onSaveLayout({
- ...(initialPageData ??
- ({
- pageType: PageType.LandingPage,
- } as Page)),
- layout: getUniqueFilteredLayout(layout),
- });
+ setSaving(true);
+ await onSaveLayout();
+
+ setSaving(false);
};
// call the hook to set the direction of the grid layout
@@ -207,6 +254,59 @@ function CustomizeMyData({
return (
+
+
+
+ }
+ values={{
+ persona: isNil(personaDetails)
+ ? decodedPersonaFQN
+ : getEntityName(personaDetails),
+ }}
+ />
+
+
+
+
+ {t('label.cancel')}
+
+
+ {t('label.reset')}
+
+
+ {t('label.save')}
+
+
+
+ }
+ headerClassName="m-0 p-0"
mainContainerClassName="p-t-0"
pageContainerStyle={{
backgroundImage: `url(${gridBgImg})`,
@@ -214,21 +314,16 @@ function CustomizeMyData({
pageTitle={t('label.customize-entity', {
entity: t('label.landing-page'),
})}>
-
{widgets}
@@ -239,13 +334,24 @@ function CustomizeMyData({
addedWidgetsList={addedWidgetsList}
handleAddWidget={handleMainPanelAddWidget}
handleCloseAddWidgetModal={handleCloseAddWidgetModal}
- maxGridSizeSupport={
- customizeMyDataPageClassBase.landingPageMaxGridSize
- }
+ maxGridSizeSupport={customizePageClassBase.landingPageMaxGridSize}
open={isWidgetModalOpen}
placeholderWidgetKey={placeholderWidgetKey}
/>
)}
+ {isResetModalOpen && (
+
+ {t('message.reset-layout-confirmation')}
+
+ )}
);
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.component.tsx
index 78fc05cd0ec1..f08046d34e6c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.component.tsx
@@ -13,8 +13,8 @@
import { Button, Col, Menu, MenuProps, Row, Typography } from 'antd';
import Modal from 'antd/lib/modal/Modal';
import classNames from 'classnames';
-import { isEmpty, noop } from 'lodash';
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import { noop } from 'lodash';
+import React, { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import {
@@ -25,15 +25,8 @@ import {
import { SidebarItem } from '../../../enums/sidebar.enum';
import leftSidebarClassBase from '../../../utils/LeftSidebarClassBase';
-import { EntityType } from '../../../enums/entity.enum';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import useCustomLocation from '../../../hooks/useCustomLocation/useCustomLocation';
-import { useCustomizeStore } from '../../../pages/CustomizablePage/CustomizeStore';
-import { getDocumentByFQN } from '../../../rest/DocStoreAPI';
-import {
- filterAndArrangeTreeByKeys,
- getNestedKeysFromNavigationItems,
-} from '../../../utils/CustomizaNavigation/CustomizeNavigation';
import BrandImage from '../../common/BrandImage/BrandImage';
import './left-sidebar.less';
import { LeftSidebarItem as LeftSidebarItemType } from './LeftSidebar.interface';
@@ -45,21 +38,8 @@ const LeftSidebar = () => {
const { onLogoutHandler } = useApplicationStore();
const [showConfirmLogoutModal, setShowConfirmLogoutModal] = useState(false);
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(true);
- const { selectedPersona } = useApplicationStore();
-
- const { currentPersonaDocStore, setCurrentPersonaDocStore } =
- useCustomizeStore();
-
- const navigationItems = useMemo(() => {
- return currentPersonaDocStore?.data?.navigation;
- }, [currentPersonaDocStore]);
- const sideBarItems = isEmpty(navigationItems)
- ? leftSidebarClassBase.getSidebarItems()
- : filterAndArrangeTreeByKeys(
- leftSidebarClassBase.getSidebarItems(),
- getNestedKeysFromNavigationItems(navigationItems)
- );
+ const sideBarItems = leftSidebarClassBase.getSidebarItems();
const selectedKeys = useMemo(() => {
const pathArray = location.pathname.split('/');
@@ -78,6 +58,23 @@ const LeftSidebar = () => {
setShowConfirmLogoutModal(false);
};
+ const TOP_SIDEBAR_MENU_ITEMS: MenuProps['items'] = useMemo(() => {
+ return [
+ ...sideBarItems.map((item) => {
+ return {
+ key: item.key,
+ label: ,
+ children: item.children?.map((item: LeftSidebarItemType) => {
+ return {
+ key: item.key,
+ label: ,
+ };
+ }),
+ };
+ }),
+ ];
+ }, []);
+
const LOWER_SIDEBAR_TOP_SIDEBAR_MENU_ITEMS: MenuProps['items'] = useMemo(
() =>
[SETTING_ITEM, LOGOUT_ITEM].map((item) => ({
@@ -106,23 +103,6 @@ const LeftSidebar = () => {
setIsSidebarCollapsed(true);
}, []);
- const fetchCustomizedDocStore = useCallback(async (personaFqn: string) => {
- try {
- const pageLayoutFQN = `${EntityType.PERSONA}.${personaFqn}`;
-
- const document = await getDocumentByFQN(pageLayoutFQN);
- setCurrentPersonaDocStore(document);
- } catch (error) {
- // silent error
- }
- }, []);
-
- useEffect(() => {
- if (selectedPersona.fullyQualifiedName) {
- fetchCustomizedDocStore(selectedPersona.fullyQualifiedName);
- }
- }, [selectedPersona]);
-
return (
{
{
- return {
- key: item.key,
- label: ,
- children: item.children?.map((item: LeftSidebarItemType) => {
- return {
- key: item.key,
- label: ,
- };
- }),
- };
- })}
+ items={TOP_SIDEBAR_MENU_ITEMS}
mode="inline"
rootClassName="left-sidebar-menu"
selectedKeys={selectedKeys}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.interface.ts
index 62265f54e5e3..d39658a5589f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.interface.ts
@@ -14,7 +14,7 @@
export interface LeftSidebarItem {
key: string;
isBeta?: boolean;
- title: string;
+ label: string;
redirect_url?: string;
icon: SvgComponent;
dataTestId: string;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebarItem.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebarItem.component.tsx
index 605d0b86943b..c11c9ddc14f3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebarItem.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebarItem.component.tsx
@@ -19,7 +19,7 @@ import { NavLink } from 'react-router-dom';
interface LeftSidebarItemProps {
data: {
key: string;
- title: string;
+ label: string;
dataTestId: string;
redirect_url?: string;
icon: SvgComponent;
@@ -29,7 +29,7 @@ interface LeftSidebarItemProps {
}
const LeftSidebarItem = ({
- data: { title, redirect_url, dataTestId, icon, isBeta, onClick },
+ data: { label, redirect_url, dataTestId, icon, isBeta, onClick },
}: LeftSidebarItemProps) => {
const { t } = useTranslation();
@@ -42,7 +42,7 @@ const LeftSidebarItem = ({
}}>
-
{title}
+
{label}
{isBeta && (
- {title}
+ {label}
);
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.test.tsx
index d298dcaca2cb..dae6a2919924 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.test.tsx
@@ -48,9 +48,7 @@ describe('PersonaDetailsCard Component', () => {
});
expect(
- await screen.findByTestId(
- `persona-details-card-${personaWithDescription.name}`
- )
+ await screen.findByTestId('persona-details-card')
).toBeInTheDocument();
});
@@ -83,7 +81,7 @@ describe('PersonaDetailsCard Component', () => {
userEvent.click(personaCardTitle);
});
- expect(mockPush).toHaveBeenCalledWith('/settings/persona/john-doe');
+ expect(mockPush).toHaveBeenCalledWith('/settings/members/persona/john-doe');
});
it('should not navigate when persona.fullyQualifiedName is missing', async () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.tsx
index e6153d7f14d2..adaa39a25b7b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.tsx
@@ -37,7 +37,7 @@ export const PersonaDetailsCard = ({ persona }: PersonaDetailsCardProps) => {
{
- const history = useHistory();
- const { fqn: personaFQN } = useFqn();
-
- const [items, setItems] = React.useState(categories);
-
- const handleCustomizeItemClick = (category: string) => {
- const nestedItems = getCustomizePageOptions(category);
-
- if (isEmpty(nestedItems)) {
- history.push(getCustomizePagePath(personaFQN, category));
- } else {
- setItems(nestedItems);
- }
- };
-
- return (
-
- {items.map((value) => (
-
-
-
- ))}
-
- );
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/ExtensionTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/ExtensionTable.tsx
index 374bc4549cf2..ebe0a1ed6d40 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/ExtensionTable.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/ExtensionTable.tsx
@@ -12,7 +12,6 @@
*/
import { Table, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
-import classNames from 'classnames';
import { isObject, isString, map } from 'lodash';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -29,10 +28,8 @@ interface ExtensionDataSource {
export const ExtensionTable = ({
extension,
- tableClassName,
}: {
extension: ExtentionEntities[ExtentionEntitiesKeys]['extension'];
- tableClassName?: string;
}) => {
const { t } = useTranslation();
const dataSource: ExtensionDataSource[] = useMemo(() => {
@@ -72,7 +69,7 @@ export const ExtensionTable = ({
return (
{
- index: React.Key;
- moveNode: (dragIndex: React.Key, hoverIndex: React.Key) => void;
-}
-
-export const DraggableTabNode = ({
- index,
- children,
- moveNode,
-}: DraggableTabPaneProps) => {
- const ref = useRef(null);
- const [{ isOver, dropClassName }, drop] = useDrop({
- accept: type,
- collect: (monitor) => {
- const { index: dragIndex } = monitor.getItem<{ index: string }>() || {};
- if (dragIndex === index) {
- return {};
- }
-
- return {
- isOver: monitor.isOver(),
- dropClassName: 'dropping',
- };
- },
- drop: (item: { index: React.Key }) => {
- moveNode(item.index, index);
- },
- });
- const [, drag] = useDrag({
- type,
- item: { index },
- collect: (monitor) => ({
- isDragging: monitor.isDragging(),
- }),
- });
- drop(drag(ref));
-
- return (
-
- {children}
-
- );
-};
-
-export const DraggableTabs: React.FC<
- TabsProps & { onTabChange?: (newKeys: React.Key[]) => void }
-> = (props) => {
- const { items = [] } = props;
- const [order, setOrder] = useState([]);
-
- const moveTabNode = (dragKey: React.Key, hoverKey: React.Key) => {
- const newOrder = order.slice();
-
- items.forEach((item) => {
- if (item.key && newOrder.indexOf(item.key) === -1) {
- newOrder.push(item.key);
- }
- });
-
- const dragIndex = newOrder.indexOf(dragKey);
- const hoverIndex = newOrder.indexOf(hoverKey);
-
- newOrder.splice(dragIndex, 1);
- newOrder.splice(hoverIndex, 0, dragKey);
-
- props.onTabChange?.(newOrder);
- setOrder(newOrder);
- };
-
- const renderTabBar: TabsProps['renderTabBar'] = (
- tabBarProps,
- DefaultTabBar
- ) => (
-
- {(node) => (
-
- {node}
-
- )}
-
- );
-
- const orderItems = sortTabs(items, order as string[]);
-
- return (
-
-
-
- );
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/CustomizeWidgets.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/CustomizeWidgets.constants.ts
deleted file mode 100644
index 233d4e00a9d5..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/constants/CustomizeWidgets.constants.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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 { WidgetWidths } from '../enums/CustomizablePage.enum';
-import {
- DetailPageWidgetKeys,
- GlossaryTermDetailPageWidgetKeys,
-} from '../enums/CustomizeDetailPage.enum';
-import i18n from '../utils/i18next/LocalUtil';
-
-export type GridSizes = keyof typeof WidgetWidths;
-export interface CommonWidgetType {
- fullyQualifiedName: string;
- name: string;
- description?: string;
- data: {
- gridSizes: Array;
- };
-}
-
-export const DESCRIPTION_WIDGET: CommonWidgetType = {
- fullyQualifiedName: DetailPageWidgetKeys.DESCRIPTION,
- name: i18n.t('label.description'),
- data: {
- gridSizes: ['small', 'large'],
- },
-};
-
-export const TAGS_WIDGET: CommonWidgetType = {
- fullyQualifiedName: DetailPageWidgetKeys.TAGS,
- name: i18n.t('label.tag-plural'),
- data: { gridSizes: ['small'] },
-};
-
-export const GLOSSARY_TERMS_WIDGET: CommonWidgetType = {
- fullyQualifiedName: DetailPageWidgetKeys.GLOSSARY_TERMS,
- name: i18n.t('label.tag-plural'),
- data: { gridSizes: ['small'] },
-};
-
-export const CUSTOM_PROPERTIES_WIDGET: CommonWidgetType = {
- fullyQualifiedName: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- name: i18n.t('label.custom-property-plural'),
- data: { gridSizes: ['small'] },
-};
-
-export const DOMAIN_WIDGET: CommonWidgetType = {
- fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.DOMAIN,
- name: i18n.t('label.domain'),
- data: { gridSizes: ['small'] },
-};
-
-export const OWNER_WIDGET: CommonWidgetType = {
- fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.OWNER,
- name: i18n.t('label.owner'),
- data: { gridSizes: ['small'] },
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/GlobalSettings.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/GlobalSettings.constants.ts
index 2ba84c2ba0f1..04326e552960 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/GlobalSettings.constants.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/GlobalSettings.constants.ts
@@ -22,7 +22,6 @@ export enum GlobalSettingsMenuCategory {
SERVICES = 'services',
BOTS = 'bots',
APPLICATIONS = 'apps',
- PERSONA = 'persona',
}
export enum GlobalSettingOptions {
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/LeftSidebar.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/LeftSidebar.constants.ts
index 63bca174bc00..532be8e0af09 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/LeftSidebar.constants.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/LeftSidebar.constants.ts
@@ -38,34 +38,34 @@ export const SIDEBAR_NESTED_KEYS = {
export const SIDEBAR_LIST: Array = [
{
key: ROUTES.EXPLORE,
- title: i18next.t('label.explore'),
+ label: i18next.t('label.explore'),
redirect_url: ROUTES.EXPLORE,
icon: ExploreIcon,
dataTestId: `app-bar-item-${SidebarItem.EXPLORE}`,
},
{
key: ROUTES.OBSERVABILITY,
- title: i18next.t('label.observability'),
+ label: i18next.t('label.observability'),
icon: ObservabilityIcon,
dataTestId: SidebarItem.OBSERVABILITY,
children: [
{
key: ROUTES.DATA_QUALITY,
- title: i18next.t('label.data-quality'),
+ label: i18next.t('label.data-quality'),
redirect_url: ROUTES.DATA_QUALITY,
icon: DataQualityIcon,
dataTestId: `app-bar-item-${SidebarItem.DATA_QUALITY}`,
},
{
key: ROUTES.INCIDENT_MANAGER,
- title: i18next.t('label.incident-manager'),
+ label: i18next.t('label.incident-manager'),
redirect_url: ROUTES.INCIDENT_MANAGER,
icon: IncidentMangerIcon,
dataTestId: `app-bar-item-${SidebarItem.INCIDENT_MANAGER}`,
},
{
key: ROUTES.OBSERVABILITY_ALERTS,
- title: i18next.t('label.alert-plural'),
+ label: i18next.t('label.alert-plural'),
redirect_url: ROUTES.OBSERVABILITY_ALERTS,
icon: AlertIcon,
dataTestId: `app-bar-item-${SidebarItem.OBSERVABILITY_ALERT}`,
@@ -74,41 +74,41 @@ export const SIDEBAR_LIST: Array = [
},
{
key: ROUTES.DATA_INSIGHT,
- title: i18next.t('label.insight-plural'),
+ label: i18next.t('label.insight-plural'),
redirect_url: getDataInsightPathWithFqn(),
icon: InsightsIcon,
dataTestId: `app-bar-item-${SidebarItem.DATA_INSIGHT}`,
},
{
key: ROUTES.DOMAIN,
- title: i18next.t('label.domain-plural'),
+ label: i18next.t('label.domain-plural'),
redirect_url: ROUTES.DOMAIN,
icon: DomainsIcon,
dataTestId: `app-bar-item-${SidebarItem.DOMAIN}`,
},
{
key: 'governance',
- title: i18next.t('label.govern'),
+ label: i18next.t('label.govern'),
icon: GovernIcon,
dataTestId: SidebarItem.GOVERNANCE,
children: [
{
key: ROUTES.GLOSSARY,
- title: i18next.t('label.glossary'),
+ label: i18next.t('label.glossary'),
redirect_url: ROUTES.GLOSSARY,
icon: GlossaryIcon,
dataTestId: `app-bar-item-${SidebarItem.GLOSSARY}`,
},
{
key: ROUTES.TAGS,
- title: i18next.t('label.classification'),
+ label: i18next.t('label.classification'),
redirect_url: ROUTES.TAGS,
icon: ClassificationIcon,
dataTestId: `app-bar-item-${SidebarItem.TAGS}`,
},
{
key: ROUTES.METRICS,
- title: i18next.t('label.metric-plural'),
+ label: i18next.t('label.metric-plural'),
redirect_url: ROUTES.METRICS,
icon: MetricIcon,
dataTestId: `app-bar-item-${SidebarItem.METRICS}`,
@@ -119,7 +119,7 @@ export const SIDEBAR_LIST: Array = [
export const SETTING_ITEM = {
key: ROUTES.SETTINGS,
- title: i18next.t('label.setting-plural'),
+ label: i18next.t('label.setting-plural'),
redirect_url: ROUTES.SETTINGS,
icon: SettingsIcon,
dataTestId: `app-bar-item-${SidebarItem.SETTINGS}`,
@@ -127,7 +127,7 @@ export const SETTING_ITEM = {
export const LOGOUT_ITEM = {
key: SidebarItem.LOGOUT,
- title: i18next.t('label.logout'),
+ label: i18next.t('label.logout'),
icon: LogoutIcon,
dataTestId: `app-bar-item-${SidebarItem.LOGOUT}`,
};
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 1752e79f02f2..08c93373b05e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts
@@ -271,7 +271,7 @@ export const ROUTES = {
SETTINGS_EDIT_CUSTOM_LOGIN_CONFIG: `/settings/OpenMetadata/loginConfiguration/edit-custom-login-configuration`,
- CUSTOMIZE_PAGE: `/customize-page/${PLACEHOLDER_ROUTE_FQN}/:pageFqn`,
+ CUSTOMIZE_PAGE: `/customize-page/:fqn/:pageFqn`,
ADD_CUSTOM_METRIC: `/add-custom-metric/${PLACEHOLDER_DASHBOARD_TYPE}/${PLACEHOLDER_ROUTE_FQN}`,
diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/CustomizeDetailPage.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/CustomizeDetailPage.enum.ts
deleted file mode 100644
index ee626b72d07a..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/enums/CustomizeDetailPage.enum.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-export enum WidgetWidths {
- large = 3,
- medium = 2,
- small = 1,
-}
-
-export enum DetailPageWidgetKeys {
- TABS = 'KnowledgePanel.Tabs',
- HEADER = 'KnowledgePanel.Header',
- ANNOUNCEMENTS = 'KnowledgePanel.Announcements',
- DESCRIPTION = 'KnowledgePanel.Description',
- TABLE_SCHEMA = 'KnowledgePanel.TableSchema',
- TOPIC_SCHEMA = 'KnowledgePanel.TopicSchema',
- FREQUENTLY_JOINED_TABLES = 'KnowledgePanel.FrequentlyJoinedTables',
- DATA_PRODUCTS = 'KnowledgePanel.DataProducts',
- TAGS = 'KnowledgePanel.Tags',
- DOMAIN = 'KnowledgePanel.Domain',
- GLOSSARY_TERMS = 'KnowledgePanel.GlossaryTerms',
- CUSTOM_PROPERTIES = 'KnowledgePanel.CustomProperties',
- EMPTY_WIDGET_PLACEHOLDER = 'ExtraWidget.EmptyWidgetPlaceholder',
-}
-
-export enum GlossaryTermDetailPageWidgetKeys {
- TABS = 'KnowledgePanel.Tabs',
- HEADER = 'KnowledgePanel.Header',
- DESCRIPTION = 'KnowledgePanel.Description',
- TAGS = 'KnowledgePanel.Tags',
- SYNONYMS = 'KnowledgePanel.Synonyms',
- RELATED_TERMS = 'KnowledgePanel.RelatedTerms',
- REFERENCES = 'KnowledgePanel.References',
- OWNER = 'KnowledgePanel.Owner',
- DOMAIN = 'KnowledgePanel.Domain',
- REVIEWER = 'KnowledgePanel.Reviewer',
- CUSTOM_PROPERTIES = 'KnowledgePanel.CustomProperties',
- EMPTY_WIDGET_PLACEHOLDER = 'ExtraWidget.EmptyWidgetPlaceholder',
- TERMS_TABLE = 'KnowledgePanel.TermsTable',
-}
diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts
index 6ae69752d416..b265b68462a7 100644
--- a/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts
@@ -157,7 +157,6 @@ export enum TabSpecificField {
CUSTOM_PROPERTIES = 'customProperties',
LOCATION = 'location',
RELATED_METRICS = 'relatedMetrics',
- UI_CUSTOMIZATION = 'uiCustomization',
}
export enum FqnPart {
@@ -206,9 +205,6 @@ export enum EntityTabs {
API_ENDPOINT = 'apiEndpoint',
OVERVIEW = 'overview',
INCIDENTS = 'incidents',
- TERMS = 'terms',
- GLOSSARY_TERMS = 'glossary_terms',
- ASSETS = 'assets',
EXPRESSION = 'expression',
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json
index ba02828eaee9..d9eb209b715a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "Customise",
"customize-entity": "Customize {{entity}}",
- "customize-ui": "Customize UI",
"dag": "DAG",
"dag-view": "DAG-Ansicht",
"daily-active-users-on-the-platform": "Täglich aktive Benutzer auf der Plattform",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Gelöschte {{entity}} verbergen",
"history": "History",
"home": "Startseite",
- "homepage": "Homepage",
"hour": "Stunde",
"http-config-source": "HTTP-Konfigurationsquelle",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "Meine Daten",
"name": "Name",
"name-lowercase": "name",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "Neu",
"new-password": "Neues Passwort",
@@ -1499,7 +1496,7 @@
"custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.",
"custom-property-is-set-to-message": "{{fieldName}} is set to",
"custom-property-name-validation": "Der Name muss mit Kleinbuchstaben beginnen und darf keine Leerzeichen, Unterstriche oder Punkte enthalten.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Customize Landing Page for Persona \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.",
"data-asset-has-been-action-type": "Der Datenvermögenswert wurde {{actionType}}",
"data-insight-alert-destination-description": "Senden Sie E-Mail-Benachrichtigungen an Administratoren oder Teams.",
@@ -1700,7 +1697,6 @@
"no-config-available": "Keine Verbindungskonfigurationen verfügbar.",
"no-config-plural": "No Configs.",
"no-custom-properties-entity": "Derzeit sind keine benutzerdefinierten Eigenschaften für das {{entity}} Data Asset definiert. Um zu erfahren, wie man benutzerdefinierte Eigenschaften hinzufügt, lesen Sie bitte <0>{{docs}}0>",
- "no-customization-available": "No customization available for this tab",
"no-data": "Keine Daten",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "Keine Daten verfügbar.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
index 647cee71acb5..81a40b7d821d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "Customise",
"customize-entity": "Customize {{entity}}",
- "customize-ui": "Customize UI",
"dag": "Dag",
"dag-view": "DAG view",
"daily-active-users-on-the-platform": "Daily Active Users on the Platform",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Hide Deleted {{entity}}",
"history": "History",
"home": "Home",
- "homepage": "Homepage",
"hour": "Hour",
"http-config-source": "HTTP Config Source",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "My Data",
"name": "Name",
"name-lowercase": "name",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "New",
"new-password": "New Password",
@@ -1499,7 +1496,7 @@
"custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.",
"custom-property-is-set-to-message": "{{fieldName}} is set to",
"custom-property-name-validation": "Name must start with lower case with no space, underscore, or dots.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Customize Landing Page for Persona \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.",
"data-asset-has-been-action-type": "Data Asset has been {{actionType}}",
"data-insight-alert-destination-description": "Send email notifications to admins or teams.",
@@ -1700,7 +1697,6 @@
"no-config-available": "No Connection Configs available.",
"no-config-plural": "No Configs.",
"no-custom-properties-entity": "There are currently no Custom Properties defined for the {{entity}} Data Asset. To discover how to add Custom Properties, please refer to <0>{{docs}}0>",
- "no-customization-available": "No customization available for this tab",
"no-data": "No data",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "No data available.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json
index a971e536564b..1bf28882a2ca 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "Personalizar",
"customize-entity": "Personalizar {{entity}}",
- "customize-ui": "Customize UI",
"dag": "DAG",
"dag-view": "Vista DAG",
"daily-active-users-on-the-platform": "Usuarios activos diarios en la plataforma",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Ocultar {{entity}} eliminados",
"history": "Historia",
"home": "Inicio",
- "homepage": "Homepage",
"hour": "Hora",
"http-config-source": "Fuente de configuración HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "Mis Datos",
"name": "Nombre",
"name-lowercase": "nombre",
- "navigation": "Navigation",
"need-help": "necesita ayuda",
"new": "Nuevo",
"new-password": "Nueva Contraseña",
@@ -1499,7 +1496,7 @@
"custom-properties-description": "Captura metadatos personalizados para enriquecer tus activos de datos mediante la extensión de los atributos.",
"custom-property-is-set-to-message": "{{fieldName}} is set to",
"custom-property-name-validation": "El nombre debe comenzar con minúsculas sin espacios, guiones bajos o puntos.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Personalizar la página de inicio para la persona \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Adapte la experiencia de usuario de OpenMetadata para satisfacer las necesidades de su organización y equipo.",
"data-asset-has-been-action-type": "El activo de datos ha sido {{actionType}}",
"data-insight-alert-destination-description": "Enviar notificaciones por correo electrónico a administradores o equipos.",
@@ -1700,7 +1697,6 @@
"no-config-available": "No hay configuraciones de conexión disponibles.",
"no-config-plural": "No hay configuraciones.",
"no-custom-properties-entity": "Actualmente no hay Propiedades Personalizadas definidas para el Activo de Datos {{entity}}. Para saber cómo añadir Propiedades Personalizadas, consulte <0>{{docs}}0>.",
- "no-customization-available": "No customization available for this tab",
"no-data": "No hay datos",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "No hay datos disponibles.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json
index 2951d8e6a600..173272d518b0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json
@@ -260,7 +260,6 @@
"custom-theme": "Thème personnalisé",
"customise": "Personnaliser",
"customize-entity": "Personnaliser {{entity}}",
- "customize-ui": "Customize UI",
"dag": "DAG",
"dag-view": "Vue DAG",
"daily-active-users-on-the-platform": "Utilisateurs Actifs Quotidiens sur la Plateforme",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Masquer les {{entity}} Supprimé·e·s",
"history": "History",
"home": "Accueil",
- "homepage": "Homepage",
"hour": "Heure",
"http-config-source": "Source de Configuration HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "Mes Données",
"name": "Nom",
"name-lowercase": "nom",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "Nouveau",
"new-password": "Nouveau mot de passe",
@@ -1499,7 +1496,7 @@
"custom-properties-description": " Capturer des métadonnées personnalisées pour enrichir vos actifs de données en étendant leurs attributs.",
"custom-property-is-set-to-message": "{{fieldName}} est réglé sur",
"custom-property-name-validation": "Le nom doit commencer par une lettre minuscule, sans espace, ni tiret bas ou point.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Personnalisez la page d'accueil pour le Persona \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Ajustez l'expérience utilisateur d'OpenMetadata pour répondre à vos besoins d'équipe et d'organisation.",
"data-asset-has-been-action-type": "l'actif de données a été {{actionType}}",
"data-insight-alert-destination-description": "Envoyez des notifications par email aux administrateurs ou aux équipes.",
@@ -1700,7 +1697,6 @@
"no-config-available": "Aucun paramètre de connexion disponible.",
"no-config-plural": "Pas de Configs.",
"no-custom-properties-entity": "Aucune propriété personnalisée n'est actuellement définie pour l'actif de données {{entity}}. Pour découvrir comment ajouter des propriétés personnalisées, veuillez consulter <0>{{docs}}0>",
- "no-customization-available": "No customization available for this tab",
"no-data": "Aucune donnée",
"no-data-assets": "Bienvenue dans OpenMetadata! Il semble qu'auncun actif de données n'ai encore été ajouté. Consultez le guide <0>How to Get Started0> pour démarrer.",
"no-data-available": "Aucune donnée disponible",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json
index 97f03731d4ba..262e2fd22bad 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json
@@ -260,7 +260,6 @@
"custom-theme": "Tema personalizado",
"customise": "Personalizar",
"customize-entity": "Personalizar {{entity}}",
- "customize-ui": "Personalizar a interface",
"dag": "DAG",
"dag-view": "Vista de DAG",
"daily-active-users-on-the-platform": "Usuarios activos diarios na plataforma",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Ocultar {{entity}} eliminado",
"history": "Historial",
"home": "Inicio",
- "homepage": "Homepage",
"hour": "Hora",
"http-config-source": "Fonte de configuración HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "Os meus datos",
"name": "Nome",
"name-lowercase": "nome",
- "navigation": "Navigation",
"need-help": "Necesitas axuda",
"new": "Novo",
"new-password": "Novo contrasinal",
@@ -1499,7 +1496,7 @@
"custom-properties-description": "Captura metadatos personalizados para enriquecer os teus activos de datos estendendo os atributos.",
"custom-property-is-set-to-message": "{{fieldName}} está configurado para",
"custom-property-name-validation": "O nome debe comezar en minúscula sen espazos, subliñados nin puntos.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Personalizar a páxina de inicio para a persoa \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Adapta a experiencia de usuario de OpenMetadata ás necesidades da túa organización e equipo.",
"data-asset-has-been-action-type": "O activo de datos foi {{actionType}}",
"data-insight-alert-destination-description": "Envía notificacións por correo electrónico aos administradores ou equipos.",
@@ -1700,7 +1697,6 @@
"no-config-available": "Non hai configuracións de conexión dispoñibles.",
"no-config-plural": "Non hai configuracións.",
"no-custom-properties-entity": "Actualmente non hai Propiedades Personalizadas definidas para o Activo de Datos {{entity}}. Para descubrir como engadir Propiedades Personalizadas, consulte <0>{{docs}}0>",
- "no-customization-available": "No customization available for this tab",
"no-data": "Non hai datos",
"no-data-assets": "Benvido a OpenMetadata! Parece que aínda non se engadiron activos de datos. Consulta a nosa guía de <0>Como comezar0> para iniciar.",
"no-data-available": "Non hai datos dispoñibles.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json
index 7ee8ad3bb546..f3806c469606 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "התאמה אישית",
"customize-entity": "התאם אישית את {{entity}}",
- "customize-ui": "Customize UI",
"dag": "גרף מופע",
"dag-view": "תצוגת גרף מופע",
"daily-active-users-on-the-platform": "משתמשים פעילים יומיים בפלטפורמה",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "הסתר ישות {{entity}} שנמחקה",
"history": "היסטוריה",
"home": "דף הבית",
- "homepage": "Homepage",
"hour": "שעה",
"http-config-source": "מקור תצורת HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "הנתונים שלי",
"name": "שם",
"name-lowercase": "שם",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "חדש",
"new-password": "סיסמה חדשה",
@@ -1499,7 +1496,7 @@
"custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.",
"custom-property-is-set-to-message": "{{fieldName}} is set to",
"custom-property-name-validation": "השם חייב להתחיל באות קטנה ללא רווח, קו תחתון או נקודות.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "התאמה אישית של עמוד הנחיתה עבור דמות \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.",
"data-asset-has-been-action-type": "נכנס של {{actionType}} נתונים",
"data-insight-alert-destination-description": "שלח התראות בדואר אלקטרוני למנהלים או לצוותים.",
@@ -1700,7 +1697,6 @@
"no-config-available": "אין הגדרות חיבור זמינות.",
"no-config-plural": "אין הגדרות.",
"no-custom-properties-entity": "נכון לעכשיו לא מוגדרות תכונות מותאמות אישית עבור נכס הנתונים {{entity}}. כדי לגלות כיצד להוסיף תכונות מותאמות אישית, נא עיין ב-<0>{{docs}}0>",
- "no-customization-available": "No customization available for this tab",
"no-data": "אין נתונים",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "אין נתונים זמינים.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json
index 302afcc124a5..64b360352f1c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "Customise",
"customize-entity": "Customize {{entity}}",
- "customize-ui": "Customize UI",
"dag": "Dag",
"dag-view": "DAGビュー",
"daily-active-users-on-the-platform": "このプラットフォームのアクティブなユーザー",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Hide Deleted {{entity}}",
"history": "History",
"home": "ホーム",
- "homepage": "Homepage",
"hour": "時",
"http-config-source": "HTTP Config Source",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "マイデータ",
"name": "名前",
"name-lowercase": "名前",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "新しい",
"new-password": "新しいパスワード",
@@ -1499,7 +1496,7 @@
"custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.",
"custom-property-is-set-to-message": "{{fieldName}} is set to",
"custom-property-name-validation": "Name must start with lower case with no space, underscore, or dots.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Customize Landing Page for Persona \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.",
"data-asset-has-been-action-type": "データアセットが{{actionType}}されました",
"data-insight-alert-destination-description": "Send email notifications to admins or teams.",
@@ -1700,7 +1697,6 @@
"no-config-available": "利用可能な接続の設定はありません。",
"no-config-plural": "No Configs.",
"no-custom-properties-entity": "現在、{{entity}} データアセットにはカスタムプロパティが定義されていません。カスタムプロパティの追加方法については、<0>{{docs}}0> を参照してください。",
- "no-customization-available": "No customization available for this tab",
"no-data": "データがありません",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "利用できるデータがありません",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json
index e85a66c511c4..736db14c8319 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "Aanpassen",
"customize-entity": "{{entity}} aanpassen",
- "customize-ui": "Customize UI",
"dag": "Dag",
"dag-view": "DAG-weergave",
"daily-active-users-on-the-platform": "Dagelijks actieve gebruikers op het platform",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Verwijderde {{entity}} verbergen",
"history": "Geschiedenis",
"home": "Home",
- "homepage": "Homepage",
"hour": "Uur",
"http-config-source": "HTTP Configuratiebron",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "Mijn data",
"name": "Naam",
"name-lowercase": "naam",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "Nieuw",
"new-password": "Nieuw wachtwoord",
@@ -1499,7 +1496,7 @@
"custom-properties-description": "Leg aangepaste metadata vast om uw data-assets te verrijken door het uitbreiden van de attributen.",
"custom-property-is-set-to-message": "{{fieldName}} is set to",
"custom-property-name-validation": "De naam moet beginnen met een kleine letter zonder spaties, underscores of punten.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Pas de landingspagina aan voor Persona \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "De OpenMetadata UX aanpassen aan de behoeften van uw organisatie en team.",
"data-asset-has-been-action-type": "Data-asset is {{actionType}}",
"data-insight-alert-destination-description": "Stuur e-mailmeldingen naar beheerders of teams.",
@@ -1700,7 +1697,6 @@
"no-config-available": "Geen connectieconfiguraties beschikbaar.",
"no-config-plural": "Geen configuraties.",
"no-custom-properties-entity": "Er zijn momenteel geen aangepaste eigenschappen gedefinieerd voor het {{entity}} Data Asset. Raadpleeg <0>{{docs}}0> voor meer informatie over het toevoegen van aangepaste eigenschappen.",
- "no-customization-available": "No customization available for this tab",
"no-data": "Geen data",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "Geen data beschikbaar.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json
index dc1eb20ba9b5..5c56d7b58813 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json
@@ -260,7 +260,6 @@
"custom-theme": "تم سفارشی",
"customise": "سفارشیسازی",
"customize-entity": "سفارشیسازی {{entity}}",
- "customize-ui": "Customize UI",
"dag": "دگ",
"dag-view": "نمای دگ",
"daily-active-users-on-the-platform": "کاربران فعال روزانه در پلتفرم",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "مخفی کردن {{entity}} حذف شده",
"history": "تاریخچه",
"home": "خانه",
- "homepage": "Homepage",
"hour": "ساعت",
"http-config-source": "منبع پیکربندی HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "دادههای من",
"name": "نام",
"name-lowercase": "نام",
- "navigation": "Navigation",
"need-help": "نیاز به کمک",
"new": "جدید",
"new-password": "رمز عبور جدید",
@@ -1499,7 +1496,7 @@
"custom-properties-description": "متادیتای سفارشی را برای غنیسازی داراییهای دادهای خود با افزودن ویژگیهای اضافی ضبط کنید.",
"custom-property-is-set-to-message": "{{fieldName}} به مقدار",
"custom-property-name-validation": "نام باید با حرف کوچک شروع شود و هیچ فاصله، زیرخط یا نقطهای نداشته باشد.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "سفارشیسازی صفحه اصلی برای نقش \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "تجربه کاربری OpenMetadata را برای نیازهای سازمانی و تیمی خود تنظیم کنید.",
"data-asset-has-been-action-type": "دارایی دادهای {{actionType}} شده است",
"data-insight-alert-destination-description": "اعلانهای ایمیلی را به مدیران یا تیمها ارسال کنید.",
@@ -1700,7 +1697,6 @@
"no-config-available": "هیچ پیکربندی اتصالی در دسترس نیست.",
"no-config-plural": "هیچ پیکربندیای وجود ندارد.",
"no-custom-properties-entity": "در حال حاضر هیچ ویژگی سفارشی برای دارایی داده {{entity}} تعریف نشده است. برای یادگیری نحوه افزودن ویژگیهای سفارشی، لطفاً به <0>{{docs}}0> مراجعه کنید.",
- "no-customization-available": "No customization available for this tab",
"no-data": "بدون داده",
"no-data-assets": "به OpenMetadata خوش آمدید! به نظر میرسد هنوز هیچ دارایی دادهای اضافه نشده است. راهنمای <0>چگونه شروع کنیم0> را بررسی کنید تا شروع کنید.",
"no-data-available": "بدون داده در دسترس.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json
index affb40ecc1ab..4c4cf535c277 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "Personalizar",
"customize-entity": "Personalizar {{entity}}",
- "customize-ui": "Customize UI",
"dag": "DAG",
"dag-view": "Visualização DAG",
"daily-active-users-on-the-platform": "Usuários Ativos Diários na Plataforma",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Ocultar {{entity}} Excluída",
"history": "Histórico",
"home": "Início",
- "homepage": "Homepage",
"hour": "Hora",
"http-config-source": "Fonte de Configuração HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "Meus Dados",
"name": "Nome",
"name-lowercase": "nome",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "Novo",
"new-password": "Nova Senha",
@@ -1499,7 +1496,7 @@
"custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.",
"custom-property-is-set-to-message": "{{fieldName}} is set to",
"custom-property-name-validation": "O nome deve começar com letra minúscula sem espaço, sublinhado ou pontos.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Personalize a Página de Entrada para a Persona \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.",
"data-asset-has-been-action-type": "O Ativo de Dados foi {{actionType}}",
"data-insight-alert-destination-description": "Envie notificações por e-mail para administradores ou equipes.",
@@ -1700,7 +1697,6 @@
"no-config-available": "Nenhuma Configuração de Conexão disponível.",
"no-config-plural": "Nenhuma Configuração.",
"no-custom-properties-entity": "Atualmente, não há Propriedades Personalizadas definidas para o Ativo de Dados {{entity}}. Para saber como adicionar Propriedades Personalizadas, consulte <0>{{docs}}0>.",
- "no-customization-available": "No customization available for this tab",
"no-data": "Sem dados",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "Nenhum dado disponível.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json
index 1aae806030dc..9fc8129ca7aa 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "Personalizar",
"customize-entity": "Personalizar {{entity}}",
- "customize-ui": "Customize UI",
"dag": "DAG",
"dag-view": "Visualização DAG",
"daily-active-users-on-the-platform": "Utilizadors Ativos Diários na Plataforma",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Ocultar {{entity}} Excluída",
"history": "Histórico",
"home": "Início",
- "homepage": "Homepage",
"hour": "Hora",
"http-config-source": "Fonte de Configuração HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "Meus Dados",
"name": "Nome",
"name-lowercase": "nome",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "Novo",
"new-password": "Nova Senha",
@@ -1700,7 +1697,6 @@
"no-config-available": "Nenhuma Configuração de Conexão disponível.",
"no-config-plural": "Nenhuma Configuração.",
"no-custom-properties-entity": "Atualmente, não existem Propriedades Personalizadas definidas para o Ativo de Dados {{entity}}. Para descobrir como adicionar Propriedades Personalizadas, consulte <0>{{docs}}0>",
- "no-customization-available": "No customization available for this tab",
"no-data": "Sem dados",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "Nenhum dado disponível.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json
index 7c66fe72cb72..6c9ac09dc76a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json
@@ -260,7 +260,6 @@
"custom-theme": "Custom Theme",
"customise": "Customise",
"customize-entity": "Customize {{entity}}",
- "customize-ui": "Customize UI",
"dag": "Dag",
"dag-view": "Просмотр DAG",
"daily-active-users-on-the-platform": "Количество активных пользователей на платформе",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "Скрыть удаленные {{entity}}",
"history": "History",
"home": "Домой",
- "homepage": "Homepage",
"hour": "Час",
"http-config-source": "Источник конфигурации HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "Мои данные",
"name": "Наименование",
"name-lowercase": "наименование",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "Новый",
"new-password": "Новый пароль",
@@ -1499,7 +1496,7 @@
"custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.",
"custom-property-is-set-to-message": "{{fieldName}} is set to",
"custom-property-name-validation": "Имя должно начинаться со строчной буквы без пробелов, подчеркивания и точек.",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "Customize Landing Page for Persona \"<0>{{persona}}0>\"",
"customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.",
"data-asset-has-been-action-type": "Объект данных был {{actionType}}",
"data-insight-alert-destination-description": "Отправляйте уведомления по электронной почте администраторам или командам.",
@@ -1700,7 +1697,6 @@
"no-config-available": "Нет доступных конфигураций подключения.",
"no-config-plural": "No Configs.",
"no-custom-properties-entity": "В настоящее время для {{entity}} Data Asset не определены пользовательские свойства. Чтобы узнать, как добавить пользовательские свойства, обратитесь к <0>{{docs}}0>",
- "no-customization-available": "No customization available for this tab",
"no-data": "Нет данных",
"no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started0> guide to begin.",
"no-data-available": "Данные недоступны.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json
index 0de47bc325d6..22787ea96f88 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json
@@ -260,7 +260,6 @@
"custom-theme": "ธีมที่กำหนดเอง",
"customise": "ปรับแต่ง",
"customize-entity": "ปรับแต่ง {{entity}}",
- "customize-ui": "ปรับแต่ง UI",
"dag": "Dag",
"dag-view": "มุมมอง DAG",
"daily-active-users-on-the-platform": "ผู้ใช้ที่ใช้งานอยู่รายวันบนแพลตฟอร์ม",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "ซ่อน {{entity}} ที่ถูกลบ",
"history": "ประวัติ",
"home": "หน้าแรก",
- "homepage": "หน้าแรก",
"hour": "ชั่วโมง",
"http-config-source": "แหล่งที่มาของการตั้งค่า HTTP",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "ข้อมูลของฉัน",
"name": "ชื่อ",
"name-lowercase": "ชื่อ",
- "navigation": "การนำทาง",
"need-help": "ต้องการความช่วยเหลือ",
"new": "ใหม่",
"new-password": "รหัสผ่านใหม่",
@@ -1700,7 +1697,6 @@
"no-config-available": "ไม่มีการตั้งค่าการเชื่อมต่อที่สามารถใช้งานได้",
"no-config-plural": "ไม่มีการตั้งค่า",
"no-custom-properties-entity": "ขณะนี้ไม่มีการกำหนดคุณสมบัติเฉพาะสำหรับสินทรัพย์ข้อมูล {{entity}} หากต้องการเรียนรู้วิธีการเพิ่มคุณสมบัติเฉพาะ โปรดดูที่ <0>{{docs}}0>.",
- "no-customization-available": "ไม่มีการปรับแต่งให้ใช้งานได้สำหรับแท็บนี้",
"no-data": "ไม่มีข้อมูล",
"no-data-assets": "ยินดีต้อนรับสู่ OpenMetadata! ดูเหมือนว่ายังไม่มีสินทรัพย์ข้อมูลถูกเพิ่ม ตรวจสอบเรา <0>วิธีการเริ่มต้นใช้งาน0> เพื่อเริ่มต้น",
"no-data-available": "ไม่มีข้อมูลที่สามารถใช้งานได้",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json
index c59d5059a02b..4bcd4f27a5d1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json
@@ -260,7 +260,6 @@
"custom-theme": "自定义主题",
"customise": "自定义",
"customize-entity": "自定义{{entity}}",
- "customize-ui": "Customize UI",
"dag": "DAG",
"dag-view": "DAG 视图",
"daily-active-users-on-the-platform": "平台上的每日活跃用户",
@@ -593,7 +592,6 @@
"hide-deleted-entity": "隐藏已删除的{{entity}}",
"history": "历史",
"home": "主页",
- "homepage": "Homepage",
"hour": "小时",
"http-config-source": "HTTP 配置源",
"http-method": "HTTP Method",
@@ -792,7 +790,6 @@
"my-data": "我的数据",
"name": "名称",
"name-lowercase": "名称",
- "navigation": "Navigation",
"need-help": "Need Help",
"new": "新",
"new-password": "新密码",
@@ -1499,7 +1496,7 @@
"custom-properties-description": " 获取自定义元数据, 通过扩展属性来丰富数据资产",
"custom-property-is-set-to-message": "{{fieldName}}设置为",
"custom-property-name-validation": "命名首字母必须是小写字母不能为空格、下划线或点号",
- "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}0>\"",
+ "customize-landing-page-header": "为用户角色\"<0>{{persona}}0>\"自定义登陆页面",
"customize-open-metadata-description": "自定义 OpenMetadata, 以满足您的组织和团队需求",
"data-asset-has-been-action-type": "数据资产已{{actionType}}",
"data-insight-alert-destination-description": "发送通知邮件给管理员或团队",
@@ -1700,7 +1697,6 @@
"no-config-available": "没有可用的连接配置",
"no-config-plural": "No Configs.",
"no-custom-properties-entity": "当前没有为 {{entity}} 数据资产定义自定义属性。要了解如何添加自定义属性,请参考 <0>{{docs}}0>",
- "no-customization-available": "No customization available for this tab",
"no-data": "没有数据",
"no-data-assets": "欢迎访问 OpenMetadata! 看起来还没有添加数据资产, 请查看我们的<0>How to Get Started0>开始指南",
"no-data-available": "没有可用的数据",
diff --git a/openmetadata-ui/src/main/resources/ui/src/mocks/MyDataPage.mock.tsx b/openmetadata-ui/src/main/resources/ui/src/mocks/MyDataPage.mock.tsx
index bacc213dd97c..f500c9e666d4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/mocks/MyDataPage.mock.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/mocks/MyDataPage.mock.tsx
@@ -16,7 +16,6 @@ import { LandingPageWidgetKeys } from '../enums/CustomizablePage.enum';
import { Document } from '../generated/entity/docStore/document';
import { Thread, ThreadType } from '../generated/entity/feed/thread';
import { User } from '../generated/entity/teams/user';
-import { PageType } from '../generated/system/ui/page';
import { Paging } from '../generated/type/paging';
import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface';
@@ -134,12 +133,9 @@ export const mockDocumentData: Document = {
fullyQualifiedName: `persona.${mockPersonaName}.Page.LandingPage`,
entityType: 'Page',
data: {
- pages: [
- {
- pageType: PageType.LandingPage,
- layout: mockCustomizedLayout,
- },
- ],
+ page: {
+ layout: mockCustomizedLayout,
+ },
},
};
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 791cdd6491cd..5d682931c0cc 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
@@ -12,10 +12,13 @@
*/
import { act, render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import React from 'react';
import { useParams } from 'react-router-dom';
-import { Page, PageType } from '../../generated/system/ui/page';
+import { LandingPageWidgetKeys } from '../../enums/CustomizablePage.enum';
+import { PageType } from '../../generated/system/ui/page';
import {
+ mockCustomizePageClassBase,
mockDocumentData,
mockPersonaDetails,
mockPersonaName,
@@ -57,6 +60,10 @@ jest.mock('../../components/common/Loader/Loader', () => {
return jest.fn().mockImplementation(() => Loader
);
});
+jest.mock('../../utils/CustomizePageClassBase', () => {
+ return mockCustomizePageClassBase;
+});
+
jest.mock('../../rest/DocStoreAPI', () => ({
createDocument: jest
.fn()
@@ -88,33 +95,6 @@ jest.mock('react-router-dom', () => ({
Link: jest.fn().mockImplementation(() => Link
),
}));
-jest.mock('./CustomizeStore', () => ({
- useCustomizeStore: jest.fn().mockImplementation(() => ({
- document: mockDocumentData,
- setDocument: jest.fn(),
- getNavigation: jest.fn(),
- currentPage: {} as Page,
- getPage: jest.fn(),
- setCurrentPageType: jest.fn(),
- })),
-}));
-
-jest.mock(
- '../../components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData',
- () => {
- return jest.fn().mockImplementation(() => CustomizeMyData
);
- }
-);
-
-jest.mock(
- '../../components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage',
- () => {
- return jest
- .fn()
- .mockImplementation(() => CustomizeGlossaryTermDetailPage
);
- }
-);
-
describe('CustomizablePage component', () => {
it('CustomizablePage should show ErrorPlaceholder if the API to fetch the persona details fails', async () => {
(getPersonaByName as jest.Mock).mockImplementationOnce(() =>
@@ -134,7 +114,7 @@ describe('CustomizablePage component', () => {
expect(screen.getByText('Loader')).toBeInTheDocument();
expect(screen.queryByText('ErrorPlaceHolder')).toBeNull();
- expect(screen.queryByTestId('CustomizeMyData')).toBeNull();
+ expect(screen.queryByTestId('customize-my-data')).toBeNull();
});
});
@@ -143,8 +123,22 @@ describe('CustomizablePage component', () => {
render( );
});
- expect(screen.getByText('CustomizeMyData')).toBeInTheDocument();
+ expect(screen.getByTestId('customize-my-data')).toBeInTheDocument();
expect(screen.queryByText('ErrorPlaceHolder')).toBeNull();
+ expect(
+ screen.getByText(LandingPageWidgetKeys.ACTIVITY_FEED)
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(LandingPageWidgetKeys.FOLLOWING)
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(LandingPageWidgetKeys.RECENTLY_VIEWED)
+ ).toBeInTheDocument();
+ expect(screen.queryByText(LandingPageWidgetKeys.MY_DATA)).toBeNull();
+ expect(screen.queryByText(LandingPageWidgetKeys.KPI)).toBeNull();
+ expect(
+ screen.queryByText(LandingPageWidgetKeys.TOTAL_DATA_ASSETS)
+ ).toBeNull();
});
it('CustomizablePage should pass the default layout data when no layout is present for the persona', async () => {
@@ -159,11 +153,68 @@ describe('CustomizablePage component', () => {
render( );
});
- expect(screen.queryByText('CustomizeMyData')).toBeInTheDocument();
+ expect(screen.getByTestId('customize-my-data')).toBeInTheDocument();
expect(screen.queryByText('ErrorPlaceHolder')).toBeNull();
+ expect(
+ screen.getByText(LandingPageWidgetKeys.ACTIVITY_FEED)
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(LandingPageWidgetKeys.FOLLOWING)
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(LandingPageWidgetKeys.RECENTLY_VIEWED)
+ ).toBeInTheDocument();
+ expect(screen.getByText(LandingPageWidgetKeys.MY_DATA)).toBeInTheDocument();
+ expect(screen.getByText(LandingPageWidgetKeys.KPI)).toBeInTheDocument();
+ expect(
+ screen.getByText(LandingPageWidgetKeys.TOTAL_DATA_ASSETS)
+ ).toBeInTheDocument();
});
- it('CustomizablePage should return ErrorPlaceHolder for invalid page FQN', async () => {
+ it('CustomizablePage should update the layout when layout data is present for persona', async () => {
+ await act(async () => {
+ render( );
+ });
+
+ const saveCurrentPageLayoutBtn = screen.getByText(
+ 'handleSaveCurrentPageLayout'
+ );
+
+ await act(async () => {
+ userEvent.click(saveCurrentPageLayoutBtn);
+ });
+
+ expect(mockShowSuccessToast).toHaveBeenCalledWith(
+ 'server.page-layout-operation-success'
+ );
+ });
+
+ it('CustomizablePage should save the layout when no layout data present for persona', async () => {
+ (getDocumentByFQN as jest.Mock).mockImplementationOnce(() =>
+ Promise.reject({
+ response: {
+ status: 404,
+ },
+ })
+ );
+ await act(async () => {
+ render( );
+ });
+
+ const saveCurrentPageLayoutBtn = screen.getByText(
+ 'handleSaveCurrentPageLayout'
+ );
+
+ await act(async () => {
+ userEvent.click(saveCurrentPageLayoutBtn);
+ });
+
+ expect(mockShowSuccessToast).toHaveBeenCalledWith(
+ 'server.page-layout-operation-success'
+ );
+ });
+
+ it('CustomizablePage should return null for invalid page FQN', async () => {
(useParams as jest.Mock).mockImplementation(() => ({
fqn: mockPersonaName,
pageFqn: 'invalidName',
@@ -173,7 +224,7 @@ describe('CustomizablePage component', () => {
render( );
});
- expect(screen.queryByText('ErrorPlaceHolder')).toBeInTheDocument();
+ expect(screen.queryByText('ErrorPlaceHolder')).toBeNull();
expect(screen.queryByText('Loader')).toBeNull();
expect(screen.queryByTestId('customize-my-data')).toBeNull();
});
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 3f3bf34da1f1..ee21418bd24d 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
@@ -13,13 +13,12 @@
import { Col, Row, Typography } from 'antd';
import { AxiosError } from 'axios';
import { compare } from 'fast-json-patch';
-import { cloneDeep, isUndefined } from 'lodash';
-import React, { useEffect, useState } from 'react';
+import { isUndefined } from 'lodash';
+import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link, useParams } from 'react-router-dom';
import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder';
import Loader from '../../components/common/Loader/Loader';
-import CustomizeGlossaryTermDetailPage from '../../components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage';
import CustomizeMyData from '../../components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData';
import {
GlobalSettingOptions,
@@ -30,8 +29,7 @@ import { ERROR_PLACEHOLDER_TYPE } from '../../enums/common.enum';
import { EntityType } from '../../enums/entity.enum';
import { Document } from '../../generated/entity/docStore/document';
import { Persona } from '../../generated/entity/teams/persona';
-import { Page, PageType } from '../../generated/system/ui/page';
-import { UICustomization } from '../../generated/system/ui/uiCustomization';
+import { PageType } from '../../generated/system/ui/page';
import { useApplicationStore } from '../../hooks/useApplicationStore';
import { useFqn } from '../../hooks/useFqn';
import {
@@ -41,108 +39,91 @@ import {
} from '../../rest/DocStoreAPI';
import { getPersonaByName } from '../../rest/PersonaAPI';
import { Transi18next } from '../../utils/CommonUtils';
+import customizePageClassBase from '../../utils/CustomizePageClassBase';
import { getSettingPath } from '../../utils/RouterUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
-import { CustomizeTableDetailPage } from '../CustomizeTableDetailPage/CustomizeTableDetailPage';
-import { SettingsNavigationPage } from '../SettingsNavigationPage/SettingsNavigationPage';
-import { useCustomizeStore } from './CustomizeStore';
export const CustomizablePage = () => {
- const { pageFqn } = useParams<{ pageFqn: string }>();
- const { fqn: personaFQN } = useFqn();
+ const { pageFqn } = useParams<{ pageFqn: PageType }>();
+ const { fqn: decodedPageFQN } = useFqn();
const { t } = useTranslation();
const { theme } = useApplicationStore();
- const [isLoading, setIsLoading] = useState(true);
+ const [page, setPage] = useState({} as Document);
+ const [editedPage, setEditedPage] = useState({} as Document);
+ const [isLoading, setIsLoading] = useState(false);
+ const [isPersonaLoading, setIsPersonaLoading] = useState(true);
const [personaDetails, setPersonaDetails] = useState();
- const {
- document,
- setDocument,
- getNavigation,
- currentPage,
- getPage,
- setCurrentPageType,
- } = useCustomizeStore();
+ const [saveCurrentPageLayout, setSaveCurrentPageLayout] = useState(false);
- const handlePageCustomizeSave = async (newPage?: Page) => {
- if (!document) {
- return;
- }
- try {
- let response: Document;
- const newDoc = cloneDeep(document);
- const pageData = getPage(pageFqn);
+ const handlePageDataChange = useCallback((newPageData: Document) => {
+ setEditedPage(newPageData);
+ }, []);
- if (pageData) {
- newDoc.data.pages = newPage
- ? newDoc.data?.pages?.map((p: Page) =>
- p.pageType === pageFqn ? newPage : p
- )
- : newDoc.data?.pages.filter((p: Page) => p.pageType !== pageFqn);
- } else {
- newDoc.data = {
- ...newDoc.data,
- pages: [...(newDoc.data.pages ?? []), newPage],
- };
- }
+ const handleSaveCurrentPageLayout = useCallback((value: boolean) => {
+ setSaveCurrentPageLayout(value);
+ }, []);
- if (document.id) {
- const jsonPatch = compare(document, newDoc);
+ const fetchPersonaDetails = useCallback(async () => {
+ try {
+ setIsPersonaLoading(true);
+ const response = await getPersonaByName(decodedPageFQN);
- response = await updateDocument(document.id ?? '', jsonPatch);
- } else {
- response = await createDocument({
- ...newDoc,
- domain: newDoc.domain?.fullyQualifiedName,
- });
+ setPersonaDetails(response);
+ } catch {
+ // No error handling needed
+ // No data placeholder will be shown in case of failure
+ } finally {
+ setIsPersonaLoading(false);
+ }
+ }, [decodedPageFQN]);
+
+ const fetchDocument = async () => {
+ if (!isUndefined(personaDetails)) {
+ const pageLayoutFQN = `${EntityType.PERSONA}.${decodedPageFQN}.${EntityType.PAGE}.${pageFqn}`;
+ try {
+ setIsLoading(true);
+ const pageData = await getDocumentByFQN(pageLayoutFQN);
+
+ setPage(pageData);
+ setEditedPage(pageData);
+ } catch (error) {
+ if ((error as AxiosError).response?.status === ClientErrors.NOT_FOUND) {
+ setPage({
+ name: `${personaDetails.name}-${decodedPageFQN}`,
+ fullyQualifiedName: pageLayoutFQN,
+ entityType: EntityType.PAGE,
+ data: {
+ page: { layout: customizePageClassBase.defaultLayout },
+ },
+ });
+ } else {
+ showErrorToast(error as AxiosError);
+ }
+ } finally {
+ setIsLoading(false);
}
- setDocument(response);
-
- showSuccessToast(
- t('server.page-layout-operation-success', {
- operation: document.id
- ? t('label.updated-lowercase')
- : t('label.created-lowercase'),
- })
- );
- } catch (error) {
- // Error
- showErrorToast(
- t('server.page-layout-operation-error', {
- operation: document.id
- ? t('label.updating-lowercase')
- : t('label.creating-lowercase'),
- })
- );
}
};
- const handleNavigationSave = async (
- uiNavigation: UICustomization['navigation']
- ) => {
- if (!document) {
- return;
- }
+ const handleSave = async () => {
try {
let response: Document;
- const newDoc = cloneDeep(document);
- newDoc.data.navigation = uiNavigation;
+ if (page.id) {
+ const jsonPatch = compare(page, editedPage);
- if (document.id) {
- const jsonPatch = compare(document, newDoc);
-
- response = await updateDocument(document.id ?? '', jsonPatch);
+ response = await updateDocument(page.id ?? '', jsonPatch);
} else {
response = await createDocument({
- ...newDoc,
- domain: newDoc.domain?.fullyQualifiedName,
+ ...editedPage,
+ domain: editedPage.domain?.fullyQualifiedName,
});
}
- setDocument(response);
-
+ setPage(response);
+ setEditedPage(response);
showSuccessToast(
t('server.page-layout-operation-success', {
- operation: document.id
+ operation: page.id
? t('label.updated-lowercase')
: t('label.created-lowercase'),
})
@@ -151,7 +132,7 @@ export const CustomizablePage = () => {
// Error
showErrorToast(
t('server.page-layout-operation-error', {
- operation: document.id
+ operation: page.id
? t('label.updating-lowercase')
: t('label.creating-lowercase'),
})
@@ -159,47 +140,22 @@ export const CustomizablePage = () => {
}
};
- const initializeCustomizeStore = async () => {
- setIsLoading(true);
- const pageLayoutFQN = `${EntityType.PERSONA}.${personaFQN}`;
- try {
- const personaDetails = await getPersonaByName(personaFQN);
- setPersonaDetails(personaDetails);
-
- if (personaDetails) {
- try {
- const pageData = await getDocumentByFQN(pageLayoutFQN);
-
- setDocument(pageData);
- setCurrentPageType(pageFqn as PageType);
- } catch (error) {
- if (
- (error as AxiosError).response?.status === ClientErrors.NOT_FOUND
- ) {
- setDocument({
- name: `${personaDetails.name}-${personaFQN}`,
- fullyQualifiedName: pageLayoutFQN,
- entityType: EntityType.PAGE,
- data: {},
- });
- setCurrentPageType(pageFqn as PageType);
- } else {
- showErrorToast(error as AxiosError);
- }
- }
- }
- } catch (error) {
- showErrorToast(error as AxiosError);
- } finally {
- setIsLoading(false);
+ useEffect(() => {
+ if (saveCurrentPageLayout) {
+ handleSave();
+ setSaveCurrentPageLayout(false);
}
- };
+ }, [saveCurrentPageLayout]);
useEffect(() => {
- initializeCustomizeStore();
- }, []);
+ fetchPersonaDetails();
+ }, [decodedPageFQN, pageFqn]);
+
+ useEffect(() => {
+ fetchDocument();
+ }, [personaDetails]);
- if (isLoading) {
+ if (isLoading || isPersonaLoading) {
return ;
}
@@ -233,45 +189,17 @@ export const CustomizablePage = () => {
);
}
- switch (pageFqn) {
- case 'navigation':
- return (
-
- );
-
- case PageType.LandingPage:
- case 'homepage':
- return (
-
- );
-
- case PageType.Glossary:
- case PageType.GlossaryTerm:
- return (
-
- );
- case PageType.Table:
- return (
-
- );
- default:
- return ;
+ if (pageFqn === PageType.LandingPage) {
+ return (
+
+ );
}
+
+ return null;
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizeStore.ts b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizeStore.ts
deleted file mode 100644
index b68c56c894fb..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizeStore.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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 { create } from 'zustand';
-
-import { Document } from '../../generated/entity/docStore/document';
-import { Page, PageType } from '../../generated/system/ui/page';
-import { NavigationItem } from '../../generated/system/ui/uiCustomization';
-
-interface CustomizePageStore {
- document: Document | null;
- currentPageType: PageType | null;
- currentPage: Page | null;
- currentPersonaDocStore: Document | null;
- setDocument: (document: Document) => void;
-
- setPage: (page: Page) => void;
-
- getPage: (pageType: string) => Page;
-
- getNavigation: () => NavigationItem[];
- setCurrentPageType: (pageType: PageType) => void;
- updateCurrentPage: (page: Page) => void;
- setCurrentPersonaDocStore: (document: Document) => void;
- resetCurrentPersonaDocStore: () => void;
-}
-
-export const useCustomizeStore = create()((set, get) => ({
- document: null,
- currentPage: null,
- currentPageType: null,
- currentPersonaDocStore: null,
- setDocument: (document: Document) => {
- set({ document });
- },
-
- setPage: (page: Page) => {
- const { document } = get();
- const newDocument = {
- ...document,
- data: {
- ...document?.data,
- pages: document?.data?.pages?.map((p: Page) =>
- p.pageType === page.pageType ? page : p
- ),
- },
- } as Document;
- set({ document: newDocument });
- },
-
- getPage: (pageType: string) => {
- const { document } = get();
-
- return document?.data?.pages?.find((p: Page) => p.pageType === pageType);
- },
-
- getNavigation: () => {
- const { document } = get();
-
- return document?.data?.navigation;
- },
-
- updateCurrentPage: (page: Page) => {
- set({ currentPage: page });
- },
-
- setCurrentPageType: (pageType: PageType) => {
- const { getPage } = get();
-
- set({
- currentPage: getPage(pageType) ?? { pageType },
- currentPageType: pageType,
- });
- },
-
- setCurrentPersonaDocStore: (document: Document) => {
- set({ currentPersonaDocStore: document });
- },
-
- reset: () => {
- set({ document: null, currentPage: null });
- },
-
- resetCurrentPage: () => {
- set({ currentPage: null });
- },
- resetCurrentPersonaDocStore: () => {
- set({ currentPersonaDocStore: null });
- },
-}));
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizeTableDetailPage/CustomizeTableDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizeTableDetailPage/CustomizeTableDetailPage.tsx
deleted file mode 100644
index 080be151a1bc..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizeTableDetailPage/CustomizeTableDetailPage.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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 { noop } from 'lodash';
-import React, { useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-import gridBgImg from '../../assets/img/grid-bg-img.png';
-import { DataAssetsHeader } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
-import { CustomizeTabWidget } from '../../components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget';
-import { CustomizablePageHeader } from '../../components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader';
-import { CustomizeMyDataProps } from '../../components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface';
-import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
-import { OperationPermission } from '../../context/PermissionProvider/PermissionProvider.interface';
-import { EntityType } from '../../enums/entity.enum';
-import { Table } from '../../generated/entity/data/table';
-import { Page, PageType } from '../../generated/system/ui/page';
-import { useGridLayoutDirection } from '../../hooks/useGridLayoutDirection';
-import { getDummyDataByPage } from '../../utils/CustomizePage/CustomizePageUtils';
-import { getEntityName } from '../../utils/EntityUtils';
-import { useCustomizeStore } from '../CustomizablePage/CustomizeStore';
-
-export const CustomizeTableDetailPage = ({
- personaDetails,
- onSaveLayout,
-}: CustomizeMyDataProps) => {
- const { t } = useTranslation();
- const { currentPage, currentPageType } = useCustomizeStore();
-
- const handleReset = useCallback(async () => {
- await onSaveLayout();
- }, [onSaveLayout]);
-
- const handleSave = async () => {
- await onSaveLayout(currentPage ?? ({ pageType: currentPageType } as Page));
- };
-
- const entityDummyData = getDummyDataByPage(
- currentPageType as PageType
- ) as unknown;
-
- // call the hook to set the direction of the grid layout
- useGridLayoutDirection();
-
- const asyncNoop = async () => {
- noop();
- };
-
- return (
-
-
-
-
-
-
-
- );
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Glossary/GlossaryPage/GlossaryPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Glossary/GlossaryPage/GlossaryPage.component.tsx
index 2839a2b47274..ab2d3592eb7a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/Glossary/GlossaryPage/GlossaryPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/Glossary/GlossaryPage/GlossaryPage.component.tsx
@@ -279,11 +279,6 @@ const GlossaryPage = () => {
if (isEmpty(jsonPatch)) {
return;
}
-
- const shouldRefreshTerms = jsonPatch.some((patch) =>
- patch.path.startsWith('/owners')
- );
-
try {
const response = await patchGlossaryTerm(activeGlossary?.id, jsonPatch);
if (response) {
@@ -292,7 +287,6 @@ const GlossaryPage = () => {
history.push(getGlossaryPath(response.fullyQualifiedName));
fetchGlossaryList();
}
- shouldRefreshTerms && fetchGlossaryTermDetails();
} else {
throw t('server.entity-updating-error', {
entity: t('label.glossary-term'),
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx
index 55565b683804..96e541a15516 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx
@@ -33,7 +33,7 @@ import {
import { EntityType } from '../../enums/entity.enum';
import { SearchIndex } from '../../enums/search.enum';
import { Thread } from '../../generated/entity/feed/thread';
-import { Page, PageType } from '../../generated/system/ui/page';
+import { PageType } from '../../generated/system/ui/page';
import { EntityReference } from '../../generated/type/entityReference';
import LimitWrapper from '../../hoc/LimitWrapper';
import { useApplicationStore } from '../../hooks/useApplicationStore';
@@ -42,7 +42,7 @@ import { getDocumentByFQN } from '../../rest/DocStoreAPI';
import { getActiveAnnouncement } from '../../rest/feedsAPI';
import { searchQuery } from '../../rest/searchAPI';
import { getWidgetFromKey } from '../../utils/CustomizableLandingPageUtils';
-import customizePageClassBase from '../../utils/CustomizeMyDataPageClassBase';
+import customizePageClassBase from '../../utils/CustomizePageClassBase';
import { showErrorToast } from '../../utils/ToastUtils';
import { WidgetConfig } from '../CustomizablePage/CustomizablePage.interface';
import './my-data.less';
@@ -78,18 +78,9 @@ const MyDataPage = () => {
try {
setIsLoading(true);
if (!isEmpty(selectedPersona)) {
- const pageFQN = `${EntityType.PERSONA}.${selectedPersona.fullyQualifiedName}`;
- const docData = await getDocumentByFQN(pageFQN);
-
- const pageData = docData.data?.pages?.find(
- (p: Page) => p.pageType === PageType.LandingPage
- ) ?? { layout: [], pageType: PageType.LandingPage };
-
- setLayout(
- isEmpty(pageData.layout)
- ? customizePageClassBase.defaultLayout
- : pageData.layout
- );
+ const pageFQN = `${EntityType.PERSONA}.${selectedPersona.fullyQualifiedName}.${EntityType.PAGE}.${PageType.LandingPage}`;
+ const pageData = await getDocumentByFQN(pageFQN);
+ setLayout(pageData.data.page.layout);
} else {
setLayout(customizePageClassBase.defaultLayout);
}
@@ -223,7 +214,6 @@ const MyDataPage = () => {
{
return jest.fn().mockImplementation(() => Loader
);
});
-jest.mock('../../utils/CustomizeMyDataPageClassBase', () => {
+jest.mock('../../utils/CustomizePageClassBase', () => {
return mockCustomizePageClassBase;
});
jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.test.tsx
index 40a57d7e2aaf..fe70f17845b6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.test.tsx
@@ -17,7 +17,6 @@ import {
waitForElementToBeRemoved,
} from '@testing-library/react';
import React from 'react';
-import { MemoryRouter } from 'react-router-dom';
import { getPersonaByName, updatePersona } from '../../../rest/PersonaAPI';
import { PersonaDetailsPage } from './PersonaDetailsPage';
@@ -150,13 +149,9 @@ jest.mock(
() => jest.fn().mockImplementation(() => EntityHeaderTitle
)
);
-jest.mock('../../../hooks/useCustomLocation/useCustomLocation', () => {
- return jest.fn().mockImplementation(() => ({ pathname: '', hash: '' }));
-});
-
describe('PersonaDetailsPage', () => {
it('Component should render', async () => {
- render( ), { wrapper: MemoryRouter };
+ render( );
await waitForElementToBeRemoved(() => screen.getByTestId('loader'));
@@ -175,7 +170,7 @@ describe('PersonaDetailsPage', () => {
(getPersonaByName as jest.Mock).mockImplementationOnce(() =>
Promise.reject()
);
- render( , { wrapper: MemoryRouter });
+ render( );
expect(
await screen.findByText('NoDataPlaceholder.component')
@@ -183,18 +178,20 @@ describe('PersonaDetailsPage', () => {
});
it('handleAfterDeleteAction should call after delete', async () => {
- render( , { wrapper: MemoryRouter });
+ render( );
const deleteBtn = await screen.findByTestId('delete-btn');
fireEvent.click(deleteBtn);
- expect(mockUseHistory.push).toHaveBeenCalledWith('/settings/persona');
+ expect(mockUseHistory.push).toHaveBeenCalledWith(
+ '/settings/members/persona'
+ );
});
it('handleDisplayNameUpdate should call after updating displayName', async () => {
const mockUpdatePersona = updatePersona as jest.Mock;
- render( , { wrapper: MemoryRouter });
+ render( );
const updateName = await screen.findByTestId('display-name-btn');
@@ -207,7 +204,7 @@ describe('PersonaDetailsPage', () => {
it('add user should work', async () => {
const mockUpdatePersona = updatePersona as jest.Mock;
- render( , { wrapper: MemoryRouter });
+ render( );
const addUser = await screen.findByTestId('user-selectable-list');
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.tsx
index bdc96d78643e..9ccda19b8761 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.tsx
@@ -28,15 +28,16 @@ import { UserSelectableList } from '../../../components/common/UserSelectableLis
import EntityHeaderTitle from '../../../components/Entity/EntityHeaderTitle/EntityHeaderTitle.component';
import { EntityName } from '../../../components/Modals/EntityNameModal/EntityNameModal.interface';
import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1';
-import { CustomizeUI } from '../../../components/Settings/Persona/CustomizeUI/CustomizeUI';
import { UsersTab } from '../../../components/Settings/Users/UsersTab/UsersTabs.component';
-import { GlobalSettingsMenuCategory } from '../../../constants/GlobalSettings.constants';
+import {
+ GlobalSettingOptions,
+ GlobalSettingsMenuCategory,
+} from '../../../constants/GlobalSettings.constants';
import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider';
import { ResourceEntity } from '../../../context/PermissionProvider/PermissionProvider.interface';
import { SIZE } from '../../../enums/common.enum';
import { EntityType } from '../../../enums/entity.enum';
import { Persona } from '../../../generated/entity/teams/persona';
-import useCustomLocation from '../../../hooks/useCustomLocation/useCustomLocation';
import { useFqn } from '../../../hooks/useFqn';
import { getPersonaByName, updatePersona } from '../../../rest/PersonaAPI';
import { getEntityName } from '../../../utils/EntityUtils';
@@ -54,11 +55,6 @@ export const PersonaDetailsPage = () => {
const [entityPermission, setEntityPermission] = useState(
DEFAULT_ENTITY_PERMISSION
);
- const location = useCustomLocation();
- const activeKey = useMemo(
- () => location.hash?.replace('#', '') || 'users',
- [location]
- );
const { getEntityPermissionByFqn } = usePermissionProvider();
@@ -66,7 +62,10 @@ export const PersonaDetailsPage = () => {
() => [
{
name: t('label.persona-plural'),
- url: getSettingPath(GlobalSettingsMenuCategory.PERSONA),
+ url: getSettingPath(
+ GlobalSettingsMenuCategory.MEMBERS,
+ GlobalSettingOptions.PERSONA
+ ),
},
{
name: getEntityName(personaDetails),
@@ -165,35 +164,14 @@ export const PersonaDetailsPage = () => {
);
const handleAfterDeleteAction = () => {
- history.push(getSettingPath(GlobalSettingsMenuCategory.PERSONA));
- };
-
- const handleTabChange = (activeKey: string) => {
- history.push({
- hash: activeKey,
- });
+ history.push(
+ getSettingPath(
+ GlobalSettingsMenuCategory.MEMBERS,
+ GlobalSettingOptions.PERSONA
+ )
+ );
};
- const tabItems = useMemo(() => {
- return [
- {
- label: t('label.user-plural'),
- key: 'users',
- children: (
-
- ),
- },
- {
- label: t('label.customize-ui'),
- key: 'customize-ui',
- children: ,
- },
- ];
- }, [personaDetails]);
-
if (isLoading) {
return ;
}
@@ -251,8 +229,19 @@ export const PersonaDetailsPage = () => {
+ ),
+ },
+ ]}
tabBarExtraContent={
{
}
- onChange={handleTabChange}
/>
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.test.tsx
index 4801d246c694..7f1682c942b6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.test.tsx
@@ -89,39 +89,28 @@ jest.mock('../../../rest/PersonaAPI', () => {
describe('PersonaPage', () => {
it('Component should render', async () => {
- await act(async () => {
+ act(() => {
render( );
});
+ expect(
+ await screen.findByTestId('user-list-v1-component')
+ ).toBeInTheDocument();
+ expect(await screen.findByTestId('add-persona-button')).toBeInTheDocument();
+ expect(
+ await screen.findByText('TitleBreadcrumb.component')
+ ).toBeInTheDocument();
+ expect(await screen.findByText('PageHeader.component')).toBeInTheDocument();
expect(
await screen.findByText('ErrorPlaceHolder.component')
).toBeInTheDocument();
});
it('AddEditPersonaForm should render onclick of add persona', async () => {
- (getAllPersonas as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({
- data: [
- {
- id: 'id1',
- name: 'sales',
- fullyQualifiedName: 'sales',
- displayName: 'Sales',
- },
- {
- id: 'id2',
- name: 'purchase',
- fullyQualifiedName: 'purchase',
- displayName: 'purchase',
- },
- ],
- })
- );
act(() => {
render( );
});
const addPersonaButton = await screen.findByTestId('add-persona-button');
-
await act(async () => {
fireEvent.click(addPersonaButton);
});
@@ -133,24 +122,6 @@ describe('PersonaPage', () => {
it('handlePersonaAddEditSave should be called onClick of save button', async () => {
const mockGetAllPersonas = getAllPersonas as jest.Mock;
- (getAllPersonas as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({
- data: [
- {
- id: 'id1',
- name: 'sales',
- fullyQualifiedName: 'sales',
- displayName: 'Sales',
- },
- {
- id: 'id2',
- name: 'purchase',
- fullyQualifiedName: 'purchase',
- displayName: 'purchase',
- },
- ],
- })
- );
act(() => {
render( );
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.tsx
index f9d90a85db96..e0cdef31555c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.tsx
@@ -55,7 +55,11 @@ export const PersonaPage = () => {
} = usePaging();
const breadcrumbs: TitleBreadcrumbProps['titleLinks'] = useMemo(
- () => getSettingPageEntityBreadCrumb(GlobalSettingsMenuCategory.PERSONA),
+ () =>
+ getSettingPageEntityBreadCrumb(
+ GlobalSettingsMenuCategory.MEMBERS,
+ t('label.persona-plural')
+ ),
[]
);
@@ -88,9 +92,8 @@ export const PersonaPage = () => {
const errorPlaceHolder = useMemo(
() => (
-
+
{
}
};
- if (isEmpty(persona) && !isLoading) {
- return (
- <>
- {errorPlaceHolder}
- {Boolean(addEditPersona) && (
-
- )}
- >
- );
- }
-
return (
-
+
@@ -169,6 +160,8 @@ export const PersonaPage = () => {
))}
+ {isEmpty(persona) && !isLoading && errorPlaceHolder}
+
{showPagination && (
{
/>
)}
+
{Boolean(addEditPersona) && (
Promise;
- currentNavigation?: NavigationItem[];
-}
-
-export const SettingsNavigationPage = ({
- onSave,
- currentNavigation,
-}: Props) => {
- const { fqn } = useFqn();
- const [isPersonaLoading, setIsPersonaLoading] = useState(true);
- const [personaDetails, setPersonaDetails] = useState(null);
- const { t } = useTranslation();
- const [saving, setSaving] = useState(false);
- const [targetKeys, setTargetKeys] = useState(
- currentNavigation
- ? getNestedKeysFromNavigationItems(currentNavigation)
- : getNestedKeys(sidebarOptions)
- );
-
- const treeData = filterAndArrangeTreeByKeys(
- cloneDeep(sidebarOptions),
- targetKeys
- );
-
- const handleChange = (newTargetKeys: string[]) => {
- setTargetKeys(newTargetKeys);
- };
-
- const titleLinks = useMemo(
- () => [
- {
- name: 'Settings',
- url: '/settings',
- },
- ...(personaDetails
- ? [
- {
- name: getEntityName(personaDetails),
- url: getPersonaDetailsPath(fqn),
- },
- ]
- : []),
- ],
- [personaDetails?.name]
- );
-
- const fetchPersonaDetails = async () => {
- try {
- setIsPersonaLoading(true);
- const persona = await getPersonaByName(fqn);
-
- setPersonaDetails(persona);
- } catch (error) {
- showErrorToast(error as AxiosError);
- } finally {
- setIsPersonaLoading(false);
- }
- };
-
- const handleSave = async () => {
- setSaving(true);
- const navigationItems = getNavigationItems(
- filterAndArrangeTreeByKeys(
- cloneDeep(sidebarOptions),
- targetKeys
- ).filter((t) => !isNil(t))
- );
-
- await onSave(navigationItems);
- setSaving(false);
- };
-
- const onDrop: TreeProps['onDrop'] = (info) => {
- const dropKey = info.node.key;
- const dragKey = info.dragNode.key;
- const dropPos = info.node.pos.split('-');
- const dropPosition =
- info.dropPosition - Number(dropPos[dropPos.length - 1]); // the drop position relative to the drop node, inside 0, top -1, bottom 1
-
- const loop = (
- data: TreeDataNode[],
- key: React.Key,
- callback: (node: TreeDataNode, i: number, data: TreeDataNode[]) => void
- ) => {
- for (let i = 0; i < data.length; i++) {
- if (data[i].key === key) {
- return callback(data[i], i, data);
- }
- if (data[i].children) {
- loop(data[i].children!, key, callback);
- }
- }
- };
- const tempData = cloneDeep(treeData);
-
- // Find dragObject
- let dragObj: TreeDataNode;
- loop(tempData, dragKey, (item, index, arr) => {
- arr.splice(index, 1);
- dragObj = item;
- });
-
- if (!info.dropToGap) {
- // Drop on the content
- loop(tempData, dropKey, (item) => {
- item.children = item.children || [];
- // where to insert. New item was inserted to the start of the array in this example, but can be anywhere
- item.children.unshift(dragObj);
- });
- } else {
- let ar: TreeDataNode[] = [];
- let i: number;
- loop(tempData, dropKey, (_item, index, arr) => {
- ar = arr;
- i = index;
- });
- if (dropPosition === -1) {
- // Drop on the top of the drop node
- ar.splice(i!, 0, dragObj!);
- } else {
- // Drop on the bottom of the drop node
- ar.splice(i! + 1, 0, dragObj!);
- }
- }
-
- handleChange(getNestedKeys(tempData));
- };
-
- const handleRemove = (key: string) => {
- setTargetKeys(targetKeys.filter((k) => k !== key));
- };
-
- const switcherIcon = useCallback(({ expanded }) => {
- return expanded ? : ;
- }, []);
-
- const handleReset = () => {
- handleChange(getNestedKeys(sidebarOptions));
- };
-
- const titleRenderer = (node: TreeDataNode) => (
-
- {node.title}{' '}
- handleRemove(node.key as string)}
- />
-
- );
-
- useEffect(() => {
- fetchPersonaDetails();
- }, [fqn]);
-
- if (isPersonaLoading) {
- return ;
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
- {t('label.save')}
-
-
- {t('label.reset')}
-
-
-
-
-
-
-
- );
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx
index 57de014c90fb..45527f49cf99 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx
@@ -61,7 +61,11 @@ import {
} from '../../enums/entity.enum';
import { CreateThread } from '../../generated/api/feed/createThread';
import { Tag } from '../../generated/entity/classification/tag';
-import { Table, TableType } from '../../generated/entity/data/table';
+import {
+ JoinedWith,
+ Table,
+ TableType,
+} from '../../generated/entity/data/table';
import { Suggestion } from '../../generated/entity/feed/suggestion';
import { ThreadType } from '../../generated/entity/feed/thread';
import { TestSummary } from '../../generated/tests/testCase';
@@ -87,6 +91,7 @@ import {
addToRecentViewed,
getFeedCounts,
getPartialNameFromTableFQN,
+ getTableFQNFromColumnFQN,
sortTagsCaseInsensitive,
} from '../../utils/CommonUtils';
import { defaultFields } from '../../utils/DatasetDetailsUtils';
@@ -95,11 +100,7 @@ import entityUtilClassBase from '../../utils/EntityUtilClassBase';
import { getEntityName } from '../../utils/EntityUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import tableClassBase from '../../utils/TableClassBase';
-import {
- getJoinsFromTableJoins,
- getTagsWithoutTier,
- getTierTags,
-} from '../../utils/TableUtils';
+import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
import { createTagObject, updateTierTag } from '../../utils/TagsUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
import { useTestCaseStore } from '../IncidentManager/IncidentManagerDetailPage/useTestCase.store';
@@ -308,13 +309,44 @@ const TableDetailsPageV1: React.FC = () => {
const { tags } = tableDetails;
const { joins } = tableDetails ?? {};
+ const tableFQNGrouping = [
+ ...(joins?.columnJoins?.flatMap(
+ (cjs) =>
+ cjs.joinedWith?.map((jw) => ({
+ fullyQualifiedName: getTableFQNFromColumnFQN(
+ jw.fullyQualifiedName
+ ),
+ joinCount: jw.joinCount,
+ })) ?? []
+ ) ?? []),
+ ...(joins?.directTableJoins ?? []),
+ ].reduce(
+ (result, jw) => ({
+ ...result,
+ [jw.fullyQualifiedName]:
+ (result[jw.fullyQualifiedName] ?? 0) + jw.joinCount,
+ }),
+ {} as Record
+ );
return {
...tableDetails,
tier: getTierTags(tags ?? []),
tableTags: getTagsWithoutTier(tags ?? []),
entityName: getEntityName(tableDetails),
- joinedTables: getJoinsFromTableJoins(joins),
+ joinedTables: Object.entries(tableFQNGrouping)
+ .map(
+ ([fullyQualifiedName, joinCount]) => ({
+ fullyQualifiedName,
+ joinCount,
+ name: getPartialNameFromTableFQN(
+ fullyQualifiedName,
+ [FqnPart.Database, FqnPart.Table],
+ FQN_SEPARATOR_CHAR
+ ),
+ })
+ )
+ .sort((a, b) => b.joinCount - a.joinCount),
};
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/PersonaAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/PersonaAPI.ts
index 3637229b85d1..29b2aa23b33d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/rest/PersonaAPI.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/rest/PersonaAPI.ts
@@ -36,12 +36,12 @@ export const getAllPersonas = async (params: GetPersonasParams) => {
return response.data;
};
-export const getPersonaByName = async (fqn: string, fields?: string) => {
+export const getPersonaByName = async (fqn: string) => {
const response = await axiosClient.get(
`${BASE_URL}/name/${getEncodedFqn(fqn)}`,
{
params: {
- fields: fields ?? TabSpecificField.USERS,
+ fields: TabSpecificField.USERS,
},
}
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/tree.less b/openmetadata-ui/src/main/resources/ui/src/styles/tree.less
index 80b91f77ed1c..1b71463bfb8e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/styles/tree.less
+++ b/openmetadata-ui/src/main/resources/ui/src/styles/tree.less
@@ -55,9 +55,7 @@
}
.ant-tree-switcher-icon {
- width: 12px;
- height: 12px;
- color: @grey-4;
+ color: black;
}
.execution-node-container {
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..e6d407ab9195 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
@@ -11,9 +11,13 @@
* limitations under the License.
*/
import { FC } from 'react';
-import AuthenticatedAppRouter from '../components/AppRouter/AuthenticatedAppRouter';
import { ApplicationRoutesClassBase } from './ApplicationRoutesClassBase';
+jest.mock('../components/AppRouter/AuthenticatedAppRouter', () => ({
+ __esModule: true,
+ default: 'AuthenticatedAppRouter',
+}));
+
describe('ApplicationRoutesClassBase', () => {
let applicationRoutesClassBase: ApplicationRoutesClassBase;
@@ -24,6 +28,6 @@ describe('ApplicationRoutesClassBase', () => {
it('should return AuthenticatedAppRouter from getRouteElements', () => {
const result: FC = applicationRoutesClassBase.getRouteElements();
- expect(result).toBe(AuthenticatedAppRouter);
+ expect(result).toBe('AuthenticatedAppRouter');
});
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ContainerDetailUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ContainerDetailUtils.ts
index 656ba18bc230..e47ba8654f5a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/ContainerDetailUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ContainerDetailUtils.ts
@@ -12,8 +12,7 @@
*/
import { isEmpty, omit } from 'lodash';
import { EntityTags } from 'Models';
-import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
-import { EntityTabs, TabSpecificField } from '../enums/entity.enum';
+import { TabSpecificField } from '../enums/entity.enum';
import { Column, ContainerDataModel } from '../generated/entity/data/container';
import { LabelType, State, TagLabel } from '../generated/type/tagLabel';
@@ -96,72 +95,5 @@ export const updateContainerColumnDescription = (
});
};
-export const getContainerDetailsPageDefaultLayout = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
-};
-
// eslint-disable-next-line max-len
export const ContainerFields = `${TabSpecificField.TAGS}, ${TabSpecificField.OWNERS},${TabSpecificField.FOLLOWERS},${TabSpecificField.DATAMODEL}, ${TabSpecificField.DOMAIN},${TabSpecificField.DATA_PRODUCTS}`;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage.ts
deleted file mode 100644
index bf22e99fe1d2..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage.ts
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Copyright 2023 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 {
- CustomizeTabWidget,
- CustomizeTabWidgetProps,
-} from '../../components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget';
-import { GenericWidget } from '../../components/Glossary/CustomiseWidgets/SynonymsWidget/GenericWidget';
-import GlossaryHeader from '../../components/Glossary/GlossaryHeader/GlossaryHeader.component';
-import { GlossaryHeaderProps } from '../../components/Glossary/GlossaryHeader/GlossaryHeader.interface';
-import { GlossaryHeaderWidget } from '../../components/Glossary/GlossaryHeader/GlossaryHeaderWidget';
-import {
- CommonWidgetType,
- CUSTOM_PROPERTIES_WIDGET,
- DESCRIPTION_WIDGET,
-} from '../../constants/CustomizeWidgets.constants';
-import { GlossaryTermDetailPageWidgetKeys } from '../../enums/CustomizeDetailPage.enum';
-import { EntityTabs } from '../../enums/entity.enum';
-import {
- WidgetCommonProps,
- WidgetConfig,
-} from '../../pages/CustomizablePage/CustomizablePage.interface';
-
-type ComponentMap = {
- [GlossaryTermDetailPageWidgetKeys.HEADER]: {
- component: typeof GlossaryHeader;
- props: GlossaryHeaderProps & WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.TABS]: {
- component: typeof CustomizeTabWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.DESCRIPTION]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.TAGS]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.DOMAIN]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.SYNONYMS]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.RELATED_TERMS]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.REFERENCES]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.OWNER]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.REVIEWER]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
-};
-
-class CustomizeGlossaryTermPageClassBase {
- defaultWidgetHeight = 2;
- detailPageWidgetMargin = 16;
- detailPageRowHeight = 100;
- detailPageMaxGridSize = 4;
- defaultLayout: Array = [];
- detailPageWidgetDefaultHeights: Record<
- keyof typeof GlossaryTermDetailPageWidgetKeys,
- number
- >;
- widgets: ComponentMap;
-
- constructor() {
- this.detailPageWidgetDefaultHeights = {
- HEADER: 1,
- DESCRIPTION: 2,
- TAGS: 2,
- DOMAIN: 1,
- CUSTOM_PROPERTIES: 3,
- TABS: 10,
- SYNONYMS: 1,
- RELATED_TERMS: 1,
- REFERENCES: 2,
- OWNER: 1,
- REVIEWER: 1,
- TERMS_TABLE: 1,
- EMPTY_WIDGET_PLACEHOLDER: 3,
- };
-
- this.defaultLayout = [
- {
- h: this.detailPageWidgetDefaultHeights.HEADER,
- i: GlossaryTermDetailPageWidgetKeys.HEADER,
- w: 8,
- x: 0,
- y: 0,
- static: true,
- },
- {
- h: this.detailPageWidgetDefaultHeights.TABS,
- i: GlossaryTermDetailPageWidgetKeys.TABS,
- w: 8,
- x: 0,
- y: 1,
- static: true,
- },
- ];
-
- this.widgets = {
- [GlossaryTermDetailPageWidgetKeys.HEADER]: {
- component: GlossaryHeader,
- props: {} as GlossaryHeaderProps & WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.TABS]: {
- component: CustomizeTabWidget,
- props: {} as CustomizeTabWidgetProps,
- },
- [GlossaryTermDetailPageWidgetKeys.DESCRIPTION]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.TAGS]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.DOMAIN]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.SYNONYMS]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.RELATED_TERMS]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.REFERENCES]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.OWNER]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.REVIEWER]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- };
- }
-
- protected updateDefaultLayoutLayout(layout: Array) {
- this.defaultLayout = layout;
- }
-
- protected updateLandingPageWidgetDefaultHeights(obj: Record) {
- this.detailPageWidgetDefaultHeights = obj;
- }
-
- public getKeyFromWidgetName(
- widgetName: string
- ): GlossaryTermDetailPageWidgetKeys {
- switch (widgetName) {
- case 'HEADER':
- return GlossaryTermDetailPageWidgetKeys.HEADER;
- case 'DESCRIPTION':
- return GlossaryTermDetailPageWidgetKeys.DESCRIPTION;
- case 'TAGS':
- return GlossaryTermDetailPageWidgetKeys.TAGS;
- case 'DOMAIN':
- return GlossaryTermDetailPageWidgetKeys.DOMAIN;
- case 'CUSTOM_PROPERTIES':
- return GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES;
- case 'TABS':
- return GlossaryTermDetailPageWidgetKeys.TABS;
- case 'SYNONYMS':
- return GlossaryTermDetailPageWidgetKeys.SYNONYMS;
- case 'RELATED_TERMS':
- return GlossaryTermDetailPageWidgetKeys.RELATED_TERMS;
- case 'REFERENCES':
- return GlossaryTermDetailPageWidgetKeys.REFERENCES;
- case 'OWNER':
- return GlossaryTermDetailPageWidgetKeys.OWNER;
- case 'REVIEWER':
- return GlossaryTermDetailPageWidgetKeys.REVIEWER;
- default:
- return GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER;
- }
- }
-
- /**
- *
- * @param string widgetKey
- * @returns React.FC<
- {
- isEditView?: boolean;
- widgetKey: string;
- handleRemoveWidget?: (widgetKey: string) => void;
- announcements: Thread[];
- followedData: EntityReference[];
- followedDataCount: number;
- isLoadingOwnedData: boolean;
- }
- >
- */
- public getWidgetsFromKey(
- widgetKey: T
- ) {
- if (widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.HEADER)) {
- return GlossaryHeaderWidget;
- } else if (widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.TABS)) {
- return CustomizeTabWidget;
- } else {
- return GenericWidget;
- }
- }
-
- public getWidgetHeight(widgetName: string) {
- switch (widgetName) {
- case 'HEADER':
- return this.detailPageWidgetDefaultHeights.HEADER;
- case 'DESCRIPTION':
- return this.detailPageWidgetDefaultHeights.DESCRIPTION;
- case 'TAGS':
- return this.detailPageWidgetDefaultHeights.TAGS;
- case 'DOMAIN':
- return this.detailPageWidgetDefaultHeights.DOMAIN;
- case 'CUSTOM_PROPERTIES':
- return this.detailPageWidgetDefaultHeights.CUSTOM_PROPERTIES;
- case 'TABS':
- return this.detailPageWidgetDefaultHeights.TABS;
- case 'SYNONYMS':
- return this.detailPageWidgetDefaultHeights.SYNONYMS;
- case 'RELATED_TERMS':
- return this.detailPageWidgetDefaultHeights.RELATED_TERMS;
- case 'REFERENCES':
- return this.detailPageWidgetDefaultHeights.REFERENCES;
- case 'OWNER':
- return this.detailPageWidgetDefaultHeights.OWNER;
- case 'REVIEWER':
- return this.detailPageWidgetDefaultHeights.REVIEWER;
-
- default:
- return this.defaultWidgetHeight;
- }
- }
-
- public getDefaultWidgetForTab(tab: EntityTabs) {
- if (tab === EntityTabs.OVERVIEW) {
- return [
- {
- h: this.detailPageWidgetDefaultHeights.DESCRIPTION,
- i: GlossaryTermDetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.CUSTOM_PROPERTIES,
- i: GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 7,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.DOMAIN,
- i: GlossaryTermDetailPageWidgetKeys.DOMAIN,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.SYNONYMS,
- i: GlossaryTermDetailPageWidgetKeys.SYNONYMS,
- w: 3,
- x: 0,
- y: 2,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.RELATED_TERMS,
- i: GlossaryTermDetailPageWidgetKeys.RELATED_TERMS,
- w: 3,
- x: 3,
- y: 2,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.REFERENCES,
- i: GlossaryTermDetailPageWidgetKeys.REFERENCES,
- w: 3,
- x: 0,
- y: 3,
- static: false,
- },
-
- {
- h: this.detailPageWidgetDefaultHeights.TAGS,
- i: GlossaryTermDetailPageWidgetKeys.TAGS,
- w: 3,
- x: 3,
- y: 3,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.OWNER,
- i: GlossaryTermDetailPageWidgetKeys.OWNER,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.REVIEWER,
- i: GlossaryTermDetailPageWidgetKeys.REVIEWER,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
- }
-
- return [];
- }
-
- public getCommonWidgetList(): CommonWidgetType[] {
- return [
- DESCRIPTION_WIDGET,
- {
- fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.SYNONYMS,
- name: 'Synonyms',
- data: {
- gridSizes: ['small'],
- },
- },
- {
- fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.RELATED_TERMS,
- name: 'Related Terms',
- data: { gridSizes: ['small'] },
- },
- {
- fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.REFERENCES,
- name: 'References',
- data: { gridSizes: ['small'] },
- },
- {
- fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.REVIEWER,
- name: 'Reviewer',
- data: { gridSizes: ['small'] },
- },
- CUSTOM_PROPERTIES_WIDGET,
- ];
- }
-}
-
-const customizeGlossaryTermPageClassBase =
- new CustomizeGlossaryTermPageClassBase();
-
-export default customizeGlossaryTermPageClassBase;
-export { CustomizeGlossaryTermPageClassBase };
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizaNavigation/CustomizeNavigation.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizaNavigation/CustomizeNavigation.ts
deleted file mode 100644
index a0862d51b781..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizaNavigation/CustomizeNavigation.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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 { DataNode } from 'antd/lib/tree';
-import { NavigationItem } from '../../generated/system/ui/uiCustomization';
-
-export const filterAndArrangeTreeByKeys = <
- T extends { key: string | number; children?: T[] }
->(
- tree: T[],
- keys: Array
-): T[] => {
- // Sort nodes according to the keys order
- function sortByKeys(nodeArray: T[]) {
- return nodeArray.sort((a, b) => keys.indexOf(a.key) - keys.indexOf(b.key));
- }
-
- // Helper function to recursively filter and arrange the tree
- function filterAndArrange(node: T) {
- // If the current node's key is in the keys array, process it
- if (keys.includes(node.key)) {
- // If the node has children, we recursively filter and arrange them
- if (node.children && node.children.length > 0) {
- node.children = node.children
- .map(filterAndArrange) // Recursively filter and arrange children
- .filter((t): t is T => t !== null); // Remove any undefined children
-
- // Sort the children according to the order of the keys array
- node.children = sortByKeys(node.children);
- }
-
- return node; // Return the node if it has the required key
- }
-
- return null; // Return null if the key doesn't match
- }
-
- // Apply the filter and arrange function to the entire tree
- let filteredTree = tree
- .map(filterAndArrange)
- .filter((t): t is T => t !== null);
-
- // Sort the filtered tree based on the order of keys at the root level
- filteredTree = sortByKeys(filteredTree);
-
- return filteredTree;
-};
-
-export const getNestedKeys = <
- T extends { key: string | number; children?: T[] }
->(
- data: T[]
-): string[] =>
- data.reduce((acc: string[], item: T): string[] => {
- if (item.children) {
- return [
- ...acc,
- item.key as string,
- ...getNestedKeys(item.children ?? []),
- ];
- }
-
- return [...acc, item.key as string];
- }, [] as string[]);
-
-export const getNavigationItems = (items: DataNode[]): NavigationItem[] =>
- items
- .map((item) =>
- item.children
- ? ({
- id: item.key,
- title: item.title,
- pageId: item.key,
- children: getNavigationItems(item.children),
- } as NavigationItem)
- : ({
- id: item.key,
- title: item.title,
- pageId: item.key,
- } as NavigationItem)
- )
- .filter(Boolean);
-
-export const getNestedKeysFromNavigationItems = (data: NavigationItem[]) =>
- data.reduce((acc: string[], item: NavigationItem): string[] => {
- if (item.children) {
- return [
- ...acc,
- item.id,
- ...getNestedKeysFromNavigationItems(item.children),
- ];
- }
-
- return [...acc, item.id];
- }, [] as string[]);
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 50a4abffbc0b..5adc4fe6769f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx
@@ -32,7 +32,7 @@ import { Document } from '../generated/entity/docStore/document';
import { Thread } from '../generated/entity/feed/thread';
import { EntityReference } from '../generated/entity/type';
import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface';
-import customizeMyDataPageClassBase from './CustomizeMyDataPageClassBase';
+import customizePageClassBase from './CustomizePageClassBase';
const getNewWidgetPlacement = (
currentLayout: WidgetConfig[],
@@ -58,7 +58,7 @@ const getNewWidgetPlacement = (
// Check if there's enough space to place the new widget on the same row
if (
- customizeMyDataPageClassBase.landingPageMaxGridSize -
+ customizePageClassBase.landingPageMaxGridSize -
(lowestWidgetLayout.x + lowestWidgetLayout.w) >=
widgetWidth
) {
@@ -84,7 +84,7 @@ export const getAddWidgetHandler =
) =>
(currentLayout: Array) => {
const widgetFQN = uniqueId(`${newWidgetData.fullyQualifiedName}-`);
- const widgetHeight = customizeMyDataPageClassBase.getWidgetHeight(
+ const widgetHeight = customizePageClassBase.getWidgetHeight(
newWidgetData.name
);
@@ -265,7 +265,7 @@ export const getWidgetFromKey = ({
);
}
- const Widget = customizeMyDataPageClassBase.getWidgetsFromKey(widgetConfig.i);
+ const Widget = customizePageClassBase.getWidgetsFromKey(widgetConfig.i);
return (
= {
- header: 1,
- description: 6,
- tableSchema: 3,
- topicSchema: 3,
- announcement: 3,
- frequentlyJoinedTables: 3,
- dataProduct: 3,
- tags: 3,
- glossaryTerms: 3,
- customProperty: 3,
- tabs: 1,
- announcements: 3,
- };
-
- announcementWidget: WidgetConfig = {
- h: this.detailPageWidgetDefaultHeights.announcements,
- i: DetailPageWidgetKeys.ANNOUNCEMENTS,
- w: 1,
- x: 3,
- y: 0,
- static: false, // Making announcement widget fixed on top right position
- };
-
- defaultLayout: Array = [
- {
- h: this.detailPageWidgetDefaultHeights.header,
- i: DetailPageWidgetKeys.HEADER,
- w: 4,
- x: 0,
- y: 0,
- static: true,
- },
- {
- h: this.detailPageWidgetDefaultHeights.tabs,
- i: DetailPageWidgetKeys.TABS,
- w: 4,
- x: 0,
- y: 1,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.tableSchema,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 1,
- x: 3,
- y: 6,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.dataProduct,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 0,
- y: 9,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.tags,
- i: DetailPageWidgetKeys.TAGS,
- w: 3,
- x: 0,
- y: 6,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.glossaryTerms,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 1,
- x: 3,
- y: 1.5,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.frequentlyJoinedTables,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 1,
- x: 3,
- y: 3,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.customProperty,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 1,
- x: 3,
- y: 4.5,
- static: false,
- },
- {
- h: this.detailPageWidgetDefaultHeights.announcement,
- i: DetailPageWidgetKeys.ANNOUNCEMENTS,
- w: 1,
- x: 3,
- y: 0,
- static: true,
- },
- ];
-
- protected updateDefaultLayoutLayout(layout: Array) {
- this.defaultLayout = layout;
- }
-
- protected updateLandingPageWidgetDefaultHeights(obj: Record) {
- this.detailPageWidgetDefaultHeights = obj;
- }
-
- /**
- *
- * @param string widgetKey
- * @returns React.FC<
- {
- isEditView?: boolean;
- widgetKey: string;
- handleRemoveWidget?: (widgetKey: string) => void;
- announcements: Thread[];
- followedData: EntityReference[];
- followedDataCount: number;
- isLoadingOwnedData: boolean;
- }
- >
- */
- public getWidgetsFromKey(_widgetKey: string): FC {
- return GenericWidget;
- }
-
- public getWidgetHeight(widgetName: string) {
- switch (widgetName) {
- case 'ActivityFeed':
- return this.detailPageWidgetDefaultHeights.activityFeed;
- case 'DataAssets':
- return this.detailPageWidgetDefaultHeights.DataAssets;
- case 'Announcements':
- return this.detailPageWidgetDefaultHeights.announcements;
- case 'Following':
- return this.detailPageWidgetDefaultHeights.following;
- case 'RecentlyViewed':
- return this.detailPageWidgetDefaultHeights.recentlyViewed;
- case 'MyData':
- return this.detailPageWidgetDefaultHeights.myData;
- case 'KPI':
- return this.detailPageWidgetDefaultHeights.kpi;
- case 'TotalAssets':
- return this.detailPageWidgetDefaultHeights.totalAssets;
- default:
- return this.defaultWidgetHeight;
- }
- }
-
- public getCommonWidgetList() {
- return [
- DESCRIPTION_WIDGET,
- TAGS_WIDGET,
- DOMAIN_WIDGET,
- OWNER_WIDGET,
- CUSTOM_PROPERTIES_WIDGET,
- ];
- }
-}
-
-const customizeDetailPageClassBase = new CustomizeDetailPageClassBase();
-
-export default customizeDetailPageClassBase;
-export { CustomizeDetailPageClassBase };
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryPage/CustomizeGlossaryPage.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryPage/CustomizeGlossaryPage.ts
deleted file mode 100644
index ced51bdae083..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryPage/CustomizeGlossaryPage.ts
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright 2023 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 {
- CustomizeTabWidget,
- CustomizeTabWidgetProps,
-} from '../../components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget';
-import { GenericWidget } from '../../components/Glossary/CustomiseWidgets/SynonymsWidget/GenericWidget';
-import GlossaryHeader from '../../components/Glossary/GlossaryHeader/GlossaryHeader.component';
-import { GlossaryHeaderProps } from '../../components/Glossary/GlossaryHeader/GlossaryHeader.interface';
-import { GlossaryHeaderWidget } from '../../components/Glossary/GlossaryHeader/GlossaryHeaderWidget';
-import { GlossaryTermDetailPageWidgetKeys } from '../../enums/CustomizeDetailPage.enum';
-import { EntityTabs } from '../../enums/entity.enum';
-import {
- WidgetCommonProps,
- WidgetConfig,
-} from '../../pages/CustomizablePage/CustomizablePage.interface';
-
-type ComponentMap = {
- [GlossaryTermDetailPageWidgetKeys.HEADER]: {
- component: typeof GlossaryHeader;
- props: GlossaryHeaderProps & WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.TABS]: {
- component: typeof CustomizeTabWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.DESCRIPTION]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.TAGS]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.DOMAIN]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.SYNONYMS]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.RELATED_TERMS]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.REFERENCES]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.OWNER]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.REVIEWER]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
- [GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER]: {
- component: typeof GenericWidget;
- props: WidgetCommonProps;
- };
-};
-
-class CustomizeGlossaryPageClassBase {
- defaultWidgetHeight = 2;
- detailWidgetMargin = 16;
- rowHeight = 100;
- maxGridSize = 4;
- defaultLayout: Array = [];
- defaultHeights: Record;
- widgets: ComponentMap;
-
- constructor() {
- this.defaultHeights = {
- HEADER: 1,
- DESCRIPTION: 2,
- TAGS: 2,
- DOMAIN: 1,
- CUSTOM_PROPERTIES: 3,
- TABS: 10,
- SYNONYMS: 1,
- RELATED_TERMS: 1,
- REFERENCES: 1,
- OWNER: 1,
- REVIEWER: 1,
- TERMS_TABLE: 6,
- EMPTY_WIDGET_PLACEHOLDER: 3,
- };
-
- this.defaultLayout = [
- {
- h: this.defaultHeights.HEADER,
- i: GlossaryTermDetailPageWidgetKeys.HEADER,
- w: 8,
- x: 0,
- y: 0,
- static: true,
- },
- {
- h: this.defaultHeights.TABS,
- i: GlossaryTermDetailPageWidgetKeys.TABS,
- w: 8,
- x: 0,
- y: 1,
- static: true,
- },
- ];
-
- this.widgets = {
- [GlossaryTermDetailPageWidgetKeys.HEADER]: {
- component: GlossaryHeader,
- props: {} as GlossaryHeaderProps & WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.TABS]: {
- component: CustomizeTabWidget,
- props: {} as CustomizeTabWidgetProps,
- },
- [GlossaryTermDetailPageWidgetKeys.DESCRIPTION]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.TAGS]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.DOMAIN]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.SYNONYMS]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.RELATED_TERMS]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.REFERENCES]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.OWNER]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.REVIEWER]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- [GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER]: {
- component: GenericWidget,
- props: {} as WidgetCommonProps,
- },
- };
- }
-
- protected updateDefaultLayoutLayout(layout: Array) {
- this.defaultLayout = layout;
- }
-
- protected updateLandingPageWidgetDefaultHeights(obj: Record) {
- this.defaultHeights = obj;
- }
-
- public getKeyFromWidgetName(
- widgetName: string
- ): GlossaryTermDetailPageWidgetKeys {
- switch (widgetName) {
- case 'HEADER':
- return GlossaryTermDetailPageWidgetKeys.HEADER;
- case 'DESCRIPTION':
- return GlossaryTermDetailPageWidgetKeys.DESCRIPTION;
- case 'TAGS':
- return GlossaryTermDetailPageWidgetKeys.TAGS;
- case 'DOMAIN':
- return GlossaryTermDetailPageWidgetKeys.DOMAIN;
- case 'CUSTOM_PROPERTIES':
- return GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES;
- case 'TABS':
- return GlossaryTermDetailPageWidgetKeys.TABS;
- case 'SYNONYMS':
- return GlossaryTermDetailPageWidgetKeys.SYNONYMS;
- case 'RELATED_TERMS':
- return GlossaryTermDetailPageWidgetKeys.RELATED_TERMS;
- case 'REFERENCES':
- return GlossaryTermDetailPageWidgetKeys.REFERENCES;
- case 'OWNER':
- return GlossaryTermDetailPageWidgetKeys.OWNER;
- case 'REVIEWER':
- return GlossaryTermDetailPageWidgetKeys.REVIEWER;
- default:
- return GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER;
- }
- }
-
- /**
- *
- * @param string widgetKey
- * @returns React.FC<
- {
- isEditView?: boolean;
- widgetKey: string;
- handleRemoveWidget?: (widgetKey: string) => void;
- announcements: Thread[];
- followedData: EntityReference[];
- followedDataCount: number;
- isLoadingOwnedData: boolean;
- }
- >
- */
- public getWidgetsFromKey(
- widgetKey: T
- ) {
- if (widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.HEADER)) {
- return GlossaryHeaderWidget;
- } else if (widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.TABS)) {
- return CustomizeTabWidget;
- } else {
- return GenericWidget;
- }
- }
-
- public getWidgetHeight(widgetName: string) {
- switch (widgetName) {
- case 'HEADER':
- return this.defaultHeights.HEADER;
- case 'DESCRIPTION':
- return this.defaultHeights.DESCRIPTION;
- case 'TAGS':
- return this.defaultHeights.TAGS;
- case 'DOMAIN':
- return this.defaultHeights.DOMAIN;
- case 'CUSTOM_PROPERTIES':
- return this.defaultHeights.CUSTOM_PROPERTIES;
- case 'TABS':
- return this.defaultHeights.TABS;
- case 'SYNONYMS':
- return this.defaultHeights.SYNONYMS;
- case 'RELATED_TERMS':
- return this.defaultHeights.RELATED_TERMS;
- case 'REFERENCES':
- return this.defaultHeights.REFERENCES;
- case 'OWNER':
- return this.defaultHeights.OWNER;
- case 'REVIEWER':
- return this.defaultHeights.REVIEWER;
- default:
- return this.defaultWidgetHeight;
- }
- }
-
- public getDefaultWidgetForTab(tab: EntityTabs) {
- if (tab === EntityTabs.TERMS) {
- return [
- {
- h: this.defaultHeights.DESCRIPTION,
- i: GlossaryTermDetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: this.defaultHeights.TERMS_TABLE,
- i: GlossaryTermDetailPageWidgetKeys.TERMS_TABLE,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: this.defaultHeights.DOMAIN,
- i: GlossaryTermDetailPageWidgetKeys.DOMAIN,
- w: 2,
- x: 6,
- y: 0,
- },
- {
- h: this.defaultHeights.OWNER,
- i: GlossaryTermDetailPageWidgetKeys.OWNER,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
-
- {
- h: this.defaultHeights.REVIEWER,
- i: GlossaryTermDetailPageWidgetKeys.REVIEWER,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: this.defaultHeights.TAGS,
- i: GlossaryTermDetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- ];
- }
-
- return [];
- }
-}
-
-const customizeGlossaryPageClassBase = new CustomizeGlossaryPageClassBase();
-
-export default customizeGlossaryPageClassBase;
-export { CustomizeGlossaryPageClassBase };
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePage/CustomizePageUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePage/CustomizePageUtils.ts
deleted file mode 100644
index 31b4498082e0..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePage/CustomizePageUtils.ts
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * 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 { TabsProps } from 'antd';
-import {
- CommonWidgetType,
- CUSTOM_PROPERTIES_WIDGET,
- DESCRIPTION_WIDGET,
- DOMAIN_WIDGET,
- GLOSSARY_TERMS_WIDGET,
- TAGS_WIDGET,
-} from '../../constants/CustomizeWidgets.constants';
-import { EntityTabs } from '../../enums/entity.enum';
-import { PageType } from '../../generated/system/ui/page';
-import customizeGlossaryTermPageClassBase from '../CustomiseGlossaryTermPage/CustomizeGlossaryTermPage';
-import customizeDetailPageClassBase from '../CustomizeDetailPage/CustomizeDetailPage';
-import customizeGlossaryPageClassBase from '../CustomizeGlossaryPage/CustomizeGlossaryPage';
-import customizeMyDataPageClassBase from '../CustomizeMyDataPageClassBase';
-import i18n from '../i18next/LocalUtil';
-import tableClassBase from '../TableClassBase';
-
-export const getDefaultLayout = (pageType: string) => {
- switch (pageType) {
- case PageType.GlossaryTerm:
- return customizeGlossaryTermPageClassBase.defaultLayout;
- case PageType.Table:
- return customizeDetailPageClassBase.defaultLayout;
- case PageType.LandingPage:
- default:
- return customizeMyDataPageClassBase.defaultLayout;
- }
-};
-
-export const getGlossaryTermDefaultTabs = () => {
- return [
- {
- id: EntityTabs.OVERVIEW,
- displayName: 'Overview',
- layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(
- EntityTabs.OVERVIEW
- ),
- name: EntityTabs.OVERVIEW,
- editable: true,
- },
- {
- id: EntityTabs.GLOSSARY_TERMS,
- displayName: 'Glossary Terms',
- layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(
- EntityTabs.GLOSSARY_TERMS
- ),
- name: EntityTabs.GLOSSARY_TERMS,
- editable: false,
- },
- {
- id: EntityTabs.ASSETS,
- displayName: 'Assets',
- layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(
- EntityTabs.ASSETS
- ),
- name: EntityTabs.ASSETS,
- editable: false,
- },
- {
- displayName: 'Activity Feeds & Tasks',
- name: EntityTabs.ACTIVITY_FEED,
- id: EntityTabs.ACTIVITY_FEED,
- layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(
- EntityTabs.ACTIVITY_FEED
- ),
- editable: false,
- },
- {
- id: EntityTabs.CUSTOM_PROPERTIES,
- name: EntityTabs.CUSTOM_PROPERTIES,
- displayName: 'Custom Property',
- layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(
- EntityTabs.CUSTOM_PROPERTIES
- ),
- editable: false,
- },
- ];
-};
-
-export const getGlossaryDefaultTabs = () => {
- return [
- {
- id: EntityTabs.TERMS,
- name: EntityTabs.TERMS,
- displayName: 'Terms',
- layout: customizeGlossaryPageClassBase.getDefaultWidgetForTab(
- EntityTabs.TERMS
- ),
- editable: true,
- },
- {
- displayName: 'Activity Feeds & Tasks',
- name: EntityTabs.ACTIVITY_FEED,
- id: EntityTabs.ACTIVITY_FEED,
- layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(
- EntityTabs.ACTIVITY_FEED
- ),
- editable: false,
- },
- ];
-};
-
-export const getTabLabelFromId = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.OVERVIEW:
- return i18n.t('label.overview');
- case EntityTabs.GLOSSARY_TERMS:
- return i18n.t('label.glossary-terms');
- case EntityTabs.ASSETS:
- return i18n.t('label.assets');
- case EntityTabs.ACTIVITY_FEED:
- return i18n.t('label.activity-feed-and-task-plural');
- case EntityTabs.CUSTOM_PROPERTIES:
- return i18n.t('label.custom-property-plural');
- case EntityTabs.TERMS:
- return i18n.t('label.terms');
- case EntityTabs.SCHEMA:
- return i18n.t('label.schema');
- case EntityTabs.SAMPLE_DATA:
- return i18n.t('label.sample-data');
- case EntityTabs.TABLE_QUERIES:
- return i18n.t('label.query-plural');
- case EntityTabs.PROFILER:
- return i18n.t('label.profiler-amp-data-quality');
- case EntityTabs.INCIDENTS:
- return i18n.t('label.incident-plural');
- case EntityTabs.LINEAGE:
- return i18n.t('label.lineage');
- case EntityTabs.VIEW_DEFINITION:
- return i18n.t('label.view-definition');
- case EntityTabs.DBT:
- return i18n.t('label.dbt-lowercase');
- default:
- return '';
- }
-};
-
-const getCustomizeTabObject = (tab: EntityTabs) => ({
- id: tab,
- name: tab,
- displayName: getTabLabelFromId(tab),
- layout: tableClassBase.getDefaultLayout(tab),
- editable: [EntityTabs.SCHEMA, EntityTabs.OVERVIEW, EntityTabs.TERMS].includes(
- tab
- ),
-});
-
-export const getTableDefaultTabs = () => {
- const tabs = tableClassBase
- .getTableDetailPageTabsIds()
- .map(getCustomizeTabObject);
-
- return tabs;
-};
-
-export const getDefaultTabs = (pageType?: string) => {
- switch (pageType) {
- case PageType.GlossaryTerm:
- return getGlossaryTermDefaultTabs();
- case PageType.Glossary:
- return getGlossaryDefaultTabs();
- case PageType.Table:
- return getTableDefaultTabs();
- case PageType.Container:
- default:
- return [
- {
- id: EntityTabs.CUSTOM_PROPERTIES,
- name: EntityTabs.CUSTOM_PROPERTIES,
- displayName: 'Custom Property',
- layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(
- EntityTabs.CUSTOM_PROPERTIES
- ),
- },
- ];
- }
-};
-
-export const getDefaultWidgetForTab = (pageType: PageType, tab: EntityTabs) => {
- switch (pageType) {
- case PageType.GlossaryTerm:
- case PageType.Glossary:
- return customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(tab);
- case PageType.Table:
- return tableClassBase.getDefaultLayout(tab);
- default:
- return [];
- }
-};
-
-export const sortTabs = (tabs: TabsProps['items'], order: string[]) => {
- return [...(tabs ?? [])].sort((a, b) => {
- const orderA = order.indexOf(a.key);
- const orderB = order.indexOf(b.key);
-
- if (orderA !== -1 && orderB !== -1) {
- return orderA - orderB;
- }
- if (orderA !== -1) {
- return -1;
- }
- if (orderB !== -1) {
- return 1;
- }
-
- const ia = tabs?.indexOf(a) ?? 0;
- const ib = tabs?.indexOf(b) ?? 0;
-
- return ia - ib;
- });
-};
-
-export const getCustomizableWidgetByPage = (
- pageType: PageType
-): CommonWidgetType[] => {
- switch (pageType) {
- case PageType.GlossaryTerm:
- case PageType.Glossary:
- return customizeGlossaryTermPageClassBase.getCommonWidgetList();
-
- case PageType.Table:
- return [
- DESCRIPTION_WIDGET,
- CUSTOM_PROPERTIES_WIDGET,
- DOMAIN_WIDGET,
- TAGS_WIDGET,
- GLOSSARY_TERMS_WIDGET,
- ];
- case PageType.LandingPage:
- default:
- return [];
- }
-};
-
-export const getDummyDataByPage = (pageType: PageType) => {
- switch (pageType) {
- case PageType.Table:
- return tableClassBase.getDummyData();
-
- case PageType.LandingPage:
- default:
- return {};
- }
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeMyDataPageClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePageClassBase.ts
similarity index 97%
rename from openmetadata-ui/src/main/resources/ui/src/utils/CustomizeMyDataPageClassBase.ts
rename to openmetadata-ui/src/main/resources/ui/src/utils/CustomizePageClassBase.ts
index b0920e95f900..528257c0bda5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeMyDataPageClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePageClassBase.ts
@@ -43,7 +43,7 @@ import {
WidgetConfig,
} from '../pages/CustomizablePage/CustomizablePage.interface';
-class CustomizeMyDataPageClassBase {
+class CustomizePageClassBase {
defaultWidgetHeight = 3;
landingPageWidgetMargin = 16;
landingPageRowHeight = 100;
@@ -246,7 +246,7 @@ class CustomizeMyDataPageClassBase {
}
}
-const customizeMyDataPageClassBase = new CustomizeMyDataPageClassBase();
+const customizePageClassBase = new CustomizePageClassBase();
-export default customizeMyDataPageClassBase;
-export { CustomizeMyDataPageClassBase };
+export default customizePageClassBase;
+export { CustomizePageClassBase };
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts
index 587248779288..e7971723ffff 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts
@@ -12,8 +12,7 @@
*/
import { AxiosError } from 'axios';
-import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
-import { EntityTabs, TabSpecificField } from '../enums/entity.enum';
+import { TabSpecificField } from '../enums/entity.enum';
import { Dashboard } from '../generated/entity/data/dashboard';
import { ChartType } from '../pages/DashboardDetailsPage/DashboardDetailsPage.component';
import { getChartById } from '../rest/chartAPI';
@@ -51,139 +50,3 @@ export const fetchCharts = async (charts: Dashboard['charts']) => {
return chartsData;
};
-
-export const getDashboardDetailsPageDefaultLayout = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
-};
-
-export const getDashboardDataModelDetailsPageDefaultLayout = (
- tab: EntityTabs
-) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Database/Database.util.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/Database/Database.util.tsx
index c7edadfcc32f..f298ff562506 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/Database/Database.util.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/Database/Database.util.tsx
@@ -22,12 +22,7 @@ import {
getEntityDetailsPath,
NO_DATA_PLACEHOLDER,
} from '../../constants/constants';
-import { DetailPageWidgetKeys } from '../../enums/CustomizeDetailPage.enum';
-import {
- EntityTabs,
- EntityType,
- TabSpecificField,
-} from '../../enums/entity.enum';
+import { EntityType, TabSpecificField } from '../../enums/entity.enum';
import { DatabaseSchema } from '../../generated/entity/data/databaseSchema';
import { EntityReference } from '../../generated/entity/type';
import { UsageDetails } from '../../generated/type/entityUsage';
@@ -120,70 +115,3 @@ export const schemaTableColumns: ColumnsType = [
getUsagePercentile(text?.weeklyStats?.percentileRank ?? 0),
},
];
-
-export const getDatabaseDetailsPageDefaultLayout = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaClassBase.ts
index 5223f5697bf2..d85dae100fb9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaClassBase.ts
@@ -14,7 +14,6 @@ import { EntityTags } from 'Models';
import { PagingHandlerParams } from '../components/common/NextPrevious/NextPrevious.interface';
import { TabProps } from '../components/common/TabsLabel/TabsLabel.interface';
import { OperationPermission } from '../context/PermissionProvider/PermissionProvider.interface';
-import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
import { EntityTabs } from '../enums/entity.enum';
import { DatabaseSchema } from '../generated/entity/data/databaseSchema';
import { Table } from '../generated/entity/data/table';
@@ -65,81 +64,6 @@ class DatabaseSchemaClassBase {
): TabProps[] {
return getDataBaseSchemaPageBaseTabs(databaseSchemaTabData);
}
-
- public getDatabaseSchemaPageTabsIds(): EntityTabs[] {
- return [
- EntityTabs.SCHEMA,
- EntityTabs.ACTIVITY_FEED,
- EntityTabs.CUSTOM_PROPERTIES,
- ];
- }
-
- public getDatabaseSchemaPageDefaultLayout = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
- };
}
const databaseSchemaClassBase = new DatabaseSchemaClassBase();
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx
index 24a7a6f7eced..e67551d66224 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx
@@ -46,6 +46,8 @@ import { EntityType, TabSpecificField } from '../enums/entity.enum';
import { EntityChangeOperations } from '../enums/VersionPage.enum';
import { Column as ContainerColumn } from '../generated/entity/data/container';
import { Column as DataModelColumn } from '../generated/entity/data/dashboardDataModel';
+import { Glossary } from '../generated/entity/data/glossary';
+import { GlossaryTerm } from '../generated/entity/data/glossaryTerm';
import { Column as TableColumn } from '../generated/entity/data/table';
import { Field } from '../generated/entity/data/topic';
import {
@@ -606,6 +608,9 @@ export const getCommonExtraInfoForVersionDetails = (
tier?: TagLabel,
domain?: EntityReference
) => {
+ // const { entityRef: ownerRef, entityDisplayName: ownerDisplayName } =
+ // getEntityReferenceDiffFromFieldName('owners', changeDescription, owners);
+
const { owners: ownerRef, ownerDisplayName } = getOwnerDiff(
owners ?? [],
changeDescription
@@ -1029,10 +1034,7 @@ export const getOwnerDiff = (
};
export const getOwnerVersionLabel = (
- entity: {
- [TabSpecificField.OWNERS]?: EntityReference[];
- changeDescription?: ChangeDescription;
- },
+ entity: Glossary | GlossaryTerm,
isVersionView: boolean,
ownerField = TabSpecificField.OWNERS, // Can be owners, experts, reviewers all are OwnerLabels
hasPermission = true
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsClassBase.ts
index be29d9324f39..6cc2bb5c013b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsClassBase.ts
@@ -17,6 +17,7 @@ import { ReactComponent as AdminIcon } from '../assets/svg/admin-colored.svg';
import { ReactComponent as ApplicationIcon } from '../assets/svg/application-colored.svg';
import { ReactComponent as BotIcon } from '../assets/svg/bot-colored.svg';
import { ReactComponent as AppearanceIcon } from '../assets/svg/custom-logo-colored.svg';
+import { ReactComponent as CustomDashboardLogoIcon } from '../assets/svg/customize-landing-page-colored.svg';
import { ReactComponent as DashboardIcon } from '../assets/svg/dashboard-colored.svg';
import { ReactComponent as DatabaseIcon } from '../assets/svg/database-colored.svg';
import { ReactComponent as EmailIcon } from '../assets/svg/email-colored.svg';
@@ -95,10 +96,6 @@ class GlobalSettingsClassBase {
name: t('label.application-plural'),
url: GlobalSettingsMenuCategory.APPLICATIONS,
},
- [GlobalSettingsMenuCategory.PERSONA]: {
- name: t('label.persona'),
- url: GlobalSettingsMenuCategory.PERSONA,
- },
};
protected updateSettingCategories(
@@ -273,6 +270,14 @@ class GlobalSettingsClassBase {
key: `${GlobalSettingsMenuCategory.MEMBERS}.${GlobalSettingOptions.ADMINS}`,
icon: AdminIcon,
},
+
+ {
+ label: t('label.persona-plural'),
+ description: t('message.page-sub-header-for-persona'),
+ isProtected: Boolean(isAdminUser),
+ key: `${GlobalSettingsMenuCategory.MEMBERS}.${GlobalSettingOptions.PERSONA}`,
+ icon: PersonasIcon,
+ },
],
},
{
@@ -310,6 +315,17 @@ class GlobalSettingsClassBase {
key: `${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.APPEARANCE}`,
icon: AppearanceIcon,
},
+ {
+ label: t('label.customize-entity', {
+ entity: t('label.landing-page'),
+ }),
+ description: t(
+ 'message.page-sub-header-for-customize-landing-page'
+ ),
+ isProtected: Boolean(isAdminUser),
+ key: `${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.CUSTOMIZE_LANDING_PAGE}`,
+ icon: CustomDashboardLogoIcon,
+ },
{
label: t('label.email'),
description: t('message.email-configuration-message'),
@@ -519,13 +535,6 @@ class GlobalSettingsClassBase {
key: GlobalSettingOptions.BOTS,
icon: BotIcon,
},
- {
- category: t('label.persona-plural'),
- description: t('message.page-sub-header-for-persona'),
- isProtected: Boolean(isAdminUser),
- key: GlobalSettingOptions.PERSONA,
- icon: PersonasIcon,
- },
];
}
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryTerm/GlossaryTermUtil.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryTerm/GlossaryTermUtil.tsx
deleted file mode 100644
index e88986669211..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryTerm/GlossaryTermUtil.tsx
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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 { TabsProps } from 'antd';
-import { isUndefined, uniqueId } from 'lodash';
-import React from 'react';
-import EmptyWidgetPlaceholder from '../../components/MyData/CustomizableComponents/EmptyWidgetPlaceholder/EmptyWidgetPlaceholder';
-import { SIZE } from '../../enums/common.enum';
-import { LandingPageWidgetKeys } from '../../enums/CustomizablePage.enum';
-import { GlossaryTermDetailPageWidgetKeys } from '../../enums/CustomizeDetailPage.enum';
-import { EntityTabs } from '../../enums/entity.enum';
-import { Document } from '../../generated/entity/docStore/document';
-import { Tab } from '../../generated/system/ui/uiCustomization';
-import { WidgetConfig } from '../../pages/CustomizablePage/CustomizablePage.interface';
-import customizeGlossaryTermPageClassBase from '../CustomiseGlossaryTermPage/CustomizeGlossaryTermPage';
-import { moveEmptyWidgetToTheEnd } from '../CustomizableLandingPageUtils';
-import customizeMyDataPageClassBase from '../CustomizeMyDataPageClassBase';
-import { getEntityName } from '../EntityUtils';
-
-export const getWidgetFromKey = ({
- widgetConfig,
- handleOpenAddWidgetModal,
- handlePlaceholderWidgetKey,
- handleRemoveWidget,
- isEditView,
- iconHeight,
- iconWidth,
-}: {
- widgetConfig: WidgetConfig;
- handleOpenAddWidgetModal?: () => void;
- handlePlaceholderWidgetKey?: (key: string) => void;
- handleRemoveWidget?: (key: string) => void;
- iconHeight?: SIZE;
- iconWidth?: SIZE;
- isEditView?: boolean;
-}) => {
- if (
- widgetConfig.i.endsWith('.EmptyWidgetPlaceholder') &&
- !isUndefined(handleOpenAddWidgetModal) &&
- !isUndefined(handlePlaceholderWidgetKey) &&
- !isUndefined(handleRemoveWidget)
- ) {
- return (
-
- );
- }
-
- const widgetKey = customizeGlossaryTermPageClassBase.getKeyFromWidgetName(
- widgetConfig.i
- );
-
- const Widget = customizeGlossaryTermPageClassBase.getWidgetsFromKey<
- typeof widgetKey
- >(widgetConfig.i as GlossaryTermDetailPageWidgetKeys);
-
- return (
-
- );
-};
-
-const getNewWidgetPlacement = (
- currentLayout: WidgetConfig[],
- widgetWidth: number
-) => {
- const lowestWidgetLayout = currentLayout.reduce(
- (acc, widget) => {
- if (
- widget.y >= acc.y &&
- widget.i !== LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER
- ) {
- if (widget.y === acc.y && widget.x < acc.x) {
- return acc;
- }
-
- return widget;
- }
-
- return acc;
- },
- { y: 0, x: 0, w: 0 }
- );
-
- // Check if there's enough space to place the new widget on the same row
- if (
- customizeMyDataPageClassBase.landingPageMaxGridSize -
- (lowestWidgetLayout.x + lowestWidgetLayout.w) >=
- widgetWidth
- ) {
- return {
- x: lowestWidgetLayout.x + lowestWidgetLayout.w,
- y: lowestWidgetLayout.y,
- };
- }
-
- // Otherwise, move to the next row
- return {
- x: 0,
- y: lowestWidgetLayout.y + 1,
- };
-};
-
-export const getAddWidgetHandler =
- (
- newWidgetData: Document,
- placeholderWidgetKey: string,
- widgetWidth: number,
- maxGridSize: number
- ) =>
- (currentLayout: Array) => {
- const widgetFQN = uniqueId(`${newWidgetData.fullyQualifiedName}-`);
- const widgetHeight = customizeMyDataPageClassBase.getWidgetHeight(
- newWidgetData.name
- );
-
- // The widget with key "ExtraWidget.EmptyWidgetPlaceholder" will always remain in the bottom
- // and is not meant to be replaced hence
- // if placeholderWidgetKey is "ExtraWidget.EmptyWidgetPlaceholder"
- // append the new widget in the array
- // else replace the new widget with other placeholder widgets
- if (
- placeholderWidgetKey === LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER
- ) {
- return [
- ...moveEmptyWidgetToTheEnd(currentLayout),
- {
- w: widgetWidth,
- h: widgetHeight,
- i: widgetFQN,
- static: false,
- ...getNewWidgetPlacement(currentLayout, widgetWidth),
- },
- ];
- } else {
- return currentLayout.map((widget: WidgetConfig) => {
- const widgetX =
- widget.x + widgetWidth <= maxGridSize
- ? widget.x
- : maxGridSize - widgetWidth;
-
- return widget.i === placeholderWidgetKey
- ? {
- ...widget,
- i: widgetFQN,
- h: widgetHeight,
- w: widgetWidth,
- x: widgetX,
- }
- : widget;
- });
- }
- };
-
-export const getGlossaryTermDetailTabs = (
- defaultTabs: TabsProps['items'],
- customizedTabs?: Tab[],
- defaultTabId: EntityTabs = EntityTabs.OVERVIEW
-) => {
- if (!customizedTabs) {
- return defaultTabs;
- }
- const overviewTab = defaultTabs?.find((t) => t.key === defaultTabId);
-
- const newTabs =
- customizedTabs?.map((t) => {
- const tabItemDetails = defaultTabs?.find((i) => i.key === t.id);
-
- return (
- tabItemDetails ?? {
- label: getEntityName(t),
- key: t.id,
- children: overviewTab?.children,
- }
- );
- }) ?? defaultTabs;
-
- return newTabs;
-};
-
-export const getTabLabelMap = (tabs?: Tab[]): Record => {
- const labelMap = {} as Record;
-
- return (
- tabs?.reduce((acc: Record, item) => {
- if (item.id && item.displayName) {
- const tab = item.id as EntityTabs;
- acc[tab] = item.displayName;
- }
-
- return acc;
- }, labelMap) ?? labelMap
- );
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx
index c2bfda868435..b95492e8bf39 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx
@@ -11,32 +11,18 @@
* limitations under the License.
*/
-import Icon from '@ant-design/icons';
-import { Tag, Tooltip, Typography } from 'antd';
+import { Typography } from 'antd';
import { DefaultOptionType } from 'antd/lib/select';
-import classNames from 'classnames';
import { isEmpty, isUndefined } from 'lodash';
import React from 'react';
-import { ReactComponent as ExternalLinkIcon } from '../assets/svg/external-links.svg';
import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface';
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
import { ModifiedGlossary } from '../components/Glossary/useGlossary.store';
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
-import {
- ICON_DIMENSION,
- SUCCESS_COLOR,
- TEXT_BODY_COLOR,
- TEXT_GREY_MUTED,
-} from '../constants/constants';
import { EntityType } from '../enums/entity.enum';
import { Glossary } from '../generated/entity/data/glossary';
-import {
- GlossaryTerm,
- Status,
- TermReference,
-} from '../generated/entity/data/glossaryTerm';
+import { GlossaryTerm, Status } from '../generated/entity/data/glossaryTerm';
import { getEntityName } from './EntityUtils';
-import { VersionStatus } from './EntityVersionUtils.interface';
import Fqn from './Fqn';
import { getGlossaryPath } from './RouterUtils';
@@ -348,49 +334,3 @@ export const filterTreeNodeOptions = (
return filterNodes(options as ModifiedGlossaryTerm[]);
};
-
-export const renderReferenceElement = (
- ref: TermReference,
- versionStatus?: VersionStatus
-) => {
- let iconColor: string;
- let textClassName: string;
- if (versionStatus?.added) {
- iconColor = SUCCESS_COLOR;
- textClassName = 'text-success';
- } else if (versionStatus?.removed) {
- iconColor = TEXT_GREY_MUTED;
- textClassName = 'text-grey-muted';
- } else {
- iconColor = TEXT_BODY_COLOR;
- textClassName = 'text-body';
- }
-
- return (
-
-
-
-
-
- {ref.name}
-
-
-
-
- );
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/LeftSidebarClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/LeftSidebarClassBase.ts
index 77a6b0f26934..e610d190cc80 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/LeftSidebarClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/LeftSidebarClassBase.ts
@@ -14,21 +14,11 @@ import { LeftSidebarItem } from '../components/MyData/LeftSidebar/LeftSidebar.in
import { SIDEBAR_LIST } from '../constants/LeftSidebar.constants';
class LeftSidebarClassBase {
- sidebarItems: Array;
-
- constructor() {
- this.sidebarItems = SIDEBAR_LIST;
- }
-
/**
* getSidebarItems
*/
public getSidebarItems(): Array {
- return this.sidebarItems;
- }
-
- public setSidebarItems(items: Array): void {
- this.sidebarItems = items;
+ return SIDEBAR_LIST;
}
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.test.ts
deleted file mode 100644
index 7ada07359838..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.test.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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 { PageType } from '../../generated/system/ui/uiCustomization';
-import {
- getCustomizePageCategories,
- getCustomizePageOptions,
-} from './PersonaUtils';
-
-describe('PersonaUtils', () => {
- describe('getCustomizePageCategories', () => {
- it('should return the correct categories', () => {
- const categories = getCustomizePageCategories();
-
- expect(categories).toEqual([
- {
- key: 'navigation',
- label: 'label.navigation',
- description: 'Navigation',
- icon: 'svg-mock',
- },
- {
- key: PageType.LandingPage,
- label: 'label.homepage',
- description: 'Homepage',
- icon: 'svg-mock',
- },
- {
- key: 'governance',
- label: 'label.governance',
- description: 'Governance',
- icon: 'svg-mock',
- },
- {
- key: 'data-assets',
- label: 'label.data-asset-plural',
- description: 'Data assets',
- icon: 'svg-mock',
- },
- ]);
- });
- });
-
- describe('getCustomizePageOptions', () => {
- it('should return the correct options for governance category', () => {
- const options = getCustomizePageOptions('governance');
-
- expect(options).toEqual([
- {
- key: PageType.Domain,
- label: 'Domain',
- description: PageType.Domain,
- icon: 'svg-mock',
- },
- {
- key: PageType.Glossary,
- label: 'Glossary',
- description: PageType.Glossary,
- icon: 'svg-mock',
- },
- {
- key: PageType.GlossaryTerm,
- label: 'Glossary Term',
- description: PageType.GlossaryTerm,
- icon: 'svg-mock',
- },
- ]);
- });
-
- it('should return the correct options for data-assets category', () => {
- const options = getCustomizePageOptions('data-assets');
-
- expect(options).toEqual(
- expect.arrayContaining([
- {
- key: PageType.Dashboard,
- label: 'Dashboard',
- description: PageType.Dashboard,
- icon: 'svg-mock',
- },
- {
- key: PageType.Database,
- label: 'Database',
- description: PageType.Database,
- icon: 'svg-mock',
- },
- {
- key: PageType.Pipeline,
- label: 'Pipeline',
- description: PageType.Pipeline,
- icon: 'svg-mock',
- },
- {
- key: PageType.Table,
- label: 'Table',
- description: PageType.Table,
- icon: 'svg-mock',
- },
- {
- key: PageType.Container,
- label: 'Container',
- description: PageType.Container,
- icon: 'svg-mock',
- },
- ])
- );
- });
-
- it('should return an empty array for an unknown category', () => {
- const options = getCustomizePageOptions('unknown-category');
-
- expect(options).toEqual([]);
- });
- });
-});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.ts
deleted file mode 100644
index c795e521d1ad..000000000000
--- a/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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 { camelCase, map, startCase } from 'lodash';
-import { ReactComponent as DashboardIcon } from '../../assets/svg/dashboard-colored.svg';
-import { ReactComponent as DataAssetsIcon } from '../../assets/svg/data-assets.svg';
-import { ReactComponent as DatabaseIcon } from '../../assets/svg/database-colored.svg';
-import { ReactComponent as GlossaryIcon } from '../../assets/svg/glossary-colored.svg';
-import { ReactComponent as GovernIcon } from '../../assets/svg/governance.svg';
-import { ReactComponent as HomepageIcon } from '../../assets/svg/homepage.svg';
-import { ReactComponent as DashboardDataModelIcon } from '../../assets/svg/ic-dashboard-data-model-colored.svg';
-import { ReactComponent as SchemaIcon } from '../../assets/svg/ic-database-schema-colored.svg';
-import { ReactComponent as MessagingIcon } from '../../assets/svg/messaging-colored.svg';
-import { ReactComponent as NavigationIcon } from '../../assets/svg/navigation.svg';
-import { ReactComponent as PipelineIcon } from '../../assets/svg/pipeline-colored.svg';
-import { ReactComponent as SearchIcon } from '../../assets/svg/search-colored.svg';
-import { ReactComponent as StorageIcon } from '../../assets/svg/storage-colored.svg';
-import { ReactComponent as StoredProcedureIcon } from '../../assets/svg/stored-procedure-colored.svg';
-import { ReactComponent as TableIcon } from '../../assets/svg/table-colored.svg';
-
-import { EntityType } from '../../enums/entity.enum';
-import { PageType } from '../../generated/system/ui/uiCustomization';
-import { SettingMenuItem } from '../GlobalSettingsUtils';
-import i18n from '../i18next/LocalUtil';
-
-const ENTITY_ICONS: Record = {
- [EntityType.TABLE]: TableIcon,
- [EntityType.CONTAINER]: StorageIcon,
- [EntityType.DASHBOARD]: DashboardIcon,
- [EntityType.DASHBOARD_DATA_MODEL]: DashboardDataModelIcon,
- [EntityType.DATABASE]: DatabaseIcon,
- [EntityType.DATABASE_SCHEMA]: SchemaIcon,
- [EntityType.DOMAIN]: SchemaIcon,
- [EntityType.GLOSSARY]: GlossaryIcon,
- [EntityType.GLOSSARY_TERM]: GlossaryIcon,
- [EntityType.PIPELINE]: PipelineIcon,
- [EntityType.SEARCH_INDEX]: SearchIcon,
- [EntityType.STORED_PROCEDURE]: StoredProcedureIcon,
- [EntityType.TOPIC]: MessagingIcon,
- [EntityType.GOVERN]: GovernIcon,
- ['dataAssets']: DataAssetsIcon,
- ['homepage']: HomepageIcon,
- ['navigation']: NavigationIcon,
- ['governance']: GovernIcon,
- [PageType.LandingPage]: MessagingIcon,
-};
-
-export const getCustomizePageCategories = (): SettingMenuItem[] => {
- return [
- {
- key: 'navigation',
- label: i18n.t('label.navigation'),
- description: 'Navigation',
- icon: ENTITY_ICONS[camelCase('Navigation')],
- },
- {
- key: PageType.LandingPage,
- label: i18n.t('label.homepage'),
- description: 'Homepage',
- icon: ENTITY_ICONS[camelCase('Homepage')],
- },
- {
- key: 'governance',
- label: i18n.t('label.governance'),
- description: 'Governance',
- icon: ENTITY_ICONS[camelCase('GOVERN')],
- },
- {
- key: 'data-assets',
- label: i18n.t('label.data-asset-plural'),
- description: 'Data assets',
- icon: ENTITY_ICONS[camelCase('data-assets')],
- },
- ];
-};
-
-const generateSettingItems = (pageType: PageType): SettingMenuItem => ({
- key: pageType,
- label: startCase(pageType),
- description: pageType,
- icon: ENTITY_ICONS[camelCase(pageType)],
-});
-
-export const getCustomizePageOptions = (
- category: string
-): SettingMenuItem[] => {
- const list = map(PageType);
-
- switch (category) {
- case 'governance':
- return list.reduce((acc, item) => {
- if (
- [PageType.Glossary, PageType.GlossaryTerm, PageType.Domain].includes(
- item
- )
- ) {
- acc.push(generateSettingItems(item));
- }
-
- return acc;
- }, [] as SettingMenuItem[]);
- case 'data-assets':
- return list.reduce((acc, item) => {
- if (
- ![
- PageType.Glossary,
- PageType.GlossaryTerm,
- PageType.Domain,
- PageType.LandingPage,
- ].includes(item)
- ) {
- acc.push(generateSettingItems(item));
- }
-
- return acc;
- }, [] as SettingMenuItem[]);
- default:
- return [];
- }
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.ts
index 93006ab3c062..e48ee97f58c4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.ts
@@ -15,8 +15,7 @@ import { isUndefined } from 'lodash';
import { ReactComponent as IconFailBadge } from '../assets/svg/fail-badge.svg';
import { ReactComponent as IconSkippedBadge } from '../assets/svg/skipped-badge.svg';
import { ReactComponent as IconSuccessBadge } from '../assets/svg/success-badge.svg';
-import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
-import { EntityTabs, TabSpecificField } from '../enums/entity.enum';
+import { TabSpecificField } from '../enums/entity.enum';
import {
Pipeline,
StatusType,
@@ -63,70 +62,3 @@ export const getFormattedPipelineDetails = (
return pipelineDetails;
}
};
-
-export const getPipelineDetailsPageDefaultLayout = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
-};
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 4226f62c3f27..0de983d84c2f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts
@@ -581,10 +581,11 @@ export const getClassificationVersionsPath = (
};
export const getPersonaDetailsPath = (fqn: string) => {
- let path = ROUTES.SETTINGS_WITH_CATEGORY_FQN;
+ let path = ROUTES.SETTINGS_WITH_TAB_FQN;
path = path
- .replace(PLACEHOLDER_SETTING_CATEGORY, GlobalSettingOptions.PERSONA)
+ .replace(PLACEHOLDER_SETTING_CATEGORY, GlobalSettingsMenuCategory.MEMBERS)
+ .replace(PLACEHOLDER_ROUTE_TAB, GlobalSettingOptions.PERSONA)
.replace(PLACEHOLDER_ROUTE_FQN, getEncodedFqn(fqn));
return path;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx
index fd85984bd5d8..40c1335146da 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx
@@ -12,8 +12,7 @@
*/
import { uniqueId } from 'lodash';
-import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
-import { EntityTabs, TabSpecificField } from '../enums/entity.enum';
+import { TabSpecificField } from '../enums/entity.enum';
import { SearchIndexField } from '../generated/entity/data/searchIndex';
import { sortTagsCaseInsensitive } from './CommonUtils';
@@ -41,70 +40,3 @@ export const makeData = (
children: column.children ? makeData(column.children) : undefined,
}));
};
-
-export const getSearchIndexDetailsPageDefaultLayout = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx
index b4deb38f412c..d1d1e05e9e74 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx
@@ -10,75 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
-import { EntityTabs, TabSpecificField } from '../enums/entity.enum';
+import { TabSpecificField } from '../enums/entity.enum';
// eslint-disable-next-line max-len
export const STORED_PROCEDURE_DEFAULT_FIELDS = `${TabSpecificField.OWNERS}, ${TabSpecificField.FOLLOWERS},${TabSpecificField.TAGS}, ${TabSpecificField.DOMAIN},${TabSpecificField.DATA_PRODUCTS}, ${TabSpecificField.VOTES},${TabSpecificField.EXTENSION}`;
-
-export const getStoredProceduresPageDefaultLayout = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
-};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TableClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TableClassBase.ts
index 91e354e073ab..64e9b0e9d2ff 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TableClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TableClassBase.ts
@@ -12,15 +12,8 @@
*/
import { TabProps } from '../components/common/TabsLabel/TabsLabel.interface';
import { OperationPermission } from '../context/PermissionProvider/PermissionProvider.interface';
-import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
import { EntityTabs } from '../enums/entity.enum';
-import {
- Constraint,
- DatabaseServiceType,
- DataType,
- Table,
- TableType,
-} from '../generated/entity/data/table';
+import { Table } from '../generated/entity/data/table';
import { TestSummary } from '../generated/tests/testCase';
import { FeedCounts } from '../interface/feed.interface';
import { getTableDetailPageBaseTabs } from './TableUtils';
@@ -55,210 +48,9 @@ class TableClassBase {
return getTableDetailPageBaseTabs(tableDetailsPageProps);
}
- public getTableDetailPageTabsIds(): EntityTabs[] {
- return [
- EntityTabs.SCHEMA,
- EntityTabs.ACTIVITY_FEED,
- EntityTabs.SAMPLE_DATA,
- EntityTabs.TABLE_QUERIES,
- EntityTabs.PROFILER,
- EntityTabs.INCIDENTS,
- EntityTabs.LINEAGE,
- EntityTabs.VIEW_DEFINITION,
- EntityTabs.CUSTOM_PROPERTIES,
- ];
- }
-
- public getDefaultLayout(tab: EntityTabs) {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 11,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 2,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 2,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 2,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 4,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
- }
-
public getAlertEnableStatus() {
return false;
}
-
- public getDummyData(): Table {
- return {
- id: 'ab4f893b-c303-43d9-9375-3e620a670b02',
- name: 'raw_product_catalog',
- fullyQualifiedName:
- 'sample_data.ecommerce_db.shopify.raw_product_catalog',
- description:
- 'This is a raw product catalog table contains the product listing, price, seller etc.. represented in our online DB. ',
- version: 0.2,
- updatedAt: 1688442727895,
- updatedBy: 'admin',
- tableType: TableType.Regular,
- dataProducts: [
- {
- id: 'c9b891b1-5d60-4171-9af0-7fd6d74f8f2b',
- type: 'dataProduct',
- name: 'Design Data product ',
- fullyQualifiedName: 'Design Data product ',
- description:
- "Here's the description for the Design Data product Name.",
- displayName: 'Design Data product Name',
- href: '#',
- },
- ],
- joins: {
- startDate: new Date(),
- dayCount: 30,
- columnJoins: [
- {
- columnName: 'address_id',
- joinedWith: [
- {
- fullyQualifiedName:
- 'sample_data.ecommerce_db.shopify.dim_address_clean.address_id',
- joinCount: 0,
- },
- {
- fullyQualifiedName:
- 'sample_data.ecommerce_db.shopify.dim_address.address_id',
- joinCount: 0,
- },
- ],
- },
- ],
- directTableJoins: [
- {
- fullyQualifiedName: 'sample_data.ecommerce_db.shopify.dim_address',
- joinCount: 0,
- },
- ],
- },
- columns: [
- {
- name: 'shop_id',
- displayName: 'Shop Id Customer',
- dataType: DataType.Number,
- dataTypeDisplay: 'numeric',
- description:
- 'Unique identifier for the store. This column is the primary key for this table.',
- fullyQualifiedName:
- 'sample_data.ecommerce_db.shopify."dim.shop".shop_id',
- tags: [],
- constraint: Constraint.PrimaryKey,
- ordinalPosition: 1,
- },
- ],
- owners: [
- {
- id: '38be030f-f817-4712-bc3b-ff7b9b9b805e',
- type: 'user',
- name: 'aaron_johnson0',
- fullyQualifiedName: 'aaron_johnson0',
- displayName: 'Aaron Johnson',
- deleted: false,
- },
- ],
- databaseSchema: {
- id: '3f0d9c39-0926-4028-8070-65b0c03556cb',
- type: 'databaseSchema',
- name: 'shopify',
- fullyQualifiedName: 'sample_data.ecommerce_db.shopify',
- description:
- 'This **mock** database contains schema related to shopify sales and orders with related dimension tables.',
- deleted: false,
- },
- database: {
- id: 'f085e133-e184-47c8-ada5-d7e005d3153b',
- type: 'database',
- name: 'ecommerce_db',
- fullyQualifiedName: 'sample_data.ecommerce_db',
- description:
- 'This **mock** database contains schemas related to shopify sales and orders with related dimension tables.',
- deleted: false,
- },
- service: {
- id: 'e61069a9-29e3-49fa-a7f4-f5227ae50b72',
- type: 'databaseService',
- name: 'sample_data',
- fullyQualifiedName: 'sample_data',
- deleted: false,
- },
- serviceType: DatabaseServiceType.BigQuery,
- tags: [],
- followers: [],
- changeDescription: {
- fieldsAdded: [
- {
- name: 'owner',
- newValue:
- '{"id":"38be030f-f817-4712-bc3b-ff7b9b9b805e","type":"user","name":"aaron_johnson0","fullyQualifiedName":"aaron_johnson0","displayName":"Aaron Johnson","deleted":false}',
- },
- ],
- fieldsUpdated: [],
- fieldsDeleted: [],
- previousVersion: 0.1,
- },
- deleted: false,
- };
- }
}
const tableClassBase = new TableClassBase();
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TagClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TagClassBase.ts
index decb520cc99d..7a13f29d299d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TagClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TagClassBase.ts
@@ -18,7 +18,7 @@ import { escapeESReservedCharacters, getEncodedFqn } from './StringsUtils';
class TagClassBase {
public async getTags(searchText: string, page: number) {
- // this is to escape and encode any chars which is known by ES search internally
+ // this is to esacpe and encode any chars which is known by ES search internally
const encodedValue = getEncodedFqn(escapeESReservedCharacters(searchText));
const res = await searchQuery({
query: `*${encodedValue}*`,
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TopicDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TopicDetailsUtils.ts
index 804965019ee6..20959667e6e1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TopicDetailsUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TopicDetailsUtils.ts
@@ -11,72 +11,18 @@
* limitations under the License.
*/
-import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum';
-import { EntityTabs } from '../enums/entity.enum';
+import { TopicConfigObjectInterface } from '../components/Topic/TopicDetails/TopicDetails.interface';
+import { Topic } from '../generated/entity/data/topic';
-export const getTopicDetailsPageDefaultLayout = (tab: EntityTabs) => {
- switch (tab) {
- case EntityTabs.SCHEMA:
- return [
- {
- h: 2,
- i: DetailPageWidgetKeys.DESCRIPTION,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 8,
- i: DetailPageWidgetKeys.TABLE_SCHEMA,
- w: 6,
- x: 0,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES,
- w: 2,
- x: 6,
- y: 0,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.DATA_PRODUCTS,
- w: 2,
- x: 6,
- y: 1,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.TAGS,
- w: 2,
- x: 6,
- y: 2,
- static: false,
- },
- {
- h: 1,
- i: DetailPageWidgetKeys.GLOSSARY_TERMS,
- w: 2,
- x: 6,
- y: 3,
- static: false,
- },
- {
- h: 3,
- i: DetailPageWidgetKeys.CUSTOM_PROPERTIES,
- w: 2,
- x: 6,
- y: 4,
- static: false,
- },
- ];
-
- default:
- return [];
- }
+export const getConfigObject = (
+ topicDetails: Topic
+): TopicConfigObjectInterface => {
+ return {
+ Partitions: topicDetails.partitions,
+ 'Replication Factor': topicDetails.replicationFactor,
+ 'Retention Size': topicDetails.retentionSize,
+ 'CleanUp Policies': topicDetails.cleanupPolicies,
+ 'Max Message Size': topicDetails.maximumMessageSize,
+ 'Schema Type': topicDetails.messageSchema?.schemaType,
+ };
};
From d41dc0b776e218ddce4e7314db98118ac3a1489e Mon Sep 17 00:00:00 2001
From: Mohit Yadav <105265192+mohityadav766@users.noreply.github.com>
Date: Tue, 10 Dec 2024 21:10:58 +0530
Subject: [PATCH 003/294] Fix ES Tag Index failing (#18991)
* Fix ES Tag Index failing
* add tags loading test
---------
Co-authored-by: karanh37
(cherry picked from commit 08cdc69f30316261ed50c4f4ae0dc93a67b1203a)
---
.../resources/elasticsearch/indexMapping.json | 4 +--
.../playwright/e2e/Pages/ExploreTree.spec.ts | 25 +++++++++++++++++++
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/openmetadata-service/src/main/resources/elasticsearch/indexMapping.json b/openmetadata-service/src/main/resources/elasticsearch/indexMapping.json
index 20a17107791e..a5b348e7452e 100644
--- a/openmetadata-service/src/main/resources/elasticsearch/indexMapping.json
+++ b/openmetadata-service/src/main/resources/elasticsearch/indexMapping.json
@@ -150,8 +150,8 @@
"indexName": "tag_search_index",
"indexMappingFile": "/elasticsearch/%s/tag_index_mapping.json",
"alias": "tag",
- "parentAliases": ["classification"],
- "childAliases": ["all", "dataAsset"]
+ "parentAliases": ["classification", "all", "dataAsset"],
+ "childAliases": []
},
"classification": {
"indexName": "classification_search_index",
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts
index 9db6cfb2d1c9..346131401181 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts
@@ -190,3 +190,28 @@ test.describe('Explore Tree scenarios ', () => {
await afterAction();
});
});
+
+test.describe('Explore page', () => {
+ test('Check the listing of tags', async ({ page }) => {
+ await page
+ .locator('div')
+ .filter({ hasText: /^Governance$/ })
+ .locator('svg')
+ .first()
+ .click();
+
+ await expect(page.getByRole('tree')).toContainText('Glossaries');
+ await expect(page.getByRole('tree')).toContainText('Tags');
+
+ const res = page.waitForResponse(
+ '/api/v1/search/query?q=&index=dataAsset*'
+ );
+ // click on tags
+ await page.getByTestId('explore-tree-title-Tags').click();
+
+ const response = await res;
+ const jsonResponse = await response.json();
+
+ expect(jsonResponse.hits.hits.length).toBeGreaterThan(0);
+ });
+});
From f22442054a9c0692a69272e882aabda8760f4f09 Mon Sep 17 00:00:00 2001
From: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 10 Dec 2024 16:13:05 +0000
Subject: [PATCH 004/294] chore(release): Prepare Branch for `1.6.1`
---
common/pom.xml | 2 +-
.../docker-compose-ingestion/docker-compose-ingestion.yml | 2 +-
.../docker-compose-openmetadata.yml | 4 ++--
docker/docker-compose-quickstart/Dockerfile | 4 ++--
.../docker-compose-quickstart/docker-compose-postgres.yml | 8 ++++----
docker/docker-compose-quickstart/docker-compose.yml | 8 ++++----
ingestion/Dockerfile | 2 +-
ingestion/operators/docker/Dockerfile | 2 +-
ingestion/pyproject.toml | 2 +-
openmetadata-airflow-apis/pyproject.toml | 2 +-
openmetadata-clients/openmetadata-java-client/pom.xml | 2 +-
openmetadata-clients/pom.xml | 2 +-
openmetadata-dist/pom.xml | 2 +-
openmetadata-service/pom.xml | 2 +-
openmetadata-shaded-deps/elasticsearch-dep/pom.xml | 2 +-
openmetadata-shaded-deps/opensearch-dep/pom.xml | 2 +-
openmetadata-shaded-deps/pom.xml | 2 +-
openmetadata-spec/pom.xml | 2 +-
openmetadata-ui/pom.xml | 2 +-
pom.xml | 2 +-
20 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/common/pom.xml b/common/pom.xml
index 8419d12de5b8..62c059357e0a 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -18,7 +18,7 @@
platform
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
diff --git a/docker/docker-compose-ingestion/docker-compose-ingestion.yml b/docker/docker-compose-ingestion/docker-compose-ingestion.yml
index 85d09867d56c..a717b5c3ebfa 100644
--- a/docker/docker-compose-ingestion/docker-compose-ingestion.yml
+++ b/docker/docker-compose-ingestion/docker-compose-ingestion.yml
@@ -18,7 +18,7 @@ volumes:
services:
ingestion:
container_name: openmetadata_ingestion
- image: docker.getcollate.io/openmetadata/ingestion:1.6.0
+ image: docker.getcollate.io/openmetadata/ingestion:1.6.1
environment:
AIRFLOW__API__AUTH_BACKENDS: "airflow.api.auth.backend.basic_auth,airflow.api.auth.backend.session"
AIRFLOW__CORE__EXECUTOR: LocalExecutor
diff --git a/docker/docker-compose-openmetadata/docker-compose-openmetadata.yml b/docker/docker-compose-openmetadata/docker-compose-openmetadata.yml
index 3daae332b5f8..82e48f8fa7de 100644
--- a/docker/docker-compose-openmetadata/docker-compose-openmetadata.yml
+++ b/docker/docker-compose-openmetadata/docker-compose-openmetadata.yml
@@ -14,7 +14,7 @@ services:
execute-migrate-all:
container_name: execute_migrate_all
command: "./bootstrap/openmetadata-ops.sh migrate"
- image: docker.getcollate.io/openmetadata/server:1.6.0
+ image: docker.getcollate.io/openmetadata/server:1.6.1
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
SERVER_PORT: ${SERVER_PORT:-8585}
@@ -227,7 +227,7 @@ services:
openmetadata-server:
container_name: openmetadata_server
restart: always
- image: docker.getcollate.io/openmetadata/server:1.6.0
+ image: docker.getcollate.io/openmetadata/server:1.6.1
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
SERVER_PORT: ${SERVER_PORT:-8585}
diff --git a/docker/docker-compose-quickstart/Dockerfile b/docker/docker-compose-quickstart/Dockerfile
index 9f63b12bbf13..f27cf543bf47 100644
--- a/docker/docker-compose-quickstart/Dockerfile
+++ b/docker/docker-compose-quickstart/Dockerfile
@@ -11,7 +11,7 @@
# Build stage
FROM alpine:3 AS build
-ARG RI_VERSION="1.6.0"
+ARG RI_VERSION="1.6.1"
ENV RELEASE_URL="https://github.com/open-metadata/OpenMetadata/releases/download/${RI_VERSION}-release/openmetadata-${RI_VERSION}.tar.gz"
RUN mkdir -p /opt/openmetadata && \
@@ -21,7 +21,7 @@ RUN mkdir -p /opt/openmetadata && \
# Final stage
FROM alpine:3
-ARG RI_VERSION="1.6.0"
+ARG RI_VERSION="1.6.1"
ARG BUILD_DATE
ARG COMMIT_ID
LABEL maintainer="OpenMetadata"
diff --git a/docker/docker-compose-quickstart/docker-compose-postgres.yml b/docker/docker-compose-quickstart/docker-compose-postgres.yml
index 8faebd441ec9..aeeccef1e2d3 100644
--- a/docker/docker-compose-quickstart/docker-compose-postgres.yml
+++ b/docker/docker-compose-quickstart/docker-compose-postgres.yml
@@ -18,7 +18,7 @@ volumes:
services:
postgresql:
container_name: openmetadata_postgresql
- image: docker.getcollate.io/openmetadata/postgresql:1.6.0
+ image: docker.getcollate.io/openmetadata/postgresql:1.6.1
restart: always
command: "--work_mem=10MB"
environment:
@@ -61,7 +61,7 @@ services:
execute-migrate-all:
container_name: execute_migrate_all
- image: docker.getcollate.io/openmetadata/server:1.6.0
+ image: docker.getcollate.io/openmetadata/server:1.6.1
command: "./bootstrap/openmetadata-ops.sh migrate"
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
@@ -275,7 +275,7 @@ services:
openmetadata-server:
container_name: openmetadata_server
restart: always
- image: docker.getcollate.io/openmetadata/server:1.6.0
+ image: docker.getcollate.io/openmetadata/server:1.6.1
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
SERVER_PORT: ${SERVER_PORT:-8585}
@@ -483,7 +483,7 @@ services:
ingestion:
container_name: openmetadata_ingestion
- image: docker.getcollate.io/openmetadata/ingestion:1.6.0
+ image: docker.getcollate.io/openmetadata/ingestion:1.6.1
depends_on:
elasticsearch:
condition: service_started
diff --git a/docker/docker-compose-quickstart/docker-compose.yml b/docker/docker-compose-quickstart/docker-compose.yml
index 42c346921e7b..92b7db1f9f25 100644
--- a/docker/docker-compose-quickstart/docker-compose.yml
+++ b/docker/docker-compose-quickstart/docker-compose.yml
@@ -18,7 +18,7 @@ volumes:
services:
mysql:
container_name: openmetadata_mysql
- image: docker.getcollate.io/openmetadata/db:1.6.0
+ image: docker.getcollate.io/openmetadata/db:1.6.1
command: "--sort_buffer_size=10M"
restart: always
environment:
@@ -59,7 +59,7 @@ services:
execute-migrate-all:
container_name: execute_migrate_all
- image: docker.getcollate.io/openmetadata/server:1.6.0
+ image: docker.getcollate.io/openmetadata/server:1.6.1
command: "./bootstrap/openmetadata-ops.sh migrate"
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
@@ -273,7 +273,7 @@ services:
openmetadata-server:
container_name: openmetadata_server
restart: always
- image: docker.getcollate.io/openmetadata/server:1.6.0
+ image: docker.getcollate.io/openmetadata/server:1.6.1
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
SERVER_PORT: ${SERVER_PORT:-8585}
@@ -482,7 +482,7 @@ services:
ingestion:
container_name: openmetadata_ingestion
- image: docker.getcollate.io/openmetadata/ingestion:1.6.0
+ image: docker.getcollate.io/openmetadata/ingestion:1.6.1
depends_on:
elasticsearch:
condition: service_started
diff --git a/ingestion/Dockerfile b/ingestion/Dockerfile
index 7a726138fefc..e212415c0f1c 100644
--- a/ingestion/Dockerfile
+++ b/ingestion/Dockerfile
@@ -76,7 +76,7 @@ ARG INGESTION_DEPENDENCY="all"
ENV PIP_NO_CACHE_DIR=1
# Make pip silent
ENV PIP_QUIET=1
-ARG RI_VERSION="1.6.0.0"
+ARG RI_VERSION="1.6.1.0"
RUN pip install --upgrade pip
RUN pip install "openmetadata-managed-apis~=${RI_VERSION}" --constraint "https://raw.githubusercontent.com/apache/airflow/constraints-2.9.1/constraints-3.10.txt"
RUN pip install "openmetadata-ingestion[${INGESTION_DEPENDENCY}]~=${RI_VERSION}"
diff --git a/ingestion/operators/docker/Dockerfile b/ingestion/operators/docker/Dockerfile
index 01826eb2c658..fec7b09ea066 100644
--- a/ingestion/operators/docker/Dockerfile
+++ b/ingestion/operators/docker/Dockerfile
@@ -81,7 +81,7 @@ ENV PIP_QUIET=1
RUN pip install --upgrade pip
ARG INGESTION_DEPENDENCY="all"
-ARG RI_VERSION="1.6.0.0"
+ARG RI_VERSION="1.6.1.0"
RUN pip install --upgrade pip
RUN pip install "openmetadata-ingestion[airflow]~=${RI_VERSION}"
RUN pip install "openmetadata-ingestion[${INGESTION_DEPENDENCY}]~=${RI_VERSION}"
diff --git a/ingestion/pyproject.toml b/ingestion/pyproject.toml
index 1bd5f937d27c..f63a5269c5b6 100644
--- a/ingestion/pyproject.toml
+++ b/ingestion/pyproject.toml
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
# since it helps us organize and isolate version management
[project]
name = "openmetadata-ingestion"
-version = "1.6.0.0"
+version = "1.6.1.0"
dynamic = ["readme", "dependencies", "optional-dependencies"]
authors = [
{ name = "OpenMetadata Committers" }
diff --git a/openmetadata-airflow-apis/pyproject.toml b/openmetadata-airflow-apis/pyproject.toml
index 6006f4c3bfa0..bcabf86bcf9a 100644
--- a/openmetadata-airflow-apis/pyproject.toml
+++ b/openmetadata-airflow-apis/pyproject.toml
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
# since it helps us organize and isolate version management
[project]
name = "openmetadata_managed_apis"
-version = "1.6.0.0"
+version = "1.6.1.0"
readme = "README.md"
authors = [
{name = "OpenMetadata Committers"}
diff --git a/openmetadata-clients/openmetadata-java-client/pom.xml b/openmetadata-clients/openmetadata-java-client/pom.xml
index c114c7d49c2b..2bc9816a523f 100644
--- a/openmetadata-clients/openmetadata-java-client/pom.xml
+++ b/openmetadata-clients/openmetadata-java-client/pom.xml
@@ -5,7 +5,7 @@
openmetadata-clients
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
diff --git a/openmetadata-clients/pom.xml b/openmetadata-clients/pom.xml
index 28cedd433658..a272b3993431 100644
--- a/openmetadata-clients/pom.xml
+++ b/openmetadata-clients/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
diff --git a/openmetadata-dist/pom.xml b/openmetadata-dist/pom.xml
index ac849ed3857d..adc5346cbd05 100644
--- a/openmetadata-dist/pom.xml
+++ b/openmetadata-dist/pom.xml
@@ -20,7 +20,7 @@
platform
org.open-metadata
- 1.6.0
+ 1.6.1
openmetadata-dist
diff --git a/openmetadata-service/pom.xml b/openmetadata-service/pom.xml
index a29fca1a969c..096dbd6e2ea8 100644
--- a/openmetadata-service/pom.xml
+++ b/openmetadata-service/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
openmetadata-service
diff --git a/openmetadata-shaded-deps/elasticsearch-dep/pom.xml b/openmetadata-shaded-deps/elasticsearch-dep/pom.xml
index d0a994a0e1ed..c0c94fdc8d83 100644
--- a/openmetadata-shaded-deps/elasticsearch-dep/pom.xml
+++ b/openmetadata-shaded-deps/elasticsearch-dep/pom.xml
@@ -5,7 +5,7 @@
openmetadata-shaded-deps
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
elasticsearch-deps
diff --git a/openmetadata-shaded-deps/opensearch-dep/pom.xml b/openmetadata-shaded-deps/opensearch-dep/pom.xml
index 784cf285bc8d..dc8de6aca1a9 100644
--- a/openmetadata-shaded-deps/opensearch-dep/pom.xml
+++ b/openmetadata-shaded-deps/opensearch-dep/pom.xml
@@ -5,7 +5,7 @@
openmetadata-shaded-deps
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
opensearch-deps
diff --git a/openmetadata-shaded-deps/pom.xml b/openmetadata-shaded-deps/pom.xml
index bd309b280cc5..b22dc2d1f99f 100644
--- a/openmetadata-shaded-deps/pom.xml
+++ b/openmetadata-shaded-deps/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
openmetadata-shaded-deps
diff --git a/openmetadata-spec/pom.xml b/openmetadata-spec/pom.xml
index 7c2f99c83352..fea0cc31461d 100644
--- a/openmetadata-spec/pom.xml
+++ b/openmetadata-spec/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
diff --git a/openmetadata-ui/pom.xml b/openmetadata-ui/pom.xml
index ff9cd83ff40c..67e8125b1e21 100644
--- a/openmetadata-ui/pom.xml
+++ b/openmetadata-ui/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.0
+ 1.6.1
4.0.0
diff --git a/pom.xml b/pom.xml
index 0c7fe79092b0..bda8ae30018b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
based on Open Metadata Standards/APIs, supporting connectors to a wide range of data services,
OpenMetadata enables end-to-end metadata management, giving you the freedom to unlock the value of your data assets.
- 1.6.0
+ 1.6.1
https://github.com/open-metadata/OpenMetadata
openmetadata-spec
From 30a5f1aab2990027893dce5ab77b8a0eccb09cdf Mon Sep 17 00:00:00 2001
From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Date: Tue, 10 Dec 2024 22:53:48 +0530
Subject: [PATCH 005/294] chore(ui): update what's new for 1.6.1 (#18994)
(cherry picked from commit caf6b2f43258f69a74ef186023fb7e063e67bc92)
---
.../Modals/WhatsNewModal/whatsNewData.ts | 148 +++++++++++++++++-
1 file changed, 146 insertions(+), 2 deletions(-)
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/WhatsNewModal/whatsNewData.ts b/openmetadata-ui/src/main/resources/ui/src/components/Modals/WhatsNewModal/whatsNewData.ts
index 18562023fcbf..7eb63e10b6c6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/WhatsNewModal/whatsNewData.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/WhatsNewModal/whatsNewData.ts
@@ -17,7 +17,7 @@ import incidentManagerSampleData from '../../../assets/img/incidentManagerSample
import profilerConfigPage from '../../../assets/img/profilerConfigPage.png';
import collateIcon from '../../../assets/svg/ic-collate.svg';
-export const COOKIE_VERSION = 'VERSION_1_5_12'; // To be changed with each release.
+export const COOKIE_VERSION = 'VERSION_1_6_1'; // To be changed with each release.
// for youtube video make isImage = false and path = {video embed id}
// embed:- youtube video => share => click on embed and take {url with id} from it
@@ -1170,7 +1170,151 @@ To continue pursuing this objective, the application was completely refactored t
{
id: 55,
version: 'v1.6.0',
- description: 'Released on 5th December 2024.',
+ description: 'Released on 10th December 2024.',
+ features: [
+ {
+ title: `Visualizing Your Data Landscape with Entity Relationship (ER) Diagrams (Collate)`,
+ description: `Understanding complex database schemas can be challenging without clear visualization. While OpenMetadata's best-in-class Lineage UI helps track data flow, there are better options for viewing structural relationships between tables. Collate 1.6 introduces ER diagrams as a new feature to let you:
+
+1. Visualize table connections through primary and foreign key constraints
+
+2. Navigate between data assets to discover relationships
+
+3. Modify connections using the built-in UI editor
+
+ER diagrams help you better understand and manage your data architecture by showing how your database tables relate to each other.`,
+ isImage: false,
+ path: 'https://www.youtube.com/embed/3m2xHpIsYuM',
+ },
+ {
+ title:
+ 'Establishing Smooth Data Governance with Automated Glossary Approval Workflows (Collate)',
+ description: `Organizations often struggle with data governance due to rigid, pre-defined manual workflows. OpenMetadata 1.6 introduces a new, automated data governance framework designed to be customized to each organization's needs.
+
+In Collate 1.6, the Glossary Approval Workflow has been migrated to this new framework. Now, you can create custom approval processes with specific conditions and rules and easily visualize them through intuitive workflow diagrams. You can also create smart approval processes for glossary terms with real-time state changes and task creation to save time and streamline work. `,
+ isImage: false,
+ path: 'https://www.youtube.com/embed/yKJNWUb_ucA',
+ },
+ {
+ title:
+ 'Data Certification Workflows for Automated Bronze, Silver, & Gold Data Standardization (Collate)',
+ description: `Collate 1.6 also leverages the new data governance framework for a new Data Certification Workflow, allowing you to define your organization's rules to certify your data as Bronze, Silver, or Gold. Certified assets are a great way to help users discover the right data and inform them which data has been properly curated.
+
+Our vision is to expand our governance framework to allow our users to create their own Custom Governance workflows. We want to enable data teams to implement and automate data governance processes that perfectly fit your organization, promoting data quality and compliance.`,
+ isImage: false,
+ path: 'https://www.youtube.com/embed/hqxtn6uAvt4',
+ },
+ {
+ title:
+ 'Maintaining a Healthy Data Platform with Observability Dashboards (Collate)',
+ description: `Monitoring data quality and incident management across platforms can be challenging. OpenMetadata has been a pillar for data quality implementations, with its ability to create tests from the UI, native observability alerts, and Incident Manager. It offers data quality insights on a per-table level. .
+
+In Collate 1.6, we’re introducing platform-wide observability dashboards that allow you to track overall data quality coverage trends and analyze incident response performance across your entire data estate. Quickly identify root causes through enhanced asset and lineage views and enable proactive data quality management across your entire data ecosystem.`,
+ isImage: false,
+ path: 'https://www.youtube.com/embed/DQ-abGXOsHE',
+ },
+ {
+ title: 'Elevating Metric Management with Dedicated Metric Entities',
+ description: `Metrics are essential for data-driven organizations, but OpenMetadata previously lacked dedicated metric management, forcing users to use glossary terms as a workaround. The new "Metric" entity in OpenMetadata 1.6 provides a purpose-built solution to:.
+
+1. Document detailed metric calculations and descriptions
+
+2. Record calculation formulas and implementation code (Python, Java, SQL, LaTeX)
+
+3. Visualize metric lineage from source data to insights
+
+This new addition helps teams better manage, understand, and calculate their business KPIs, for improved data literacy and consistency across data teams. `,
+ isImage: false,
+ path: 'https://www.youtube.com/embed/Nf97_oWNAmM',
+ },
+ {
+ title: 'Reinforcing Data Security with Search RBAC',
+ description: `OpenMetadata's Roles and Policies enable granular permission control, ensuring appropriate access to metadata across different domains and teams. Some data teams may wish to enable data discovery to search for other tables while still enforcing controls with access requests. Other data teams in more restrictive environments may also wish to control the search experience.
+
+OpenMetadata 1.6 extends Role-Based Access Control (RBAC) to search functionality, allowing administrators to tailor user search experience. This provides personalized search results, with users only seeing assets they have permission to access, as well as stronger data governance by ensuring users only interact with data within their defined roles and responsibilities.`,
+ isImage: false,
+ path: 'https://www.youtube.com/embed/03ke9uv0PG0',
+ },
+ {
+ title: 'Streamlining Data Management with Additional Enhancements',
+ description: `Release 1.6 comes with several other notable improvements:
+
+- **Asynchronous Export APIs** : Enjoy increased efficiency when exporting and importing large datasets with new asynchronous APIs.
+
+- **Faster Search Re-indexing**: Experience significantly improved performance in search re-indexing, making data discovery even smoother.
+
+- **Improved Data Insights Custom Dashboards UI (Collate)**: To make it even easier to write your own insights dashboards in Collate.
+
+- **Slack Integration (Collate)**: Collate is releasing a new Application that lets your users find and share assets directly within your Slack workspace!
+
+- **Alert Debuggability**: Allowing users to test the destinations and see whenever the alert was triggered.`,
+ isImage: false,
+ path: 'https://www.youtube.com/embed/7pUF9ZK2iK4',
+ },
+ {
+ title: 'Expanded Connector Ecosystem and Diversity',
+ description: `OpenMetadata’s ingestion framework contains 80+ native connectors. These connectors are the foundation of the platform and bring in all the metadata your team needs: technical metadata, lineage, usage, profiling, etc.
+
+We bring new connectors in each release, continuously expanding our coverage. This time, release 1.6 comes with seven new connectors:
+
+1. **OpenAPI**: Extract rich metadata from OpenAPI specifications, including endpoints and schemas.
+
+2. **Sigma**: Bringing in your BI dashboard information.
+
+3. **Exasol**: Gain insights into your Exasol database, now supported thanks to Nicola Coretti’s OSS contribution!
+
+And in Collate, we are bringing four ETL, dashboarding and ML tools: **Matillion, Azure Data Factory, Stitch, PowerBI Server** and **Vertex AI!**`,
+ isImage: false,
+ path: '',
+ },
+ ],
+ changeLogs: {
+ ['Backward Incompatible Changes']: `
+
+**Ingestion Workflow Status:**
+
+We are updating how we compute the success percentage. Previously, we took into account for partial success the results of the Source (e.g., the tables we were able to properly retrieve from Snowflake, Redshift, etc.). This means that we had an error threshold in there were if up to 90% of the tables were successfully ingested, we would still consider the workflow as successful. However, any errors when sending the information to OpenMetadata would be considered as a failure.
+Now, we're changing this behavior to consider the success rate of all the steps involved in the workflow. The UI will then show more Partial Success statuses rather than Failed, properly reflecting the real state of the workflow.
+
+**Profiler & Auto Classification Workflow:**
+
+We are creating a new Auto Classification workflow that will take care of managing the sample data and PII classification, which was previously done by the Profiler workflow. This change will allow us to have a more modular and scalable system.
+The Profiler workflow will now only focus on the profiling part of the data, while the Auto Classification will take care of the rest.
+
+This means that we are removing these properties from the DatabaseServiceProfilerPipeline schema:
+
+- generateSampleData
+- processPiiSensitive
+- confidence which will be moved to the new
+- Adding Glossary Term view is improved. Now we show glossary terms hierarchically enabling a better understanding of how the terms are setup while adding it to a table or dashboard.
+- DatabaseServiceAutoClassificationPipeline schema.
+
+What you will need to do:
+
+- If you are using the EXTERNAL ingestion for the profiler (YAML configuration), you will need to update your configuration, removing these properties as well.
+- If you still want to use the Auto PII Classification and sampling features, you can create the new workflow from the UI.
+`,
+
+ ['RBAC Policy Updates for EditTags']: `We have given more granularity to the EditTags policy. Previously, it was a single policy that allowed the user to manage any kind of tagging to the assets, including adding tags, glossary terms, and Tiers.
+
+Now, we have split this policy to give further control on which kind of tagging the user can manage. The EditTags policy has been
+split into:
+
+- **EditTags**: to add tags.
+- **EditGlossaryTerms**: to add Glossary Terms.
+- **EditTier**: to add Tier tags.`,
+
+ [`Metadata Actions for ML Tagging - Deprecation Notice ${CollateIconWithLinkMD}`]: `
+Since we are introducing the Auto Classification workflow, we are going to remove in 1.7 the ML Tagging action from the Metadata Actions. That feature will be covered already by the Auto Classification workflow, which even brings more flexibility allow the on-the-fly usage of the sample data for classification purposes without having to store it in the database.`,
+
+ [`Service Spec for the Ingestion Framework`]: `This impacts users who maintain their own connectors for the ingestion framework that are NOT part of the OpenMetadata python library (openmetadata-ingestion). Introducing the "connector specifcication class (ServiceSpec)". The ServiceSpec class serves as the entrypoint for the connector and holds the references for the classes that will be used to ingest and process the metadata from the source. You can see postgres for an implementation example.`,
+ },
+ },
+ {
+ id: 56,
+ version: 'v1.6.1',
+ description: 'Released on 10th December 2024.',
+ note: "In 1.6.1, Fixes tags listing for explore page on top of 1.6.0 release. Don't miss out the release highlights!",
features: [
{
title: `Visualizing Your Data Landscape with Entity Relationship (ER) Diagrams (Collate)`,
From e6d975add23648e8c0a9547ef4329f2f3481c30e Mon Sep 17 00:00:00 2001
From: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 12 Dec 2024 05:51:36 +0000
Subject: [PATCH 006/294] chore(release): Prepare Branch for `1.6.2`
---
common/pom.xml | 2 +-
.../docker-compose-ingestion/docker-compose-ingestion.yml | 2 +-
.../docker-compose-openmetadata.yml | 4 ++--
docker/docker-compose-quickstart/Dockerfile | 4 ++--
.../docker-compose-quickstart/docker-compose-postgres.yml | 8 ++++----
docker/docker-compose-quickstart/docker-compose.yml | 8 ++++----
ingestion/Dockerfile | 2 +-
ingestion/operators/docker/Dockerfile | 2 +-
ingestion/pyproject.toml | 2 +-
openmetadata-airflow-apis/pyproject.toml | 2 +-
openmetadata-clients/openmetadata-java-client/pom.xml | 2 +-
openmetadata-clients/pom.xml | 2 +-
openmetadata-dist/pom.xml | 2 +-
openmetadata-service/pom.xml | 2 +-
openmetadata-shaded-deps/elasticsearch-dep/pom.xml | 2 +-
openmetadata-shaded-deps/opensearch-dep/pom.xml | 2 +-
openmetadata-shaded-deps/pom.xml | 2 +-
openmetadata-spec/pom.xml | 2 +-
openmetadata-ui/pom.xml | 2 +-
pom.xml | 2 +-
20 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/common/pom.xml b/common/pom.xml
index 62c059357e0a..8571f63cc54c 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -18,7 +18,7 @@
platform
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
diff --git a/docker/docker-compose-ingestion/docker-compose-ingestion.yml b/docker/docker-compose-ingestion/docker-compose-ingestion.yml
index a717b5c3ebfa..68abdf3f430f 100644
--- a/docker/docker-compose-ingestion/docker-compose-ingestion.yml
+++ b/docker/docker-compose-ingestion/docker-compose-ingestion.yml
@@ -18,7 +18,7 @@ volumes:
services:
ingestion:
container_name: openmetadata_ingestion
- image: docker.getcollate.io/openmetadata/ingestion:1.6.1
+ image: docker.getcollate.io/openmetadata/ingestion:1.6.2
environment:
AIRFLOW__API__AUTH_BACKENDS: "airflow.api.auth.backend.basic_auth,airflow.api.auth.backend.session"
AIRFLOW__CORE__EXECUTOR: LocalExecutor
diff --git a/docker/docker-compose-openmetadata/docker-compose-openmetadata.yml b/docker/docker-compose-openmetadata/docker-compose-openmetadata.yml
index 82e48f8fa7de..5546ef294c27 100644
--- a/docker/docker-compose-openmetadata/docker-compose-openmetadata.yml
+++ b/docker/docker-compose-openmetadata/docker-compose-openmetadata.yml
@@ -14,7 +14,7 @@ services:
execute-migrate-all:
container_name: execute_migrate_all
command: "./bootstrap/openmetadata-ops.sh migrate"
- image: docker.getcollate.io/openmetadata/server:1.6.1
+ image: docker.getcollate.io/openmetadata/server:1.6.2
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
SERVER_PORT: ${SERVER_PORT:-8585}
@@ -227,7 +227,7 @@ services:
openmetadata-server:
container_name: openmetadata_server
restart: always
- image: docker.getcollate.io/openmetadata/server:1.6.1
+ image: docker.getcollate.io/openmetadata/server:1.6.2
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
SERVER_PORT: ${SERVER_PORT:-8585}
diff --git a/docker/docker-compose-quickstart/Dockerfile b/docker/docker-compose-quickstart/Dockerfile
index f27cf543bf47..c55fada178f4 100644
--- a/docker/docker-compose-quickstart/Dockerfile
+++ b/docker/docker-compose-quickstart/Dockerfile
@@ -11,7 +11,7 @@
# Build stage
FROM alpine:3 AS build
-ARG RI_VERSION="1.6.1"
+ARG RI_VERSION="1.6.2"
ENV RELEASE_URL="https://github.com/open-metadata/OpenMetadata/releases/download/${RI_VERSION}-release/openmetadata-${RI_VERSION}.tar.gz"
RUN mkdir -p /opt/openmetadata && \
@@ -21,7 +21,7 @@ RUN mkdir -p /opt/openmetadata && \
# Final stage
FROM alpine:3
-ARG RI_VERSION="1.6.1"
+ARG RI_VERSION="1.6.2"
ARG BUILD_DATE
ARG COMMIT_ID
LABEL maintainer="OpenMetadata"
diff --git a/docker/docker-compose-quickstart/docker-compose-postgres.yml b/docker/docker-compose-quickstart/docker-compose-postgres.yml
index aeeccef1e2d3..df898438ef23 100644
--- a/docker/docker-compose-quickstart/docker-compose-postgres.yml
+++ b/docker/docker-compose-quickstart/docker-compose-postgres.yml
@@ -18,7 +18,7 @@ volumes:
services:
postgresql:
container_name: openmetadata_postgresql
- image: docker.getcollate.io/openmetadata/postgresql:1.6.1
+ image: docker.getcollate.io/openmetadata/postgresql:1.6.2
restart: always
command: "--work_mem=10MB"
environment:
@@ -61,7 +61,7 @@ services:
execute-migrate-all:
container_name: execute_migrate_all
- image: docker.getcollate.io/openmetadata/server:1.6.1
+ image: docker.getcollate.io/openmetadata/server:1.6.2
command: "./bootstrap/openmetadata-ops.sh migrate"
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
@@ -275,7 +275,7 @@ services:
openmetadata-server:
container_name: openmetadata_server
restart: always
- image: docker.getcollate.io/openmetadata/server:1.6.1
+ image: docker.getcollate.io/openmetadata/server:1.6.2
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
SERVER_PORT: ${SERVER_PORT:-8585}
@@ -483,7 +483,7 @@ services:
ingestion:
container_name: openmetadata_ingestion
- image: docker.getcollate.io/openmetadata/ingestion:1.6.1
+ image: docker.getcollate.io/openmetadata/ingestion:1.6.2
depends_on:
elasticsearch:
condition: service_started
diff --git a/docker/docker-compose-quickstart/docker-compose.yml b/docker/docker-compose-quickstart/docker-compose.yml
index 92b7db1f9f25..9ea74153d3f2 100644
--- a/docker/docker-compose-quickstart/docker-compose.yml
+++ b/docker/docker-compose-quickstart/docker-compose.yml
@@ -18,7 +18,7 @@ volumes:
services:
mysql:
container_name: openmetadata_mysql
- image: docker.getcollate.io/openmetadata/db:1.6.1
+ image: docker.getcollate.io/openmetadata/db:1.6.2
command: "--sort_buffer_size=10M"
restart: always
environment:
@@ -59,7 +59,7 @@ services:
execute-migrate-all:
container_name: execute_migrate_all
- image: docker.getcollate.io/openmetadata/server:1.6.1
+ image: docker.getcollate.io/openmetadata/server:1.6.2
command: "./bootstrap/openmetadata-ops.sh migrate"
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
@@ -273,7 +273,7 @@ services:
openmetadata-server:
container_name: openmetadata_server
restart: always
- image: docker.getcollate.io/openmetadata/server:1.6.1
+ image: docker.getcollate.io/openmetadata/server:1.6.2
environment:
OPENMETADATA_CLUSTER_NAME: ${OPENMETADATA_CLUSTER_NAME:-openmetadata}
SERVER_PORT: ${SERVER_PORT:-8585}
@@ -482,7 +482,7 @@ services:
ingestion:
container_name: openmetadata_ingestion
- image: docker.getcollate.io/openmetadata/ingestion:1.6.1
+ image: docker.getcollate.io/openmetadata/ingestion:1.6.2
depends_on:
elasticsearch:
condition: service_started
diff --git a/ingestion/Dockerfile b/ingestion/Dockerfile
index e212415c0f1c..f220e0cba106 100644
--- a/ingestion/Dockerfile
+++ b/ingestion/Dockerfile
@@ -76,7 +76,7 @@ ARG INGESTION_DEPENDENCY="all"
ENV PIP_NO_CACHE_DIR=1
# Make pip silent
ENV PIP_QUIET=1
-ARG RI_VERSION="1.6.1.0"
+ARG RI_VERSION="1.6.2.0"
RUN pip install --upgrade pip
RUN pip install "openmetadata-managed-apis~=${RI_VERSION}" --constraint "https://raw.githubusercontent.com/apache/airflow/constraints-2.9.1/constraints-3.10.txt"
RUN pip install "openmetadata-ingestion[${INGESTION_DEPENDENCY}]~=${RI_VERSION}"
diff --git a/ingestion/operators/docker/Dockerfile b/ingestion/operators/docker/Dockerfile
index fec7b09ea066..b197365c2a52 100644
--- a/ingestion/operators/docker/Dockerfile
+++ b/ingestion/operators/docker/Dockerfile
@@ -81,7 +81,7 @@ ENV PIP_QUIET=1
RUN pip install --upgrade pip
ARG INGESTION_DEPENDENCY="all"
-ARG RI_VERSION="1.6.1.0"
+ARG RI_VERSION="1.6.2.0"
RUN pip install --upgrade pip
RUN pip install "openmetadata-ingestion[airflow]~=${RI_VERSION}"
RUN pip install "openmetadata-ingestion[${INGESTION_DEPENDENCY}]~=${RI_VERSION}"
diff --git a/ingestion/pyproject.toml b/ingestion/pyproject.toml
index f63a5269c5b6..5f9623b78ff6 100644
--- a/ingestion/pyproject.toml
+++ b/ingestion/pyproject.toml
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
# since it helps us organize and isolate version management
[project]
name = "openmetadata-ingestion"
-version = "1.6.1.0"
+version = "1.6.2.0"
dynamic = ["readme", "dependencies", "optional-dependencies"]
authors = [
{ name = "OpenMetadata Committers" }
diff --git a/openmetadata-airflow-apis/pyproject.toml b/openmetadata-airflow-apis/pyproject.toml
index bcabf86bcf9a..06001a474709 100644
--- a/openmetadata-airflow-apis/pyproject.toml
+++ b/openmetadata-airflow-apis/pyproject.toml
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
# since it helps us organize and isolate version management
[project]
name = "openmetadata_managed_apis"
-version = "1.6.1.0"
+version = "1.6.2.0"
readme = "README.md"
authors = [
{name = "OpenMetadata Committers"}
diff --git a/openmetadata-clients/openmetadata-java-client/pom.xml b/openmetadata-clients/openmetadata-java-client/pom.xml
index 2bc9816a523f..a52b7370c011 100644
--- a/openmetadata-clients/openmetadata-java-client/pom.xml
+++ b/openmetadata-clients/openmetadata-java-client/pom.xml
@@ -5,7 +5,7 @@
openmetadata-clients
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
diff --git a/openmetadata-clients/pom.xml b/openmetadata-clients/pom.xml
index a272b3993431..0b0ff6be9ac7 100644
--- a/openmetadata-clients/pom.xml
+++ b/openmetadata-clients/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
diff --git a/openmetadata-dist/pom.xml b/openmetadata-dist/pom.xml
index adc5346cbd05..6df8762cafde 100644
--- a/openmetadata-dist/pom.xml
+++ b/openmetadata-dist/pom.xml
@@ -20,7 +20,7 @@
platform
org.open-metadata
- 1.6.1
+ 1.6.2
openmetadata-dist
diff --git a/openmetadata-service/pom.xml b/openmetadata-service/pom.xml
index 096dbd6e2ea8..5299851b9021 100644
--- a/openmetadata-service/pom.xml
+++ b/openmetadata-service/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
openmetadata-service
diff --git a/openmetadata-shaded-deps/elasticsearch-dep/pom.xml b/openmetadata-shaded-deps/elasticsearch-dep/pom.xml
index c0c94fdc8d83..23ea37785246 100644
--- a/openmetadata-shaded-deps/elasticsearch-dep/pom.xml
+++ b/openmetadata-shaded-deps/elasticsearch-dep/pom.xml
@@ -5,7 +5,7 @@
openmetadata-shaded-deps
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
elasticsearch-deps
diff --git a/openmetadata-shaded-deps/opensearch-dep/pom.xml b/openmetadata-shaded-deps/opensearch-dep/pom.xml
index dc8de6aca1a9..78247a1d7bbc 100644
--- a/openmetadata-shaded-deps/opensearch-dep/pom.xml
+++ b/openmetadata-shaded-deps/opensearch-dep/pom.xml
@@ -5,7 +5,7 @@
openmetadata-shaded-deps
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
opensearch-deps
diff --git a/openmetadata-shaded-deps/pom.xml b/openmetadata-shaded-deps/pom.xml
index b22dc2d1f99f..1e86bb9844c9 100644
--- a/openmetadata-shaded-deps/pom.xml
+++ b/openmetadata-shaded-deps/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
openmetadata-shaded-deps
diff --git a/openmetadata-spec/pom.xml b/openmetadata-spec/pom.xml
index fea0cc31461d..a4c9249d1bf6 100644
--- a/openmetadata-spec/pom.xml
+++ b/openmetadata-spec/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
diff --git a/openmetadata-ui/pom.xml b/openmetadata-ui/pom.xml
index 67e8125b1e21..5623c41b7810 100644
--- a/openmetadata-ui/pom.xml
+++ b/openmetadata-ui/pom.xml
@@ -5,7 +5,7 @@
platform
org.open-metadata
- 1.6.1
+ 1.6.2
4.0.0
diff --git a/pom.xml b/pom.xml
index bda8ae30018b..cf005a62d8be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
based on Open Metadata Standards/APIs, supporting connectors to a wide range of data services,
OpenMetadata enables end-to-end metadata management, giving you the freedom to unlock the value of your data assets.
- 1.6.1
+ 1.6.2
https://github.com/open-metadata/OpenMetadata
openmetadata-spec
From eca21b384522b338f9ce12c770af54b0a39a6cb7 Mon Sep 17 00:00:00 2001
From: IceS2
Date: Thu, 12 Dec 2024 10:01:39 +0100
Subject: [PATCH 007/294] Parametrizing Flowable configurations and adding a
way to handle history cleaning (#18990)
---
.../governance/workflows/WorkflowHandler.java | 72 +++++++++++++++----
.../service/jdbi3/CollectionDAO.java | 2 +
.../service/jdbi3/SystemRepository.java | 19 +++++
.../resources/settings/SettingsCache.java | 17 +++++
.../configuration/workflowSettings.json | 59 +++++++++++++++
.../json/schema/settings/settings.json | 6 +-
6 files changed, 162 insertions(+), 13 deletions(-)
create mode 100644 openmetadata-spec/src/main/resources/json/schema/configuration/workflowSettings.json
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowHandler.java b/openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowHandler.java
index ae3ecab0ac34..899c4b7f9c10 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowHandler.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowHandler.java
@@ -2,6 +2,7 @@
import static org.openmetadata.service.governance.workflows.elements.TriggerFactory.getTriggerWorkflowId;
+import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -14,6 +15,7 @@
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
+import org.flowable.engine.ProcessEngines;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
@@ -23,44 +25,82 @@
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
+import org.openmetadata.schema.configuration.WorkflowSettings;
+import org.openmetadata.service.Entity;
import org.openmetadata.service.OpenMetadataApplicationConfig;
import org.openmetadata.service.exception.UnhandledServerException;
+import org.openmetadata.service.jdbi3.SystemRepository;
import org.openmetadata.service.jdbi3.locator.ConnectionType;
@Slf4j
public class WorkflowHandler {
- private final RepositoryService repositoryService;
- private final RuntimeService runtimeService;
- private final TaskService taskService;
- private final HistoryService historyService;
+ private ProcessEngine processEngine;
+ private RepositoryService repositoryService;
+ private RuntimeService runtimeService;
+ private TaskService taskService;
+ private HistoryService historyService;
private static WorkflowHandler instance;
private static volatile boolean initialized = false;
private WorkflowHandler(OpenMetadataApplicationConfig config) {
ProcessEngineConfiguration processEngineConfiguration =
new StandaloneProcessEngineConfiguration()
- .setAsyncExecutorActivate(true)
- .setAsyncExecutorCorePoolSize(50)
- .setAsyncExecutorMaxPoolSize(100)
- .setAsyncExecutorThreadPoolQueueSize(1000)
- .setAsyncExecutorMaxAsyncJobsDuePerAcquisition(20)
.setJdbcUrl(config.getDataSourceFactory().getUrl())
.setJdbcUsername(config.getDataSourceFactory().getUser())
.setJdbcPassword(config.getDataSourceFactory().getPassword())
.setJdbcDriver(config.getDataSourceFactory().getDriverClass())
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
- // Add Global Failure Listener
- processEngineConfiguration.setEventListeners(List.of(new WorkflowFailureListener()));
-
if (ConnectionType.MYSQL.label.equals(config.getDataSourceFactory().getDriverClass())) {
processEngineConfiguration.setDatabaseType(ProcessEngineConfiguration.DATABASE_TYPE_MYSQL);
} else {
processEngineConfiguration.setDatabaseType(ProcessEngineConfiguration.DATABASE_TYPE_POSTGRES);
}
+ initializeNewProcessEngine(processEngineConfiguration);
+ }
+
+ public void initializeNewProcessEngine(
+ ProcessEngineConfiguration currentProcessEngineConfiguration) {
+ ProcessEngines.destroy();
+ SystemRepository systemRepository = Entity.getSystemRepository();
+ WorkflowSettings workflowSettings = systemRepository.getWorkflowSettings();
+
+ StandaloneProcessEngineConfiguration processEngineConfiguration =
+ new StandaloneProcessEngineConfiguration();
+
+ // Setting Database Configuration
+ processEngineConfiguration
+ .setJdbcUrl(currentProcessEngineConfiguration.getJdbcUrl())
+ .setJdbcUsername(currentProcessEngineConfiguration.getJdbcUsername())
+ .setJdbcPassword(currentProcessEngineConfiguration.getJdbcPassword())
+ .setJdbcDriver(currentProcessEngineConfiguration.getJdbcDriver())
+ .setDatabaseType(currentProcessEngineConfiguration.getDatabaseType())
+ .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
+
+ // Setting Async Executor Configuration
+ processEngineConfiguration
+ .setAsyncExecutorActivate(true)
+ .setAsyncExecutorCorePoolSize(workflowSettings.getExecutorConfiguration().getCorePoolSize())
+ .setAsyncExecutorMaxPoolSize(workflowSettings.getExecutorConfiguration().getMaxPoolSize())
+ .setAsyncExecutorThreadPoolQueueSize(
+ workflowSettings.getExecutorConfiguration().getQueueSize())
+ .setAsyncExecutorMaxAsyncJobsDuePerAcquisition(
+ workflowSettings.getExecutorConfiguration().getTasksDuePerAcquisition());
+
+ // Setting History CleanUp
+ processEngineConfiguration
+ .setEnableHistoryCleaning(true)
+ .setCleanInstancesEndedAfter(
+ Duration.ofDays(
+ workflowSettings.getHistoryCleanUpConfiguration().getCleanAfterNumberOfDays()));
+
+ // Add Global Failure Listener
+ processEngineConfiguration.setEventListeners(List.of(new WorkflowFailureListener()));
+
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
+ this.processEngine = processEngine;
this.repositoryService = processEngine.getRepositoryService();
this.runtimeService = processEngine.getRuntimeService();
this.taskService = processEngine.getTaskService();
@@ -81,6 +121,14 @@ public static WorkflowHandler getInstance() {
throw new UnhandledServerException("WorkflowHandler is not initialized.");
}
+ public ProcessEngineConfiguration getProcessEngineConfiguration() {
+ if (processEngine != null) {
+ return processEngine.getProcessEngineConfiguration();
+ } else {
+ return null;
+ }
+ }
+
public void deploy(Workflow workflow) {
BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
index a5938ea9f4a2..809a53e5d6b4 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
@@ -67,6 +67,7 @@
import org.openmetadata.schema.auth.RefreshToken;
import org.openmetadata.schema.auth.TokenType;
import org.openmetadata.schema.configuration.AssetCertificationSettings;
+import org.openmetadata.schema.configuration.WorkflowSettings;
import org.openmetadata.schema.dataInsight.DataInsightChart;
import org.openmetadata.schema.dataInsight.custom.DataInsightCustomChart;
import org.openmetadata.schema.dataInsight.kpi.Kpi;
@@ -5156,6 +5157,7 @@ public static Settings getSettings(SettingsType configType, String json) {
case SEARCH_SETTINGS -> JsonUtils.readValue(json, SearchSettings.class);
case ASSET_CERTIFICATION_SETTINGS -> JsonUtils.readValue(
json, AssetCertificationSettings.class);
+ case WORKFLOW_SETTINGS -> JsonUtils.readValue(json, WorkflowSettings.class);
case LINEAGE_SETTINGS -> JsonUtils.readValue(json, LineageSettings.class);
default -> throw new IllegalArgumentException("Invalid Settings Type " + configType);
};
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
index d08b2ca6c336..b854c39a6ee5 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
@@ -16,6 +16,7 @@
import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.api.configuration.UiThemePreference;
import org.openmetadata.schema.configuration.AssetCertificationSettings;
+import org.openmetadata.schema.configuration.WorkflowSettings;
import org.openmetadata.schema.email.SmtpSettings;
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineServiceClientResponse;
import org.openmetadata.schema.security.client.OpenMetadataJWTClientConfig;
@@ -32,6 +33,7 @@
import org.openmetadata.service.OpenMetadataApplicationConfig;
import org.openmetadata.service.exception.CustomExceptionMessage;
import org.openmetadata.service.fernet.Fernet;
+import org.openmetadata.service.governance.workflows.WorkflowHandler;
import org.openmetadata.service.jdbi3.CollectionDAO.SystemDAO;
import org.openmetadata.service.migration.MigrationValidationClient;
import org.openmetadata.service.resources.settings.SettingsCache;
@@ -119,6 +121,15 @@ public AssetCertificationSettings getAssetCertificationSettings() {
.orElse(null);
}
+ public WorkflowSettings getWorkflowSettings() {
+ Optional oWorkflowSettings =
+ Optional.ofNullable(getConfigWithKey(SettingsType.WORKFLOW_SETTINGS.value()));
+
+ return oWorkflowSettings
+ .map(settings -> (WorkflowSettings) settings.getConfigValue())
+ .orElse(null);
+ }
+
public Settings getEmailConfigInternal() {
try {
Settings setting = dao.getConfigWithKey(SettingsType.EMAIL_CONFIGURATION.value());
@@ -209,6 +220,13 @@ public Response patchSetting(String settingName, JsonPatch patch) {
return (new RestUtil.PutResponse<>(Response.Status.OK, original, ENTITY_UPDATED)).toResponse();
}
+ private void postUpdate(SettingsType settingsType) {
+ if (settingsType == SettingsType.WORKFLOW_SETTINGS) {
+ WorkflowHandler workflowHandler = WorkflowHandler.getInstance();
+ workflowHandler.initializeNewProcessEngine(workflowHandler.getProcessEngineConfiguration());
+ }
+ }
+
public void updateSetting(Settings setting) {
try {
if (setting.getConfigType() == SettingsType.EMAIL_CONFIGURATION) {
@@ -235,6 +253,7 @@ public void updateSetting(Settings setting) {
setting.getConfigType().toString(), JsonUtils.pojoToJson(setting.getConfigValue()));
// Invalidate Cache
SettingsCache.invalidateSettings(setting.getConfigType().value());
+ postUpdate(setting.getConfigType());
} catch (Exception ex) {
LOG.error("Failing in Updating Setting.", ex);
throw new CustomExceptionMessage(
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java
index a6785ddb4616..16532c563ce7 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java
@@ -19,6 +19,7 @@
import static org.openmetadata.schema.settings.SettingsType.LINEAGE_SETTINGS;
import static org.openmetadata.schema.settings.SettingsType.LOGIN_CONFIGURATION;
import static org.openmetadata.schema.settings.SettingsType.SEARCH_SETTINGS;
+import static org.openmetadata.schema.settings.SettingsType.WORKFLOW_SETTINGS;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
@@ -35,6 +36,9 @@
import org.openmetadata.schema.api.lineage.LineageSettings;
import org.openmetadata.schema.api.search.SearchSettings;
import org.openmetadata.schema.configuration.AssetCertificationSettings;
+import org.openmetadata.schema.configuration.ExecutorConfiguration;
+import org.openmetadata.schema.configuration.HistoryCleanUpConfiguration;
+import org.openmetadata.schema.configuration.WorkflowSettings;
import org.openmetadata.schema.email.SmtpSettings;
import org.openmetadata.schema.settings.Settings;
import org.openmetadata.schema.settings.SettingsType;
@@ -144,6 +148,19 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
systemRepository.createNewSetting(setting);
}
+ // Initialise Workflow Settings
+ Settings workflowSettings = systemRepository.getConfigWithKey(WORKFLOW_SETTINGS.toString());
+ if (workflowSettings == null) {
+ Settings setting =
+ new Settings()
+ .withConfigType(WORKFLOW_SETTINGS)
+ .withConfigValue(
+ new WorkflowSettings()
+ .withExecutorConfiguration(new ExecutorConfiguration())
+ .withHistoryCleanUpConfiguration(new HistoryCleanUpConfiguration()));
+ systemRepository.createNewSetting(setting);
+ }
+
Settings lineageSettings = systemRepository.getConfigWithKey(LINEAGE_SETTINGS.toString());
if (lineageSettings == null) {
// Only in case a config doesn't exist in DB we insert it
diff --git a/openmetadata-spec/src/main/resources/json/schema/configuration/workflowSettings.json b/openmetadata-spec/src/main/resources/json/schema/configuration/workflowSettings.json
new file mode 100644
index 000000000000..fe2969cb88d5
--- /dev/null
+++ b/openmetadata-spec/src/main/resources/json/schema/configuration/workflowSettings.json
@@ -0,0 +1,59 @@
+{
+ "$id": "https://open-metadata.org/schema/configuration/workflowSettings.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "WorkflowSettings",
+ "description": "This schema defines the Workflow Settings.",
+ "type": "object",
+ "javaType": "org.openmetadata.schema.configuration.WorkflowSettings",
+ "definitions": {
+ "executorConfiguration": {
+ "type": "object",
+ "properties": {
+ "corePoolSize": {
+ "type": "integer",
+ "default": 50,
+ "description": "Default worker Pool Size. The Workflow Executor by default has this amount of workers."
+ },
+ "maxPoolSize": {
+ "type": "integer",
+ "default": 100,
+ "description": "Maximum worker Pool Size. The Workflow Executor could grow up to this number of workers."
+ },
+ "queueSize": {
+ "type": "integer",
+ "default": 1000,
+ "description": "Amount of Tasks that can be queued to be picked up by the Workflow Executor."
+ },
+ "tasksDuePerAcquisition": {
+ "type": "integer",
+ "default": 20,
+ "description": "The amount of Tasks that the Workflow Executor is able to pick up each time it looks for more."
+ }
+ },
+ "additionalProperties": false
+ },
+ "historyCleanUpConfiguration": {
+ "type": "object",
+ "properties": {
+ "cleanAfterNumberOfDays": {
+ "type": "integer",
+ "default": 7,
+ "description": "Cleans the Workflow Task that were finished, after given number of days."
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "properties": {
+ "executorConfiguration": {
+ "$ref": "#/definitions/executorConfiguration",
+ "description": "Used to set up the Workflow Executor Settings."
+ },
+ "historyCleanUpConfiguration": {
+ "$ref": "#/definitions/historyCleanUpConfiguration",
+ "description": "Used to set up the History CleanUp Settings."
+ }
+ },
+ "required": ["allowedClassification", "validityPeriod"],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/openmetadata-spec/src/main/resources/json/schema/settings/settings.json b/openmetadata-spec/src/main/resources/json/schema/settings/settings.json
index 0811f5e8fc8f..5a98c9d9552a 100644
--- a/openmetadata-spec/src/main/resources/json/schema/settings/settings.json
+++ b/openmetadata-spec/src/main/resources/json/schema/settings/settings.json
@@ -32,7 +32,8 @@
"profilerConfiguration",
"searchSettings",
"assetCertificationSettings",
- "lineageSettings"
+ "lineageSettings",
+ "workflowSettings"
]
}
},
@@ -84,6 +85,9 @@
},
{
"$ref": "../configuration/lineageSettings.json"
+ },
+ {
+ "$ref": "../configuration/workflowSettings.json"
}
]
}
From 42320d73c9a692ba44cab29e6df8747ab7655080 Mon Sep 17 00:00:00 2001
From: Pere Miquel Brull
Date: Thu, 12 Dec 2024 10:40:03 +0100
Subject: [PATCH 008/294] MINOR - Add APIs to Ref Map (#19019)
* MINOR - Add APIs to Ref Map
* MINOR - Add APIs to Ref Map
* MINOR - Add APIs to Ref Map
* format
---
ingestion/src/metadata/utils/constants.py | 4 ++++
ingestion/src/metadata/utils/elasticsearch.py | 6 ++++++
2 files changed, 10 insertions(+)
diff --git a/ingestion/src/metadata/utils/constants.py b/ingestion/src/metadata/utils/constants.py
index 49a0e7c5eef5..e9a20bcce42d 100644
--- a/ingestion/src/metadata/utils/constants.py
+++ b/ingestion/src/metadata/utils/constants.py
@@ -12,6 +12,8 @@
"""
Define constants useful for the metadata ingestion
"""
+from metadata.generated.schema.entity.data.apiCollection import APICollection
+from metadata.generated.schema.entity.data.apiEndpoint import APIEndpoint
from metadata.generated.schema.entity.data.chart import Chart
from metadata.generated.schema.entity.data.container import Container
from metadata.generated.schema.entity.data.dashboard import Dashboard
@@ -131,6 +133,8 @@
"metadataService": MetadataService,
"searchService": SearchService,
# Data Asset Entities
+ "apiCollection": APICollection,
+ "apiEndpoint": APIEndpoint,
"table": Table,
"storedProcedure": StoredProcedure,
"database": Database,
diff --git a/ingestion/src/metadata/utils/elasticsearch.py b/ingestion/src/metadata/utils/elasticsearch.py
index 0f0ab014b057..14bbe94ab02c 100644
--- a/ingestion/src/metadata/utils/elasticsearch.py
+++ b/ingestion/src/metadata/utils/elasticsearch.py
@@ -18,6 +18,8 @@
from metadata.generated.schema.analytics.reportData import ReportData
from metadata.generated.schema.entity.classification.tag import Tag
+from metadata.generated.schema.entity.data.apiCollection import APICollection
+from metadata.generated.schema.entity.data.apiEndpoint import APIEndpoint
from metadata.generated.schema.entity.data.chart import Chart
from metadata.generated.schema.entity.data.container import Container
from metadata.generated.schema.entity.data.dashboard import Dashboard
@@ -33,6 +35,7 @@
from metadata.generated.schema.entity.data.storedProcedure import StoredProcedure
from metadata.generated.schema.entity.data.table import Table
from metadata.generated.schema.entity.data.topic import Topic
+from metadata.generated.schema.entity.services.apiService import ApiService
from metadata.generated.schema.entity.services.databaseService import DatabaseService
from metadata.generated.schema.entity.teams.team import Team
from metadata.generated.schema.entity.teams.user import User
@@ -42,6 +45,9 @@
T = TypeVar("T", bound=BaseModel)
ES_INDEX_MAP = {
+ ApiService.__name__: "api_service_search_index",
+ APICollection.__name__: "api_collection_search_index",
+ APIEndpoint.__name__: "api_endpoint_search_index",
Table.__name__: "table_search_index",
Database.__name__: "database_search_index",
DatabaseSchema.__name__: "database_schema_search_index",
From d2d7212bc673e2cd1f359e6cb5666728efa35c35 Mon Sep 17 00:00:00 2001
From: Mohit Yadav <105265192+mohityadav766@users.noreply.github.com>
Date: Sun, 15 Dec 2024 01:16:55 +0530
Subject: [PATCH 009/294] Fix Settings Cache (#19053)
(cherry picked from commit 01646431f676190840c6206019859c2589705af6)
---
.../service/OpenMetadataApplication.java | 4 ++
.../governance/workflows/WorkflowHandler.java | 2 +-
.../service/jdbi3/EntityRepository.java | 2 +-
.../service/jdbi3/SystemRepository.java | 24 +++++++++++
.../WorkflowDefinitionResource.java | 1 -
.../resources/system/SystemResourceTest.java | 43 +++++++++++++++++++
6 files changed, 73 insertions(+), 3 deletions(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java b/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java
index 9fedc0c89534..fdb07d0bcba9 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java
@@ -79,6 +79,7 @@
import org.openmetadata.service.exception.JsonMappingExceptionMapper;
import org.openmetadata.service.exception.OMErrorPageHandler;
import org.openmetadata.service.fernet.Fernet;
+import org.openmetadata.service.governance.workflows.WorkflowHandler;
import org.openmetadata.service.jdbi3.CollectionDAO;
import org.openmetadata.service.jdbi3.EntityRepository;
import org.openmetadata.service.jdbi3.MigrationDAO;
@@ -173,6 +174,9 @@ public void run(OpenMetadataApplicationConfig catalogConfig, Environment environ
// Configure the Fernet instance
Fernet.getInstance().setFernetKey(catalogConfig);
+ // Initialize Workflow Handler
+ WorkflowHandler.initialize(catalogConfig);
+
// Init Settings Cache after repositories
SettingsCache.initialize(catalogConfig);
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowHandler.java b/openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowHandler.java
index 899c4b7f9c10..3e28d2d39885 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowHandler.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowHandler.java
@@ -64,7 +64,7 @@ public void initializeNewProcessEngine(
ProcessEngineConfiguration currentProcessEngineConfiguration) {
ProcessEngines.destroy();
SystemRepository systemRepository = Entity.getSystemRepository();
- WorkflowSettings workflowSettings = systemRepository.getWorkflowSettings();
+ WorkflowSettings workflowSettings = systemRepository.getWorkflowSettingsOrDefault();
StandaloneProcessEngineConfiguration processEngineConfiguration =
new StandaloneProcessEngineConfiguration();
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java
index 221da3625c89..d4206190e298 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java
@@ -3023,7 +3023,7 @@ private void updateCertification() {
SystemRepository systemRepository = Entity.getSystemRepository();
AssetCertificationSettings assetCertificationSettings =
- systemRepository.getAssetCertificationSettings();
+ systemRepository.getAssetCertificationSettingOrDefault();
String certificationLabel = updatedCertification.getTagLabel().getTagFQN();
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
index b854c39a6ee5..302882eb0c7d 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
@@ -16,6 +16,8 @@
import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.api.configuration.UiThemePreference;
import org.openmetadata.schema.configuration.AssetCertificationSettings;
+import org.openmetadata.schema.configuration.ExecutorConfiguration;
+import org.openmetadata.schema.configuration.HistoryCleanUpConfiguration;
import org.openmetadata.schema.configuration.WorkflowSettings;
import org.openmetadata.schema.email.SmtpSettings;
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineServiceClientResponse;
@@ -121,6 +123,17 @@ public AssetCertificationSettings getAssetCertificationSettings() {
.orElse(null);
}
+ public AssetCertificationSettings getAssetCertificationSettingOrDefault() {
+ AssetCertificationSettings assetCertificationSettings = getAssetCertificationSettings();
+ if (assetCertificationSettings == null) {
+ assetCertificationSettings =
+ new AssetCertificationSettings()
+ .withAllowedClassification("Certification")
+ .withValidityPeriod("P30D");
+ }
+ return assetCertificationSettings;
+ }
+
public WorkflowSettings getWorkflowSettings() {
Optional oWorkflowSettings =
Optional.ofNullable(getConfigWithKey(SettingsType.WORKFLOW_SETTINGS.value()));
@@ -130,6 +143,17 @@ public WorkflowSettings getWorkflowSettings() {
.orElse(null);
}
+ public WorkflowSettings getWorkflowSettingsOrDefault() {
+ WorkflowSettings workflowSettings = getWorkflowSettings();
+ if (workflowSettings == null) {
+ workflowSettings =
+ new WorkflowSettings()
+ .withExecutorConfiguration(new ExecutorConfiguration())
+ .withHistoryCleanUpConfiguration(new HistoryCleanUpConfiguration());
+ }
+ return workflowSettings;
+ }
+
public Settings getEmailConfigInternal() {
try {
Settings setting = dao.getConfigWithKey(SettingsType.EMAIL_CONFIGURATION.value());
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java
index 7cbc8e066a97..f76306509325 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java
@@ -70,7 +70,6 @@ public static class WorkflowDefinitionList extends ResultList
Date: Sun, 15 Dec 2024 01:18:12 +0530
Subject: [PATCH 010/294] Domain Policy Update to be non-system (#19060)
(cherry picked from commit 3578a4b32d605494d44073bfe3fb82742a62b93e)
---
.../native/1.5.15/mysql/postDataMigrationSQLScript.sql | 0
.../sql/migrations/native/1.5.15/mysql/schemaChanges.sql | 5 +++++
.../native/1.5.15/postgres/postDataMigrationSQLScript.sql | 0
.../sql/migrations/native/1.5.15/postgres/schemaChanges.sql | 5 +++++
.../main/resources/json/data/policy/DomainAccessPolicy.json | 4 ++--
.../main/resources/json/data/role/DomainOnlyAccessRole.json | 4 ++--
6 files changed, 14 insertions(+), 4 deletions(-)
create mode 100644 bootstrap/sql/migrations/native/1.5.15/mysql/postDataMigrationSQLScript.sql
create mode 100644 bootstrap/sql/migrations/native/1.5.15/mysql/schemaChanges.sql
create mode 100644 bootstrap/sql/migrations/native/1.5.15/postgres/postDataMigrationSQLScript.sql
create mode 100644 bootstrap/sql/migrations/native/1.5.15/postgres/schemaChanges.sql
diff --git a/bootstrap/sql/migrations/native/1.5.15/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.5.15/mysql/postDataMigrationSQLScript.sql
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/bootstrap/sql/migrations/native/1.5.15/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.5.15/mysql/schemaChanges.sql
new file mode 100644
index 000000000000..19762625a9d8
--- /dev/null
+++ b/bootstrap/sql/migrations/native/1.5.15/mysql/schemaChanges.sql
@@ -0,0 +1,5 @@
+-- Make domain policy and role non-system
+UPDATE policy_entity SET json = JSON_SET(json, '$.provider', 'user') where name = 'DomainOnlyAccessPolicy';
+UPDATE policy_entity SET json = JSON_SET(json, '$.allowDelete', true) where name = 'DomainOnlyAccessPolicy';
+UPDATE role_entity SET json = JSON_SET(json, '$.provider', 'user') where name = 'DomainOnlyAccessRole';
+UPDATE role_entity SET json = JSON_SET(json, '$.allowDelete', true) where name = 'DomainOnlyAccessRole';
\ No newline at end of file
diff --git a/bootstrap/sql/migrations/native/1.5.15/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.5.15/postgres/postDataMigrationSQLScript.sql
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/bootstrap/sql/migrations/native/1.5.15/postgres/schemaChanges.sql b/bootstrap/sql/migrations/native/1.5.15/postgres/schemaChanges.sql
new file mode 100644
index 000000000000..6f92fbea754c
--- /dev/null
+++ b/bootstrap/sql/migrations/native/1.5.15/postgres/schemaChanges.sql
@@ -0,0 +1,5 @@
+-- Make domain policy and role non-system
+UPDATE policy_entity SET json = JSONB_SET(json::jsonb, '{provider}', '"user"', true) where name = 'DomainOnlyAccessPolicy';
+UPDATE policy_entity SET json = JSONB_SET(json::jsonb, '{allowDelete}', 'true', true) WHERE name = 'DomainOnlyAccessPolicy';
+UPDATE role_entity SET json = JSONB_SET(json::jsonb, '{provider}', '"user"', true) where name = 'DomainOnlyAccessRole';
+UPDATE role_entity SET json = JSONB_SET(json::jsonb, '{allowDelete}', 'true', true) WHERE name = 'DomainOnlyAccessRole';
diff --git a/openmetadata-service/src/main/resources/json/data/policy/DomainAccessPolicy.json b/openmetadata-service/src/main/resources/json/data/policy/DomainAccessPolicy.json
index 572760b5ef01..d103fff85265 100644
--- a/openmetadata-service/src/main/resources/json/data/policy/DomainAccessPolicy.json
+++ b/openmetadata-service/src/main/resources/json/data/policy/DomainAccessPolicy.json
@@ -4,8 +4,8 @@
"fullyQualifiedName": "DomainOnlyAccessPolicy",
"description": "This Policy adds restrictions so that users will have access to domain related data. If the user has some domain, then he will be able to access data only for that domain. If the user does not have any domain assigned , he will be able to access only assets which also does not have any domain.",
"enabled": true,
- "allowDelete": false,
- "provider": "system",
+ "allowDelete": true,
+ "provider": "user",
"rules": [
{
"name": "DomainOnlyAccessRule",
diff --git a/openmetadata-service/src/main/resources/json/data/role/DomainOnlyAccessRole.json b/openmetadata-service/src/main/resources/json/data/role/DomainOnlyAccessRole.json
index b18aeae18424..ec770210e4da 100644
--- a/openmetadata-service/src/main/resources/json/data/role/DomainOnlyAccessRole.json
+++ b/openmetadata-service/src/main/resources/json/data/role/DomainOnlyAccessRole.json
@@ -2,8 +2,8 @@
"name": "DomainOnlyAccessRole",
"displayName": "Domain Only Access Role",
"description": "Role Corresponding to Domain Access Restriction.",
- "allowDelete": false,
- "provider": "system",
+ "allowDelete": true,
+ "provider": "user",
"policies" : [
{
"type" : "policy",
From c75d453154ab91916ccdf477444a14c3b2cc9f72 Mon Sep 17 00:00:00 2001
From: sonika-shah <58761340+sonika-shah@users.noreply.github.com>
Date: Mon, 16 Dec 2024 15:14:29 +0530
Subject: [PATCH 011/294] fix: Resolve conflicts
---
.../main/java/org/openmetadata/service/jdbi3/CollectionDAO.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
index 809a53e5d6b4..a423db901cd3 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
@@ -4152,7 +4152,7 @@ List listWithoutEntityFilter(
@Bind("eventType") String eventType, @Bind("timestamp") long timestamp);
@SqlQuery(
- "SELECT json FROM change_event ce where ce.offset > :offset ORDER BY ce.eventTime ASC LIMIT :limit")
+ "SELECT json FROM change_event ce WHERE ce.offset > :offset ORDER BY ce.offset ASC LIMIT :limit")
List list(@Bind("limit") long limit, @Bind("offset") long offset);
@ConnectionAwareSqlQuery(value = "SELECT MAX(offset) FROM change_event", connectionType = MYSQL)
From db2770fbb48a2da9ec559e9d0ca23baa6c6a79aa Mon Sep 17 00:00:00 2001
From: Siddhant <86899184+Siddhanttimeline@users.noreply.github.com>
Date: Mon, 16 Dec 2024 16:07:56 +0530
Subject: [PATCH 012/294] Fixes 19073 : Resolve NullPointerException in MS
Teams DQ template handling (#19062)
* fix: Resolve NullPointerException in MS Teams DQ template handling
* fix: Update DQ template logic to handle test case changes and results
Details:
Ensures a general template is created for any changes in the test case.
Generates a DQ template when the test case result is updated.
* refactor: Make testCaseResult as constant named TEST_CASE_RESULT
---
.../decorators/MSTeamsMessageDecorator.java | 51 +++++++------------
1 file changed, 19 insertions(+), 32 deletions(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/formatter/decorators/MSTeamsMessageDecorator.java b/openmetadata-service/src/main/java/org/openmetadata/service/formatter/decorators/MSTeamsMessageDecorator.java
index f1916c2be585..f16b031f3d4a 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/formatter/decorators/MSTeamsMessageDecorator.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/formatter/decorators/MSTeamsMessageDecorator.java
@@ -29,6 +29,7 @@
import org.openmetadata.schema.tests.TestCaseParameterValue;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityReference;
+import org.openmetadata.schema.type.FieldChange;
import org.openmetadata.schema.type.TagLabel;
import org.openmetadata.service.Entity;
import org.openmetadata.service.apps.bundles.changeEvent.msteams.TeamsMessage;
@@ -41,6 +42,7 @@
import org.openmetadata.service.exception.UnhandledServerException;
public class MSTeamsMessageDecorator implements MessageDecorator {
+ private static final String TEST_CASE_RESULT = "testCaseResult";
@Override
public String getBold() {
@@ -115,11 +117,25 @@ private TeamsMessage createMessage(
String entityType = event.getEntityType();
return switch (entityType) {
- case Entity.TEST_CASE -> createDQMessage(publisherName, event, outgoingMessage);
+ case Entity.TEST_CASE -> createTestCaseMessage(publisherName, event, outgoingMessage);
default -> createGeneralChangeEventMessage(publisherName, event, outgoingMessage);
};
}
+ private TeamsMessage createTestCaseMessage(
+ String publisherName, ChangeEvent event, OutgoingMessage outgoingMessage) {
+ List fieldsAdded = event.getChangeDescription().getFieldsAdded();
+ List fieldsUpdated = event.getChangeDescription().getFieldsUpdated();
+
+ boolean hasRelevantChange =
+ fieldsAdded.stream().anyMatch(field -> TEST_CASE_RESULT.equals(field.getName()))
+ || fieldsUpdated.stream().anyMatch(field -> TEST_CASE_RESULT.equals(field.getName()));
+
+ return hasRelevantChange
+ ? createDQMessage(event, outgoingMessage)
+ : createGeneralChangeEventMessage(publisherName, event, outgoingMessage);
+ }
+
private TeamsMessage createGeneralChangeEventMessage(
String publisherName, ChangeEvent event, OutgoingMessage outgoingMessage) {
@@ -173,11 +189,9 @@ private TeamsMessage createGeneralChangeEventMessage(
return TeamsMessage.builder().type("message").attachments(List.of(attachment)).build();
}
- private TeamsMessage createDQMessage(
- String publisherName, ChangeEvent event, OutgoingMessage outgoingMessage) {
-
+ private TeamsMessage createDQMessage(ChangeEvent event, OutgoingMessage outgoingMessage) {
Map, Object>> dqTemplateData =
- buildDQTemplateData(publisherName, event, outgoingMessage);
+ MessageDecorator.buildDQTemplateData(event, outgoingMessage);
TextBlock changeEventDetailsTextBlock = createHeader();
@@ -569,33 +583,6 @@ private Map, Object>> buildGeneralTemplate
return builder.build();
}
- // todo - complete buildDQTemplateData fn
- private Map, Object>> buildDQTemplateData(
- String publisherName, ChangeEvent event, OutgoingMessage outgoingMessage) {
-
- TemplateDataBuilder builder = new TemplateDataBuilder<>();
-
- // Use DQ_Template_Section directly
- builder
- .add(
- DQ_Template_Section.EVENT_DETAILS,
- EventDetailsKeys.EVENT_TYPE,
- event.getEventType().value())
- .add(DQ_Template_Section.EVENT_DETAILS, EventDetailsKeys.UPDATED_BY, event.getUserName())
- .add(DQ_Template_Section.EVENT_DETAILS, EventDetailsKeys.ENTITY_TYPE, event.getEntityType())
- .add(
- DQ_Template_Section.EVENT_DETAILS,
- EventDetailsKeys.ENTITY_FQN,
- MessageDecorator.getFQNForChangeEventEntity(event))
- .add(
- DQ_Template_Section.EVENT_DETAILS,
- EventDetailsKeys.TIME,
- new Date(event.getTimestamp()).toString())
- .add(DQ_Template_Section.EVENT_DETAILS, EventDetailsKeys.OUTGOING_MESSAGE, outgoingMessage);
-
- return builder.build();
- }
-
private TextBlock createHeader() {
return TextBlock.builder()
.type("TextBlock")
From cf1c737300ac1e399f263d8a1cd91263500bfd57 Mon Sep 17 00:00:00 2001
From: Siddhant <86899184+Siddhanttimeline@users.noreply.github.com>
Date: Thu, 12 Dec 2024 15:24:01 +0530
Subject: [PATCH 013/294] Fix: Filter activity feed events for test cases and
test suites. (#19004)
* fix: Filter activity feed events for test cases and suites.
* minor fix around the button label after save task tab
---------
Co-authored-by: Ashish Gupta
---
.../json/data/eventsubscription/ActivityFeedEvents.json | 2 +-
.../components/Entity/Task/TaskTab/TaskTab.component.tsx | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/openmetadata-service/src/main/resources/json/data/eventsubscription/ActivityFeedEvents.json b/openmetadata-service/src/main/resources/json/data/eventsubscription/ActivityFeedEvents.json
index ecdaeba28b41..4db1f4b296bf 100644
--- a/openmetadata-service/src/main/resources/json/data/eventsubscription/ActivityFeedEvents.json
+++ b/openmetadata-service/src/main/resources/json/data/eventsubscription/ActivityFeedEvents.json
@@ -10,7 +10,7 @@
{
"name": "matchAnyEventType",
"effect": "include",
- "condition": "matchAnyEventType({'entityCreated', 'entityDeleted', 'entitySoftDeleted'}) && !matchUpdatedBy({'ingestion-bot'}) && !matchAnySource({'user', 'team', 'owners', 'databaseService', 'messagingService', 'dashboardService', 'pipelineService', 'storageService', 'mlmodelService', 'metadataService', 'searchService', 'apiService', 'ingestionPipeline', 'workflow'})",
+ "condition": "matchAnyEventType({'entityCreated', 'entityDeleted', 'entitySoftDeleted'}) && !matchUpdatedBy({'ingestion-bot'}) && !matchAnySource({'user', 'team', 'owners', 'databaseService', 'messagingService', 'dashboardService', 'pipelineService', 'storageService', 'mlmodelService', 'metadataService', 'searchService', 'apiService', 'ingestionPipeline', 'workflow', 'testCase', 'testSuite'})",
"prefixCondition": "AND"
},
{
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTab.component.tsx
index 4e6cd1a724cb..0549abd69000 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTab.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTab.component.tsx
@@ -431,8 +431,11 @@ export const TaskTab = ({
onTaskResolve();
}
setTaskAction(
- TASK_ACTION_LIST.find((action) => action.key === info.key) ??
- TASK_ACTION_LIST[0]
+ [
+ ...TASK_ACTION_LIST,
+ ...GLOSSARY_TASK_ACTION_LIST,
+ ...INCIDENT_TASK_ACTION_LIST,
+ ].find((action) => action.key === info.key) ?? TASK_ACTION_LIST[0]
);
};
From d150061908eab2b7a82014ca3c65e45ace343f8c Mon Sep 17 00:00:00 2001
From: Mohit Yadav <105265192+mohityadav766@users.noreply.github.com>
Date: Tue, 17 Dec 2024 23:43:07 +0530
Subject: [PATCH 014/294] Fix Login Configuration Issue (#19109)
* Fix Login Configuration Issue
* Fix Tests
(cherry picked from commit 4b9948dbfbbfcacea997a72df7442b11430a1bb2)
---
.../exception/CatalogExceptionMessage.java | 2 +-
.../service/jdbi3/SystemRepository.java | 5 +++++
.../resources/settings/SettingsCache.java | 2 +-
.../security/auth/BasicAuthenticator.java | 14 ++++++--------
.../service/security/auth/LdapAuthenticator.java | 8 +++-----
.../service/security/auth/LoginAttemptCache.java | 16 +++++++++++++++-
.../resources/system/ConfigResourceTest.java | 2 +-
.../resources/system/SystemResourceTest.java | 2 +-
8 files changed, 33 insertions(+), 18 deletions(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogExceptionMessage.java b/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogExceptionMessage.java
index 7935488bd8fd..223f2a518961 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogExceptionMessage.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogExceptionMessage.java
@@ -35,7 +35,7 @@ public final class CatalogExceptionMessage {
public static final String PASSWORD_INVALID_FORMAT =
"Password must be of minimum 8 characters, with one special, one Upper, one lower case character, and one Digit.";
public static final String MAX_FAILED_LOGIN_ATTEMPT =
- "Failed Login Attempts Exceeded. Please try after some time.";
+ "Failed Login Attempts Exceeded. Use Forgot Password or retry after some time.";
public static final String INCORRECT_OLD_PASSWORD = "INCORRECT_OLD_PASSWORD";
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
index 302882eb0c7d..01b7d76d12f9 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java
@@ -43,6 +43,7 @@
import org.openmetadata.service.secrets.SecretsManager;
import org.openmetadata.service.secrets.SecretsManagerFactory;
import org.openmetadata.service.security.JwtFilter;
+import org.openmetadata.service.security.auth.LoginAttemptCache;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.OpenMetadataConnectionBuilder;
import org.openmetadata.service.util.RestUtil;
@@ -249,6 +250,10 @@ private void postUpdate(SettingsType settingsType) {
WorkflowHandler workflowHandler = WorkflowHandler.getInstance();
workflowHandler.initializeNewProcessEngine(workflowHandler.getProcessEngineConfiguration());
}
+
+ if (settingsType == SettingsType.LOGIN_CONFIGURATION) {
+ LoginAttemptCache.updateLoginConfiguration();
+ }
}
public void updateSetting(Settings setting) {
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java
index 16532c563ce7..e68c3b891ae7 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java
@@ -118,7 +118,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
.withConfigValue(
new LoginConfiguration()
.withMaxLoginFailAttempts(3)
- .withAccessBlockTime(600)
+ .withAccessBlockTime(30)
.withJwtTokenExpiryTime(3600));
systemRepository.createNewSetting(setting);
}
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java
index a99a7eeab87a..9d33d87927d1 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java
@@ -102,7 +102,6 @@ public class BasicAuthenticator implements AuthenticatorHandler {
private static final int HASHING_COST = 12;
private UserRepository userRepository;
private TokenRepository tokenRepository;
- private LoginAttemptCache loginAttemptCache;
private AuthorizerConfiguration authorizerConfiguration;
private boolean isSelfSignUpAvailable;
@@ -111,7 +110,6 @@ public void init(OpenMetadataApplicationConfig config) {
this.userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER);
this.tokenRepository = Entity.getTokenRepository();
this.authorizerConfiguration = config.getAuthorizerConfiguration();
- this.loginAttemptCache = new LoginAttemptCache();
this.isSelfSignUpAvailable = config.getAuthenticationConfiguration().getEnableSelfSignup();
}
@@ -267,7 +265,7 @@ public void resetUserPasswordWithToken(UriInfo uriInfo, PasswordResetRequest req
LOG.error("Error in sending Password Change Mail to User. Reason : " + ex.getMessage(), ex);
throw new CustomExceptionMessage(424, FAILED_SEND_EMAIL, EMAIL_SENDING_ISSUE);
}
- loginAttemptCache.recordSuccessfulLogin(request.getUsername());
+ LoginAttemptCache.getInstance().recordSuccessfulLogin(request.getUsername());
}
@Override
@@ -312,7 +310,7 @@ public void changeUserPwdWithOldPwd(
storedUser.getAuthenticationMechanism().setConfig(storedBasicAuthMechanism);
PutResponse response = userRepository.createOrUpdate(uriInfo, storedUser);
// remove login/details from cache
- loginAttemptCache.recordSuccessfulLogin(userName);
+ LoginAttemptCache.getInstance().recordSuccessfulLogin(userName);
// in case admin updates , send email to user
if (request.getRequestType() == USER && getSmtpSettings().getEnableSmtpServer()) {
@@ -476,7 +474,7 @@ public JwtResponse loginUser(LoginRequest loginRequest) throws IOException, Temp
@Override
public void checkIfLoginBlocked(String email) {
- if (loginAttemptCache.isLoginBlocked(email)) {
+ if (LoginAttemptCache.getInstance().isLoginBlocked(email)) {
throw new AuthenticationException(MAX_FAILED_LOGIN_ATTEMPT);
}
}
@@ -484,15 +482,15 @@ public void checkIfLoginBlocked(String email) {
@Override
public void recordFailedLoginAttempt(String email, String userName)
throws TemplateException, IOException {
- loginAttemptCache.recordFailedLogin(email);
- int failedLoginAttempt = loginAttemptCache.getUserFailedLoginCount(email);
+ LoginAttemptCache.getInstance().recordFailedLogin(email);
+ int failedLoginAttempt = LoginAttemptCache.getInstance().getUserFailedLoginCount(email);
if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) {
sendAccountStatus(
userName,
email,
"Multiple Failed Login Attempts.",
String.format(
- "Someone is trying to access your account. Login is Blocked for %s minutes. Please change your password.",
+ "Someone is trying to access your account. Login is Blocked for %s seconds. Please change your password.",
SecurityUtil.getLoginConfiguration().getAccessBlockTime()));
}
}
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LdapAuthenticator.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LdapAuthenticator.java
index 9531d76c1f97..d608434d7b2c 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LdapAuthenticator.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LdapAuthenticator.java
@@ -84,7 +84,6 @@ public class LdapAuthenticator implements AuthenticatorHandler {
private RoleRepository roleRepository;
private UserRepository userRepository;
private TokenRepository tokenRepository;
- private LoginAttemptCache loginAttemptCache;
private LdapConfiguration ldapConfiguration;
private LDAPConnectionPool ldapLookupConnectionPool;
private boolean isSelfSignUpEnabled;
@@ -102,7 +101,6 @@ public void init(OpenMetadataApplicationConfig config) {
this.roleRepository = (RoleRepository) Entity.getEntityRepository(Entity.ROLE);
this.tokenRepository = Entity.getTokenRepository();
this.ldapConfiguration = config.getAuthenticationConfiguration().getLdapConfiguration();
- this.loginAttemptCache = new LoginAttemptCache();
this.isSelfSignUpEnabled = config.getAuthenticationConfiguration().getEnableSelfSignup();
}
@@ -176,7 +174,7 @@ private User checkAndCreateUser(String userDn, String email, String userName) th
@Override
public void checkIfLoginBlocked(String email) {
- if (loginAttemptCache.isLoginBlocked(email)) {
+ if (LoginAttemptCache.getInstance().isLoginBlocked(email)) {
throw new AuthenticationException(MAX_FAILED_LOGIN_ATTEMPT);
}
}
@@ -184,8 +182,8 @@ public void checkIfLoginBlocked(String email) {
@Override
public void recordFailedLoginAttempt(String email, String userName)
throws TemplateException, IOException {
- loginAttemptCache.recordFailedLogin(email);
- int failedLoginAttempt = loginAttemptCache.getUserFailedLoginCount(email);
+ LoginAttemptCache.getInstance().recordFailedLogin(email);
+ int failedLoginAttempt = LoginAttemptCache.getInstance().getUserFailedLoginCount(email);
if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) {
EmailUtil.sendAccountStatus(
userName,
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LoginAttemptCache.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LoginAttemptCache.java
index 0ceb672d098c..336b97e473d1 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LoginAttemptCache.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LoginAttemptCache.java
@@ -3,6 +3,7 @@
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import io.dropwizard.logback.shaded.guava.annotations.VisibleForTesting;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import lombok.NonNull;
@@ -12,10 +13,11 @@
import org.openmetadata.service.resources.settings.SettingsCache;
public class LoginAttemptCache {
+ private static LoginAttemptCache INSTANCE;
private int maxAttempt = 3;
private final LoadingCache attemptsCache;
- public LoginAttemptCache() {
+ private LoginAttemptCache() {
LoginConfiguration loginConfiguration =
SettingsCache.getSetting(SettingsType.LOGIN_CONFIGURATION, LoginConfiguration.class);
long accessBlockTime = 600;
@@ -35,6 +37,18 @@ public LoginAttemptCache() {
});
}
+ public static LoginAttemptCache getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new LoginAttemptCache();
+ }
+ return INSTANCE;
+ }
+
+ public static void updateLoginConfiguration() {
+ INSTANCE = new LoginAttemptCache();
+ }
+
+ @VisibleForTesting
public LoginAttemptCache(int maxAttempt, int blockTimeInSec) {
this.maxAttempt = maxAttempt;
attemptsCache =
diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/ConfigResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/ConfigResourceTest.java
index 9cf314ed1755..9a80814133ef 100644
--- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/ConfigResourceTest.java
+++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/ConfigResourceTest.java
@@ -124,7 +124,7 @@ void get_Login_Configuration_200_OK() throws IOException {
LoginConfiguration loginConfiguration =
TestUtils.get(target, LoginConfiguration.class, TEST_AUTH_HEADERS);
assertEquals(3, loginConfiguration.getMaxLoginFailAttempts());
- assertEquals(600, loginConfiguration.getAccessBlockTime());
+ assertEquals(30, loginConfiguration.getAccessBlockTime());
assertEquals(3600, loginConfiguration.getJwtTokenExpiryTime());
}
diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java
index 1cf64c31e5ea..686de7c1bd3a 100644
--- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java
+++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java
@@ -436,7 +436,7 @@ void testLoginConfigurationSettings() throws HttpResponseException {
// Assert default values
assertEquals(3, loginConfig.getMaxLoginFailAttempts());
- assertEquals(600, loginConfig.getAccessBlockTime());
+ assertEquals(30, loginConfig.getAccessBlockTime());
assertEquals(3600, loginConfig.getJwtTokenExpiryTime());
// Update login configuration
From 407c0c9547cb2374c56c8a52aba4fc633b52f031 Mon Sep 17 00:00:00 2001
From: Ashish Gupta
Date: Fri, 13 Dec 2024 23:41:40 +0530
Subject: [PATCH 015/294] supported editUser permission in user tab for team
page (#18987)
* supported editUser permission in user tab for team page
* remove edit all permission check in teams add/remove user api
* added playwright test for the editUser permission
* Added playwright test for data consumer user and remove no used field from the advance api call
---------
Co-authored-by: sonikashah
Co-authored-by: sonika-shah <58761340+sonika-shah@users.noreply.github.com>
(cherry picked from commit 9e6078f654ed0a54e9d41b12c3a748449c6079ea)
---
.../service/resources/teams/TeamResource.java | 9 -
.../ui/playwright/constant/permission.ts | 9 +
.../ui/playwright/e2e/Pages/Teams.spec.ts | 235 +++++++++++++++++-
.../ui/playwright/support/team/TeamClass.ts | 27 +-
.../resources/ui/playwright/utils/team.ts | 37 +++
.../TeamDetails/UserTab/UserTab.component.tsx | 19 +-
.../ui/src/pages/TeamsPage/TeamsPage.test.tsx | 1 -
.../ui/src/pages/TeamsPage/TeamsPage.tsx | 1 -
8 files changed, 315 insertions(+), 23 deletions(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java
index c30c3178bb25..6f0070dff28a 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java
@@ -72,7 +72,6 @@
import org.openmetadata.service.resources.Collection;
import org.openmetadata.service.resources.EntityResource;
import org.openmetadata.service.security.Authorizer;
-import org.openmetadata.service.security.policyevaluator.OperationContext;
import org.openmetadata.service.util.CSVExportResponse;
import org.openmetadata.service.util.EntityUtil;
import org.openmetadata.service.util.JsonUtils;
@@ -687,10 +686,6 @@ public Response updateTeamUsers(
@Context SecurityContext securityContext,
@PathParam("teamId") UUID teamId,
List users) {
-
- OperationContext operationContext =
- new OperationContext(entityType, MetadataOperation.EDIT_ALL);
- authorizer.authorize(securityContext, operationContext, getResourceContextById(teamId));
return repository
.updateTeamUsers(securityContext.getUserPrincipal().getName(), teamId, users)
.toResponse();
@@ -721,10 +716,6 @@ public Response deleteTeamUser(
@Parameter(description = "Id of the user being removed", schema = @Schema(type = "string"))
@PathParam("userId")
String userId) {
-
- OperationContext operationContext =
- new OperationContext(entityType, MetadataOperation.EDIT_ALL);
- authorizer.authorize(securityContext, operationContext, getResourceContextById(teamId));
return repository
.deleteTeamUser(
securityContext.getUserPrincipal().getName(), teamId, UUID.fromString(userId))
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/permission.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/permission.ts
index b4dccdce8030..7c810b833c70 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/constant/permission.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/permission.ts
@@ -77,6 +77,15 @@ export const DATA_CONSUMER_RULES: PolicyRulesType[] = [
},
];
+export const EDIT_USER_FOR_TEAM_RULES: PolicyRulesType[] = [
+ {
+ name: 'EditUserTeams-EditRule',
+ resources: ['team'],
+ operations: ['EditUsers'],
+ effect: 'allow',
+ },
+];
+
export const ORGANIZATION_POLICY_RULES: PolicyRulesType[] = [
{
name: 'OrganizationPolicy-NoOwner-Rule',
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Teams.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Teams.spec.ts
index 2436b924d16c..0e8a26acec81 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Teams.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Teams.spec.ts
@@ -10,12 +10,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import test, { expect } from '@playwright/test';
+import { expect, Page, test as base } from '@playwright/test';
+import { EDIT_USER_FOR_TEAM_RULES } from '../../constant/permission';
import { GlobalSettingOptions } from '../../constant/settings';
+import { PolicyClass } from '../../support/access-control/PoliciesClass';
+import { RolesClass } from '../../support/access-control/RolesClass';
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
import { TableClass } from '../../support/entity/TableClass';
import { TeamClass } from '../../support/team/TeamClass';
import { UserClass } from '../../support/user/UserClass';
+import { performAdminLogin } from '../../utils/admin';
import {
createNewPage,
descriptionBox,
@@ -28,6 +32,7 @@ import { addMultiOwner } from '../../utils/entity';
import { settingClick } from '../../utils/sidebar';
import {
addTeamOwnerToEntity,
+ addUserInTeam,
createTeam,
hardDeleteTeam,
searchTeam,
@@ -35,10 +40,15 @@ import {
verifyAssetsInTeamsPage,
} from '../../utils/team';
-// use the admin user to login
-test.use({ storageState: 'playwright/.auth/admin.json' });
-
+const id = uuid();
+const dataConsumerUser = new UserClass();
+const editOnlyUser = new UserClass(); // this user will have only editUser permission in team
+let team = new TeamClass();
+const team2 = new TeamClass();
+const policy = new PolicyClass();
+const role = new RolesClass();
const user = new UserClass();
+const user2 = new UserClass();
const userName = user.data.email.split('@')[0];
let teamDetails: {
@@ -55,7 +65,28 @@ let teamDetails: {
updatedEmail: `pwteamUpdated${uuid()}@example.com`,
};
+const test = base.extend<{
+ editOnlyUserPage: Page;
+ dataConsumerPage: Page;
+}>({
+ editOnlyUserPage: async ({ browser }, use) => {
+ const page = await browser.newPage();
+ await editOnlyUser.login(page);
+ await use(page);
+ await page.close();
+ },
+ dataConsumerPage: async ({ browser }, use) => {
+ const page = await browser.newPage();
+ await dataConsumerUser.login(page);
+ await use(page);
+ await page.close();
+ },
+});
+
test.describe('Teams Page', () => {
+ // use the admin user to login
+ test.use({ storageState: 'playwright/.auth/admin.json' });
+
test.slow(true);
test.beforeAll('Setup pre-requests', async ({ browser }) => {
@@ -636,3 +667,199 @@ test.describe('Teams Page', () => {
await afterAction();
});
});
+
+test.describe('Teams Page with EditUser Permission', () => {
+ test.slow(true);
+
+ test.beforeAll('Setup pre-requests', async ({ browser }) => {
+ const { apiContext, afterAction } = await performAdminLogin(browser);
+ await editOnlyUser.create(apiContext);
+
+ const id = uuid();
+ await policy.create(apiContext, EDIT_USER_FOR_TEAM_RULES);
+ await role.create(apiContext, [policy.responseData.name]);
+
+ team = new TeamClass({
+ name: `PW%edit-user-team-${id}`,
+ displayName: `PW Edit User Team ${id}`,
+ description: 'playwright edit user team description',
+ teamType: 'Group',
+ users: [editOnlyUser.responseData.id],
+ defaultRoles: role.responseData.id ? [role.responseData.id] : [],
+ });
+ await team.create(apiContext);
+ await team2.create(apiContext);
+ await user.create(apiContext);
+ await user2.create(apiContext);
+ await afterAction();
+ });
+
+ test.afterAll('Cleanup', async ({ browser }) => {
+ const { apiContext, afterAction } = await performAdminLogin(browser);
+ await user.delete(apiContext);
+ await user2.delete(apiContext);
+ await editOnlyUser.delete(apiContext);
+ await team.delete(apiContext);
+ await team2.delete(apiContext);
+ await policy.delete(apiContext);
+ await role.delete(apiContext);
+ await afterAction();
+ });
+
+ test.beforeEach('Visit Home Page', async ({ editOnlyUserPage }) => {
+ await redirectToHomePage(editOnlyUserPage);
+ await team2.visitTeamPage(editOnlyUserPage);
+ });
+
+ test('Add and Remove User for Team', async ({ editOnlyUserPage }) => {
+ await test.step('Add user in Team from the placeholder', async () => {
+ await addUserInTeam(editOnlyUserPage, user);
+ });
+
+ await test.step('Add user in Team for the header manage area', async () => {
+ await addUserInTeam(editOnlyUserPage, user2);
+ });
+
+ await test.step('Remove user from Team', async () => {
+ await editOnlyUserPage
+ .getByRole('row', {
+ name: `${user.data.firstName.slice(0, 1).toUpperCase()} ${
+ user.data.firstName
+ }.`,
+ })
+ .getByTestId('remove-user-btn')
+ .click();
+
+ const userResponse = editOnlyUserPage.waitForResponse(
+ '/api/v1/users?fields=**'
+ );
+ await editOnlyUserPage.getByRole('button', { name: 'Confirm' }).click();
+ await userResponse;
+
+ await expect(
+ editOnlyUserPage.locator(`[data-testid="${userName.toLowerCase()}"]`)
+ ).not.toBeVisible();
+ });
+ });
+});
+
+test.describe('Teams Page with Data Consumer User', () => {
+ test.slow(true);
+
+ test.beforeAll('Setup pre-requests', async ({ browser }) => {
+ const { apiContext, afterAction } = await performAdminLogin(browser);
+ await dataConsumerUser.create(apiContext);
+ await user.create(apiContext);
+ await policy.create(apiContext, EDIT_USER_FOR_TEAM_RULES);
+ await role.create(apiContext, [policy.responseData.name]);
+
+ team = new TeamClass({
+ name: `PW%-data-consumer-team-${id}`,
+ displayName: `PW Data Consumer Team ${id}`,
+ description: 'playwright data consumer team description',
+ teamType: 'Group',
+ users: [user.responseData.id],
+ defaultRoles: role.responseData.id ? [role.responseData.id] : [],
+ });
+ await team.create(apiContext);
+ await team2.create(apiContext);
+ await afterAction();
+ });
+
+ test.afterAll('Cleanup', async ({ browser }) => {
+ const { apiContext, afterAction } = await performAdminLogin(browser);
+ await dataConsumerUser.delete(apiContext);
+ await user.delete(apiContext);
+ await team.delete(apiContext);
+ await team2.delete(apiContext);
+ await afterAction();
+ });
+
+ test.beforeEach('Visit Home Page', async ({ dataConsumerPage }) => {
+ await redirectToHomePage(dataConsumerPage);
+ });
+
+ test('Should not have edit access on team page with no data available', async ({
+ dataConsumerPage,
+ }) => {
+ await team2.visitTeamPage(dataConsumerPage);
+
+ await expect(
+ dataConsumerPage.getByTestId('edit-team-name')
+ ).not.toBeVisible();
+ await expect(dataConsumerPage.getByTestId('add-domain')).not.toBeVisible();
+ await expect(dataConsumerPage.getByTestId('edit-owner')).not.toBeVisible();
+ await expect(dataConsumerPage.getByTestId('edit-email')).not.toBeVisible();
+ await expect(
+ dataConsumerPage.getByTestId('edit-team-subscription')
+ ).not.toBeVisible();
+ await expect(
+ dataConsumerPage.getByTestId('manage-button')
+ ).not.toBeVisible();
+
+ await expect(dataConsumerPage.getByTestId('join-teams')).toBeVisible();
+
+ // User Tab
+ await expect(
+ dataConsumerPage.getByTestId('add-new-user')
+ ).not.toBeVisible();
+ await expect(
+ dataConsumerPage.getByTestId('permission-error-placeholder')
+ ).toBeVisible();
+
+ // Asset Tab
+ const assetResponse = dataConsumerPage.waitForResponse(
+ '/api/v1/search/query?**'
+ );
+ await dataConsumerPage.getByTestId('assets').click();
+ await assetResponse;
+
+ await expect(
+ dataConsumerPage.getByTestId('add-placeholder-button')
+ ).not.toBeVisible();
+ await expect(
+ dataConsumerPage.getByTestId('no-data-placeholder')
+ ).toBeVisible();
+
+ // Role Tab
+ await dataConsumerPage.getByTestId('roles').click();
+
+ await expect(
+ dataConsumerPage.getByTestId('add-placeholder-button')
+ ).not.toBeVisible();
+ await expect(
+ dataConsumerPage.getByTestId('permission-error-placeholder')
+ ).toBeVisible();
+
+ // Policies Tab
+ await dataConsumerPage.getByTestId('policies').click();
+
+ await expect(
+ dataConsumerPage.getByTestId('add-placeholder-button')
+ ).not.toBeVisible();
+ await expect(
+ dataConsumerPage.getByTestId('permission-error-placeholder')
+ ).toBeVisible();
+ });
+
+ test('Should not have edit access on team page with data available', async ({
+ dataConsumerPage,
+ }) => {
+ await team.visitTeamPage(dataConsumerPage);
+
+ // User Tab
+ await expect(
+ dataConsumerPage.getByTestId('add-new-user')
+ ).not.toBeVisible();
+
+ // Role Tab
+ await dataConsumerPage.getByTestId('roles').click();
+
+ await expect(dataConsumerPage.getByTestId('add-role')).not.toBeVisible();
+
+ // Policies Tab
+ await dataConsumerPage.getByTestId('policies').click();
+
+ await expect(dataConsumerPage.getByTestId('add-policy')).not.toBeVisible();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/team/TeamClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/team/TeamClass.ts
index d580c59c8b7d..a6553bc4eda0 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/support/team/TeamClass.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/support/team/TeamClass.ts
@@ -10,8 +10,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { APIRequestContext } from '@playwright/test';
+import { APIRequestContext, expect, Page } from '@playwright/test';
+import { GlobalSettingOptions } from '../../constant/settings';
import { uuid } from '../../utils/common';
+import { settingClick } from '../../utils/sidebar';
+import { searchTeam } from '../../utils/team';
type ResponseDataType = {
name: string;
displayName: string;
@@ -46,6 +49,28 @@ export class TeamClass {
return this.responseData;
}
+ async visitTeamPage(page: Page) {
+ // complete url since we are making basic and advance call to get the details of the team
+ const fetchOrganizationResponse = page.waitForResponse(
+ `/api/v1/teams/name/Organization?fields=users%2CdefaultRoles%2Cpolicies%2CchildrenCount%2Cdomains&include=all`
+ );
+ await settingClick(page, GlobalSettingOptions.TEAMS);
+ await fetchOrganizationResponse;
+
+ await searchTeam(page, this.responseData?.['displayName']);
+
+ await page
+ .locator(`[data-row-key="${this.data.name}"]`)
+ .getByRole('link')
+ .click();
+
+ await page.waitForLoadState('networkidle');
+
+ await expect(page.getByTestId('team-heading')).toHaveText(
+ this.data.displayName
+ );
+ }
+
async create(apiContext: APIRequestContext) {
const response = await apiContext.post('/api/v1/teams', {
data: this.data,
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts
index 382f5c2f0146..f77452977e78 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts
@@ -13,6 +13,7 @@
import { APIRequestContext, expect, Page } from '@playwright/test';
import { TableClass } from '../support/entity/TableClass';
import { TeamClass } from '../support/team/TeamClass';
+import { UserClass } from '../support/user/UserClass';
import { descriptionBox, toastNotification, uuid } from './common';
import { addOwner } from './entity';
import { validateFormNameFieldInput } from './form';
@@ -316,3 +317,39 @@ export const verifyAssetsInTeamsPage = async (
page.getByTestId('assets').getByTestId('filter-count')
).toContainText(assetCount.toString());
};
+
+export const addUserInTeam = async (page: Page, user: UserClass) => {
+ const userName = user.data.email.split('@')[0];
+ const fetchUsersResponse = page.waitForResponse(
+ '/api/v1/users?limit=25&isBot=false'
+ );
+ await page.locator('[data-testid="add-new-user"]').click();
+ await fetchUsersResponse;
+
+ // Search and select the user
+ await page
+ .locator('[data-testid="selectable-list"] [data-testid="searchbar"]')
+ .fill(user.getUserName());
+
+ await page
+ .locator(`[data-testid="selectable-list"] [title="${user.getUserName()}"]`)
+ .click();
+
+ await expect(
+ page.locator(
+ `[data-testid="selectable-list"] [title="${user.getUserName()}"]`
+ )
+ ).toHaveClass(/active/);
+
+ const updateTeamResponse = page.waitForResponse('/api/v1/users*');
+
+ // Update the team with the new user
+ await page.locator('[data-testid="selectable-list-update-btn"]').click();
+ await updateTeamResponse;
+
+ // Verify the user is added to the team
+
+ await expect(
+ page.locator(`[data-testid="${userName.toLowerCase()}"]`)
+ ).toBeVisible();
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/UserTab/UserTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/UserTab/UserTab.component.tsx
index b4a4b0d16450..0205cf9c0a7d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/UserTab/UserTab.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Team/TeamDetails/UserTab/UserTab.component.tsx
@@ -105,6 +105,11 @@ export const UserTab = ({
[currentTeam.teamType]
);
+ const editUserPermission = useMemo(
+ () => permission.EditAll || permission.EditUsers,
+ [permission.EditAll, permission.EditUsers]
+ );
+
/**
* Make API call to fetch current team user data
*/
@@ -213,13 +218,13 @@ export const UserTab = ({
}
@@ -235,7 +240,7 @@ export const UserTab = ({
return tabColumns.filter((column) =>
column.key === 'actions' ? !isTeamDeleted : true
);
- }, [handleRemoveClick, permission, isTeamDeleted]);
+ }, [handleRemoveClick, editUserPermission, isTeamDeleted]);
const sortedUser = useMemo(() => orderBy(users, ['name'], 'asc'), [users]);
@@ -329,10 +334,10 @@ export const UserTab = ({
}
type="primary">
{t('label.add')}
@@ -352,7 +357,7 @@ export const UserTab = ({
}
className="mt-0-important"
heading={t('label.user')}
- permission={permission.EditAll}
+ permission={editUserPermission}
type={ERROR_PLACEHOLDER_TYPE.ASSIGN}
/>
) : (
@@ -382,7 +387,7 @@ export const UserTab = ({
{!currentTeam.deleted && isGroupType && (
- {users.length > 0 && permission.EditAll && (
+ {users.length > 0 && editUserPermission && (
{
{
fields: [
'users',
- 'userCount',
'defaultRoles',
'policies',
'childrenCount',
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TeamsPage/TeamsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TeamsPage/TeamsPage.tsx
index 29baf0f254fd..7cde8c3d8cc5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/TeamsPage/TeamsPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/TeamsPage/TeamsPage.tsx
@@ -244,7 +244,6 @@ const TeamsPage = () => {
const data = await getTeamByName(name, {
fields: [
TabSpecificField.USERS,
- TabSpecificField.USER_COUNT,
TabSpecificField.DEFAULT_ROLES,
TabSpecificField.POLICIES,
TabSpecificField.CHILDREN_COUNT,
From 912ca638c565cbea2e3d4a572958dd4ec5115b84 Mon Sep 17 00:00:00 2001
From: Ashish Gupta
Date: Thu, 19 Dec 2024 12:14:27 +0530
Subject: [PATCH 016/294] 19064: fix the spacing around the input in custom
properties right panel (#19132)
* fix the spacing around the input in custom properties right panel
* remove unwanted css
(cherry picked from commit a0c33ccc3f5b4dbb186576143a298f2ce189c2ee)
---
.../CustomPropertyTable/PropertyInput.tsx | 2 +-
.../CustomPropertyTable/PropertyValue.tsx | 8 ++++----
.../CustomPropertyTable/property-value.less | 1 +
.../common/InlineEdit/InlineEdit.component.tsx | 4 +++-
.../common/InlineEdit/inline-edit.less | 18 ++++++++++++++++++
5 files changed, 27 insertions(+), 6 deletions(-)
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/InlineEdit/inline-edit.less
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyInput.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyInput.tsx
index c8c0128ac6a2..1f43636a57b0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyInput.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyInput.tsx
@@ -48,7 +48,7 @@ export const PropertyInput: FC = ({
= ({
const getPropertyInput = () => {
const commonStyle: CSSProperties = {
marginBottom: '0px',
- minWidth: '250px',
+ width: '100%',
};
switch (propertyType.name) {
case 'string':
@@ -314,11 +314,11 @@ export const PropertyValue: FC = ({
@@ -359,10 +359,10 @@ export const PropertyValue: FC = ({
@@ -905,7 +905,7 @@ export const PropertyValue: FC = ({
return (
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/property-value.less b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/property-value.less
index fc786b0bdae7..cd625c814427 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/property-value.less
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/property-value.less
@@ -94,5 +94,6 @@
}
.custom-property-inline-edit-container {
+ width: 100%;
overflow-x: scroll;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/InlineEdit/InlineEdit.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/InlineEdit/InlineEdit.component.tsx
index e7e97214b088..97e203b9fd16 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/InlineEdit/InlineEdit.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/InlineEdit/InlineEdit.component.tsx
@@ -14,6 +14,7 @@ import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
import { Button, Space } from 'antd';
import classNames from 'classnames';
import React from 'react';
+import './inline-edit.less';
import { InlineEditProps } from './InlineEdit.interface';
const InlineEdit = ({
@@ -28,7 +29,8 @@ const InlineEdit = ({
}: InlineEditProps) => {
return (
Date: Thu, 19 Dec 2024 12:39:44 +0530
Subject: [PATCH 017/294] Cleanup lineage on pipeline and store procedure
removal (#19133)
(cherry picked from commit f9de2b926ad7048032fd5c222d35445b1bdda7da)
---
.../openmetadata/service/jdbi3/CollectionDAO.java | 5 +----
.../service/jdbi3/LineageRepository.java | 2 +-
.../service/jdbi3/PipelineRepository.java | 14 ++++++++++++++
.../service/jdbi3/StoredProcedureRepository.java | 13 +++++++++++++
4 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
index a423db901cd3..5926e5413af3 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java
@@ -1180,10 +1180,7 @@ void deleteLineageBySource(
+ "AND json->>'source' = :source",
connectionType = POSTGRES)
void deleteLineageBySourcePipeline(
- @BindUUID("toId") UUID toId,
- @Bind("toEntity") String toEntity,
- @Bind("source") String source,
- @Bind("relation") int relation);
+ @BindUUID("toId") UUID toId, @Bind("source") String source, @Bind("relation") int relation);
class FromRelationshipMapper implements RowMapper {
@Override
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/LineageRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/LineageRepository.java
index 134fe998ec15..9026c4f36daf 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/LineageRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/LineageRepository.java
@@ -633,7 +633,7 @@ public void deleteLineageBySource(UUID toId, String toEntity, String source) {
.findLineageBySourcePipeline(toId, toEntity, source, Relationship.UPSTREAM.ordinal());
// Finally, delete lineage relationship
dao.relationshipDAO()
- .deleteLineageBySourcePipeline(toId, toEntity, source, Relationship.UPSTREAM.ordinal());
+ .deleteLineageBySourcePipeline(toId, toEntity, Relationship.UPSTREAM.ordinal());
} else {
relations =
dao.relationshipDAO()
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PipelineRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PipelineRepository.java
index 8aaf4db08d8f..bab495ad27d0 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PipelineRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PipelineRepository.java
@@ -42,6 +42,8 @@
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.FieldChange;
import org.openmetadata.schema.type.Include;
+import org.openmetadata.schema.type.LineageDetails;
+import org.openmetadata.schema.type.Relationship;
import org.openmetadata.schema.type.Status;
import org.openmetadata.schema.type.TagLabel;
import org.openmetadata.schema.type.Task;
@@ -296,6 +298,18 @@ public void storeEntity(Pipeline pipeline, boolean update) {
pipeline.withService(service).withTasks(taskWithTagsAndOwners);
}
+ @Override
+ protected void cleanup(Pipeline pipeline) {
+ // When a pipeline is removed , the linege needs to be removed
+ daoCollection
+ .relationshipDAO()
+ .deleteLineageBySourcePipeline(
+ pipeline.getId(),
+ LineageDetails.Source.PIPELINE_LINEAGE.value(),
+ Relationship.UPSTREAM.ordinal());
+ super.cleanup(pipeline);
+ }
+
@Override
public void storeRelationships(Pipeline pipeline) {
addServiceRelationship(pipeline, pipeline.getService());
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/StoredProcedureRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/StoredProcedureRepository.java
index 60ad7db78d23..11624a475808 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/StoredProcedureRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/StoredProcedureRepository.java
@@ -11,6 +11,7 @@
import org.openmetadata.schema.entity.data.StoredProcedure;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.Include;
+import org.openmetadata.schema.type.LineageDetails;
import org.openmetadata.schema.type.Relationship;
import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.databases.StoredProcedureResource;
@@ -69,6 +70,18 @@ public void storeRelationships(StoredProcedure storedProcedure) {
Relationship.CONTAINS);
}
+ @Override
+ protected void cleanup(StoredProcedure storedProcedure) {
+ // When a pipeline is removed , the linege needs to be removed
+ daoCollection
+ .relationshipDAO()
+ .deleteLineageBySourcePipeline(
+ storedProcedure.getId(),
+ LineageDetails.Source.QUERY_LINEAGE.value(),
+ Relationship.UPSTREAM.ordinal());
+ super.cleanup(storedProcedure);
+ }
+
@Override
public void setInheritedFields(StoredProcedure storedProcedure, EntityUtil.Fields fields) {
DatabaseSchema schema =
From 3add5f90f47d7b7118328edb3125c500b54ae1b9 Mon Sep 17 00:00:00 2001
From: Pranita Fulsundar
Date: Wed, 18 Dec 2024 14:40:10 +0530
Subject: [PATCH 018/294] feat(ui): highlight search term for schema table in
table details page (#19110)
* feat: highlight search term
* refactor: use stringToHTML instead of parse
* test: highlightSearchText method
* fix: test for schema table
* fix: mock implemetation of stringToHTML
* test: highlightSearchText function with null or falsy parameters
(cherry picked from commit 31b11323896e9046d79e66dc3921dbccd79dfcff)
---
.../SchemaTable/SchemaTable.component.tsx | 26 ++++-----
.../Database/SchemaTable/SchemaTable.test.tsx | 10 ++++
.../ui/src/utils/EntityUtils.test.tsx | 56 +++++++++++++++++++
.../resources/ui/src/utils/EntityUtils.tsx | 13 +++++
.../ui/src/utils/mocks/EntityUtils.mock.ts | 8 +++
5 files changed, 100 insertions(+), 13 deletions(-)
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx
index 3b5fb3b97c62..a4c9abf53026 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx
@@ -23,7 +23,6 @@ import {
isUndefined,
set,
sortBy,
- toLower,
uniqBy,
} from 'lodash';
import { EntityTags, TagFilterOptions } from 'Models';
@@ -56,9 +55,11 @@ import {
getColumnSorter,
getEntityName,
getFrequentlyJoinedColumns,
+ highlightSearchText,
searchInColumns,
} from '../../../utils/EntityUtils';
import { getEntityColumnFQN } from '../../../utils/FeedUtils';
+import { stringToHTML } from '../../../utils/StringsUtils';
import {
getAllTags,
searchTagInData,
@@ -246,15 +247,12 @@ const SchemaTable = ({
return NO_DATA_PLACEHOLDER;
}
- return isReadOnly ||
- (displayValue && displayValue.length < 25 && !isReadOnly) ? (
- toLower(displayValue)
- ) : (
-
-
- {displayValue}
-
-
+ return (
+
+ {stringToHTML(highlightSearchText(displayValue, searchText))}
+
);
};
@@ -268,7 +266,7 @@ const SchemaTable = ({
- {name}
+ {stringToHTML(highlightSearchText(name, searchText))}
{!isEmpty(displayName) ? (
@@ -386,7 +384,9 @@ const SchemaTable = ({
- {getEntityName(record)}
+ {stringToHTML(
+ highlightSearchText(getEntityName(record), searchText)
+ )}
) : null}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.test.tsx
index 1e49687b4a50..401a7328534f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.test.tsx
@@ -167,6 +167,16 @@ jest.mock('../../../constants/Table.constants', () => ({
},
}));
+jest.mock('../../../utils/StringsUtils', () => ({
+ ...jest.requireActual('../../../utils/StringsUtils'),
+ stringToHTML: jest.fn((text) => text),
+}));
+
+jest.mock('../../../utils/EntityUtils', () => ({
+ ...jest.requireActual('../../../utils/EntityUtils'),
+ highlightSearchText: jest.fn((text) => text),
+}));
+
describe('Test EntityTable Component', () => {
it('Initially, Table should load', async () => {
render(
, {
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 264482fe8fcc..757e69de9f71 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
@@ -23,12 +23,16 @@ import {
getEntityLinkFromType,
getEntityOverview,
highlightEntityNameAndDescription,
+ highlightSearchText,
} from './EntityUtils';
import {
entityWithoutNameAndDescHighlight,
highlightedEntityDescription,
highlightedEntityDisplayName,
+ mockHighlightedResult,
mockHighlights,
+ mockSearchText,
+ mockText,
} from './mocks/EntityUtils.mock';
jest.mock('../constants/constants', () => ({
@@ -199,4 +203,56 @@ describe('EntityUtils unit tests', () => {
expect(sorter(item2, item1)).toBe(1);
});
});
+
+ describe('highlightSearchText method', () => {
+ it('should return the text with highlighted search text', () => {
+ const result = highlightSearchText(mockText, mockSearchText);
+
+ expect(result).toBe(mockHighlightedResult);
+ });
+
+ it('should return the original text if searchText is not found', () => {
+ const result = highlightSearchText(mockText, 'nonexistent');
+
+ expect(result).toBe(mockText);
+ });
+
+ it('should return an empty string if no text is provided', () => {
+ const result = highlightSearchText('', 'test');
+
+ expect(result).toBe('');
+ });
+
+ it('should return an empty string if no searchText is provided', () => {
+ const result = highlightSearchText(mockText, '');
+
+ expect(result).toBe(mockText);
+ });
+
+ it('should return empty string if both text and searchText are missing', () => {
+ const result = highlightSearchText('', '');
+
+ expect(result).toBe('');
+ });
+
+ const falsyTestCases = [
+ { text: null, searchText: 'test', expected: '' },
+ { text: 'mockText', searchText: null, expected: 'mockText' },
+ { text: null, searchText: null, expected: '' },
+ { text: 0 as any, searchText: '', expected: 0 },
+ { text: false as any, searchText: '', expected: false },
+ ];
+
+ it.each(falsyTestCases)(
+ 'should return expected when text or searchText is null or falsy',
+ ({ text, searchText, expected }) => {
+ const result = highlightSearchText(
+ text ?? undefined,
+ searchText ?? undefined
+ );
+
+ expect(result).toBe(expected);
+ }
+ );
+ });
});
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 6740c1785118..7a4eb3fc908d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
@@ -2540,3 +2540,16 @@ export const getColumnSorter =
(field: K) => {
return 0;
};
};
+
+export const highlightSearchText = (
+ text?: string,
+ searchText?: string
+): string => {
+ if (!searchText || !text) {
+ return text ?? '';
+ }
+
+ const regex = new RegExp(`(${searchText})`, 'gi');
+
+ return text.replace(regex, `$1 `);
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts
index aaea1f5e86a5..295ef7a91681 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts
@@ -136,3 +136,11 @@ export const mockHighlights = {
export const highlightedEntityDescription = `This dimension table contains the billing and shipping addresses of customers.`;
export const highlightedEntityDisplayName = `dim_address `;
+
+export const mockText =
+ 'This is a test description to verify highlightText method.';
+
+export const mockSearchText = 'test';
+
+export const mockHighlightedResult =
+ 'This is a test description to verify highlightText method.';
From e0fde37e9392b7f65c51c14e6fc37945391ae323 Mon Sep 17 00:00:00 2001
From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Date: Mon, 16 Dec 2024 21:02:16 +0530
Subject: [PATCH 019/294] fix(ui): sync search value with url and state value
(#19050)
* fix(ui): sync search value with url and state value
* fix flaky for user spec
(cherry picked from commit 4d30c83c55b3b9aa8d18e9716567175b40039712)
---
.../src/main/resources/ui/playwright/utils/user.ts | 8 +++++++-
.../ui/src/pages/UserListPage/UserListPageV1.tsx | 6 ++++--
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts
index eaee1add4451..20926d5b9576 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts
@@ -97,6 +97,12 @@ export const deletedUserChecks = async (page: Page) => {
export const visitUserProfilePage = async (page: Page, userName: string) => {
await settingClick(page, GlobalSettingOptions.USERS);
+ await page.waitForSelector(
+ '[data-testid="user-list-v1-component"] [data-testid="loader"]',
+ {
+ state: 'detached',
+ }
+ );
const userResponse = page.waitForResponse(
'/api/v1/search/query?q=**&from=0&size=*&index=*'
);
@@ -470,9 +476,9 @@ export const permanentDeleteUser = async (
);
await page.click('[data-testid="confirm-button"]');
await hardDeleteUserResponse;
- await reFetchUsers;
await toastNotification(page, `"${displayName}" deleted successfully!`);
+ await reFetchUsers;
// Wait for the loader to disappear
await page.waitForSelector('[data-testid="loader"]', { state: 'hidden' });
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 1833734de7d5..08957e5818bc 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
@@ -14,7 +14,7 @@
import { Button, Col, Modal, Row, Space, Switch, Tooltip } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { AxiosError } from 'axios';
-import { capitalize, isEmpty } from 'lodash';
+import { capitalize, debounce, isEmpty } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom';
@@ -235,6 +235,7 @@ const UserListPageV1 = () => {
const handleSearch = (value: string) => {
setSearchValue(value);
handlePageChange(INITIAL_PAGING_VALUE);
+
const params = new URLSearchParams({ user: value });
// This function is called onChange in the search input with debouncing
// Hence using history.replace instead of history.push to avoid adding multiple routes in history
@@ -459,7 +460,8 @@ const UserListPageV1 = () => {
type: t('label.user'),
})}...`}
searchValue={searchValue}
- onSearch={handleSearch}
+ typingInterval={0}
+ onSearch={debounce(handleSearch, 400)}
/>
From 1f59cec66ebfc2b9203d83f43d79f62200fca412 Mon Sep 17 00:00:00 2001
From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Date: Mon, 16 Dec 2024 22:08:54 +0530
Subject: [PATCH 020/294] fix(ui): expand invalid state upon glossary term add
(#18968)
* fix(ui): expand invalid state upon glossary term add
invalid expand state for glossary term update
* fix tests
* update glossary store upon changes in tree
* fix tests
(cherry picked from commit 254fce41385e0f7debfd8e6210496f7567b3d1a1)
---
.../GlossaryTermTab.component.tsx | 5 +-
.../Glossary/GlossaryV1.component.tsx | 59 +++++---
.../components/Glossary/useGlossary.store.ts | 11 ++
.../main/resources/ui/src/rest/glossaryAPI.ts | 2 +-
.../ui/src/utils/GlossaryUtils.test.ts | 136 ++++++++++++++++++
.../resources/ui/src/utils/GlossaryUtils.tsx | 32 +++++
6 files changed, 221 insertions(+), 24 deletions(-)
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 1e69fa0736fb..147f3dff4c19 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
@@ -106,7 +106,10 @@ const GlossaryTermTab = ({
useGlossaryStore();
const { t } = useTranslation();
- const glossaryTerms = (glossaryChildTerms as ModifiedGlossaryTerm[]) ?? [];
+ const glossaryTerms = useMemo(
+ () => (glossaryChildTerms as ModifiedGlossaryTerm[]) ?? [],
+ [glossaryChildTerms]
+ );
const [movedGlossaryTerm, setMovedGlossaryTerm] =
useState();
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx
index c92528e87b93..a3c165428aa1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx
@@ -98,8 +98,12 @@ const GlossaryV1 = ({
const [editMode, setEditMode] = useState(false);
- const { activeGlossary, glossaryChildTerms, setGlossaryChildTerms } =
- useGlossaryStore();
+ const {
+ activeGlossary,
+ glossaryChildTerms,
+ setGlossaryChildTerms,
+ insertNewGlossaryTermToChildTerms,
+ } = useGlossaryStore();
const { id, fullyQualifiedName } = activeGlossary ?? {};
@@ -128,11 +132,9 @@ const GlossaryV1 = ({
const { data } = await getFirstLevelGlossaryTerms(
params?.glossary ?? params?.parent ?? ''
);
- const children = data.map((data) =>
- data.childrenCount ?? 0 > 0 ? { ...data, children: [] } : data
- );
-
- setGlossaryChildTerms(children as ModifiedGlossary[]);
+ // We are considering childrenCount fot expand collapse state
+ // Hence don't need any intervention to list response here
+ setGlossaryChildTerms(data as ModifiedGlossary[]);
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
@@ -231,7 +233,11 @@ const GlossaryV1 = ({
entity: t('label.glossary-term'),
});
} else {
- updateGlossaryTermInStore(response);
+ updateGlossaryTermInStore({
+ ...response,
+ // Since patch didn't respond with childrenCount preserve it from currentData
+ childrenCount: currentData.childrenCount,
+ });
setIsEditModalOpen(false);
}
} catch (error) {
@@ -256,29 +262,38 @@ const GlossaryV1 = ({
}
};
- const onTermModalSuccess = useCallback(() => {
- loadGlossaryTerms(true);
- if (!isGlossaryActive && tab !== 'terms') {
- history.push(
- getGlossaryTermDetailsPath(
- selectedData.fullyQualifiedName || '',
- 'terms'
- )
- );
- }
- setIsEditModalOpen(false);
- }, [isGlossaryActive, tab, selectedData]);
+ const onTermModalSuccess = useCallback(
+ (term: GlossaryTerm) => {
+ // Setting loading so that nested terms are rendered again on table with change
+ setIsTermsLoading(true);
+ // Update store with newly created term
+ insertNewGlossaryTermToChildTerms(term);
+ if (!isGlossaryActive && tab !== 'terms') {
+ history.push(
+ getGlossaryTermDetailsPath(
+ selectedData.fullyQualifiedName || '',
+ 'terms'
+ )
+ );
+ }
+ // Close modal and set loading to false
+ setIsEditModalOpen(false);
+ setIsTermsLoading(false);
+ },
+ [isGlossaryActive, tab, selectedData]
+ );
const handleGlossaryTermAdd = async (formData: GlossaryTermForm) => {
try {
- await addGlossaryTerm({
+ const term = await addGlossaryTerm({
...formData,
glossary:
activeGlossaryTerm?.glossary?.name ||
(selectedData.fullyQualifiedName ?? ''),
parent: activeGlossaryTerm?.fullyQualifiedName,
});
- onTermModalSuccess();
+
+ onTermModalSuccess(term);
} catch (error) {
if (
(error as AxiosError).response?.status === HTTP_STATUS_CODE.CONFLICT
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/useGlossary.store.ts b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/useGlossary.store.ts
index 81ec1d9bef89..f8799cb02a13 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/useGlossary.store.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/useGlossary.store.ts
@@ -12,7 +12,9 @@
*/
import { create } from 'zustand';
import { Glossary } from '../../generated/entity/data/glossary';
+import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
import { GlossaryTermWithChildren } from '../../rest/glossaryAPI';
+import { findAndUpdateNested } from '../../utils/GlossaryUtils';
export type ModifiedGlossary = Glossary & {
children?: GlossaryTermWithChildren[];
@@ -27,6 +29,7 @@ export const useGlossaryStore = create<{
updateGlossary: (glossary: Glossary) => void;
updateActiveGlossary: (glossary: Partial) => void;
setGlossaryChildTerms: (glossaryChildTerms: ModifiedGlossary[]) => void;
+ insertNewGlossaryTermToChildTerms: (glossary: GlossaryTerm) => void;
}>()((set, get) => ({
glossaries: [],
activeGlossary: {} as ModifiedGlossary,
@@ -67,6 +70,14 @@ export const useGlossaryStore = create<{
glossaries[index] = updatedGlossary;
}
},
+ insertNewGlossaryTermToChildTerms: (glossary: GlossaryTerm) => {
+ const { glossaryChildTerms } = get();
+
+ // Typically used to updated the glossary term list in the glossary page
+ set({
+ glossaryChildTerms: findAndUpdateNested(glossaryChildTerms, glossary),
+ });
+ },
setGlossaryChildTerms: (glossaryChildTerms: ModifiedGlossary[]) => {
set({ glossaryChildTerms });
},
diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts
index 72d817f816e0..2b6a4db5cd0f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts
@@ -147,7 +147,7 @@ export const getGlossaryTermByFQN = async (fqn = '', params?: ListParams) => {
export const addGlossaryTerm = (
data: CreateGlossaryTerm
-): Promise => {
+): Promise => {
const url = '/glossaryTerms';
return APIClient.post(url, data);
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.test.ts
index bea6c81ae092..11263c62500a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.test.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.test.ts
@@ -11,8 +11,10 @@
* limitations under the License.
*/
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
+import { ModifiedGlossary } from '../components/Glossary/useGlossary.store';
import { EntityType } from '../enums/entity.enum';
import { Glossary } from '../generated/entity/data/glossary';
+import { GlossaryTerm } from '../generated/entity/data/glossaryTerm';
import {
MOCKED_GLOSSARY_TERMS,
MOCKED_GLOSSARY_TERMS_1,
@@ -22,6 +24,7 @@ import {
import {
buildTree,
filterTreeNodeOptions,
+ findAndUpdateNested,
findExpandableKeys,
findExpandableKeysForArray,
getQueryFilterToExcludeTerm,
@@ -219,3 +222,136 @@ describe('Glossary Utils', () => {
expect(filteredOptions).toEqual(expected_glossary);
});
});
+
+describe('findAndUpdateNested', () => {
+ it('should add new term to the correct parent', () => {
+ const terms: ModifiedGlossary[] = [
+ {
+ fullyQualifiedName: 'parent1',
+ children: [],
+ id: 'parent1',
+ name: 'parent1',
+ description: 'parent1',
+ },
+ {
+ fullyQualifiedName: 'parent2',
+ children: [],
+ id: 'parent2',
+ name: 'parent2',
+ description: 'parent2',
+ },
+ ];
+
+ const newTerm: GlossaryTerm = {
+ fullyQualifiedName: 'child1',
+ parent: {
+ fullyQualifiedName: 'parent1',
+ id: 'parent1',
+ type: 'Glossary',
+ },
+ id: 'child1',
+ name: 'child1',
+ description: 'child1',
+ glossary: {
+ fullyQualifiedName: 'child1',
+ id: 'child1',
+ name: 'child1',
+ description: 'child1',
+ type: 'Glossary',
+ },
+ };
+
+ const updatedTerms = findAndUpdateNested(terms, newTerm);
+
+ expect(updatedTerms[0].children).toHaveLength(1);
+ expect(updatedTerms?.[0].children?.[0]).toEqual(newTerm);
+ });
+
+ it('should add new term to nested parent', () => {
+ const terms: ModifiedGlossary[] = [
+ {
+ fullyQualifiedName: 'parent1',
+ children: [
+ {
+ fullyQualifiedName: 'child1',
+ children: [],
+ glossary: {
+ fullyQualifiedName: 'child1',
+ id: 'child1',
+ name: 'child1',
+ description: 'child1',
+ type: 'Glossary',
+ },
+ id: 'child1',
+ name: 'child1',
+ description: 'child1',
+ },
+ ],
+ id: 'parent1',
+ name: 'parent1',
+ description: 'parent1',
+ },
+ ];
+
+ const newTerm: GlossaryTerm = {
+ fullyQualifiedName: 'child2',
+ parent: { fullyQualifiedName: 'child1', id: 'child1', type: 'Glossary' },
+ id: 'child2',
+ name: 'child2',
+ description: 'child2',
+ glossary: {
+ fullyQualifiedName: 'child2',
+ id: 'child2',
+ name: 'child2',
+ description: 'child2',
+ type: 'Glossary',
+ },
+ };
+
+ const updatedTerms = findAndUpdateNested(terms, newTerm);
+
+ expect(
+ updatedTerms?.[0].children && updatedTerms?.[0].children[0].children
+ ).toHaveLength(1);
+ expect(
+ updatedTerms?.[0].children &&
+ updatedTerms?.[0].children[0].children &&
+ updatedTerms?.[0].children[0].children[0]
+ ).toEqual(newTerm);
+ });
+
+ it('should not modify terms if parent is not found', () => {
+ const terms: ModifiedGlossary[] = [
+ {
+ fullyQualifiedName: 'parent1',
+ children: [],
+ id: 'parent1',
+ name: 'parent1',
+ description: 'parent1',
+ },
+ ];
+
+ const newTerm: GlossaryTerm = {
+ fullyQualifiedName: 'child1',
+ parent: {
+ fullyQualifiedName: 'nonexistent',
+ id: 'nonexistent',
+ type: 'Glossary',
+ },
+ id: 'child1',
+ name: 'child1',
+ description: 'child1',
+ glossary: {
+ fullyQualifiedName: 'child1',
+ id: 'child1',
+ name: 'child1',
+ description: 'child1',
+ type: 'Glossary',
+ },
+ };
+
+ const updatedTerms = findAndUpdateNested(terms, newTerm);
+
+ expect(updatedTerms).toEqual(terms);
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx
index b95492e8bf39..96c553405090 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx
@@ -334,3 +334,35 @@ export const filterTreeNodeOptions = (
return filterNodes(options as ModifiedGlossaryTerm[]);
};
+
+export const findAndUpdateNested = (
+ terms: ModifiedGlossary[],
+ newTerm: GlossaryTerm
+): ModifiedGlossary[] => {
+ // If new term has no parent, it's a top level term
+ // So just update 0 level terms no need to iterate over it
+ if (!newTerm.parent) {
+ return [...terms, newTerm as ModifiedGlossary];
+ }
+
+ // If parent is there means term is created within a term
+ // So we need to find the parent term and update it's children
+ return terms.map((term) => {
+ if (term.fullyQualifiedName === newTerm.parent?.fullyQualifiedName) {
+ return {
+ ...term,
+ children: [...(term.children || []), newTerm] as GlossaryTerm[],
+ } as ModifiedGlossary;
+ } else if ('children' in term && term.children?.length) {
+ return {
+ ...term,
+ children: findAndUpdateNested(
+ term.children as ModifiedGlossary[],
+ newTerm
+ ),
+ } as ModifiedGlossary;
+ }
+
+ return term;
+ });
+};
From aa5b6ca9390d278c563713b0802fe537745d5f67 Mon Sep 17 00:00:00 2001
From: Ashish Gupta
Date: Thu, 19 Dec 2024 22:45:45 +0530
Subject: [PATCH 021/294] fix table page breaking due to highligther text
(#19146)
(cherry picked from commit 7d962d91eb9e1ad48a1be89e4b9b54e33011ee24)
---
.../playwright/support/entity/TableClass.ts | 9 +-
.../SchemaTable/SchemaTable.component.tsx | 3 +-
.../ui/src/utils/EntityUtils.test.tsx | 100 ++++++++++++++++++
.../resources/ui/src/utils/EntityUtils.tsx | 29 ++++-
.../ui/src/utils/mocks/EntityUtils.mock.ts | 2 +-
5 files changed, 136 insertions(+), 7 deletions(-)
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts
index 29ac8d6490cb..bd40908dceb3 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts
@@ -77,16 +77,17 @@ export class TableClass extends EntityClass {
children: [
{
name: 'first_name',
- dataType: 'VARCHAR',
+ dataType: 'STRUCT',
dataLength: 100,
- dataTypeDisplay: 'varchar',
+ dataTypeDisplay:
+ 'struct',
description: 'First name of the staff member.',
},
{
name: 'last_name',
- dataType: 'VARCHAR',
+ dataType: 'ARRAY',
dataLength: 100,
- dataTypeDisplay: 'varchar',
+ dataTypeDisplay: 'array>>',
},
],
},
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx
index a4c9abf53026..bbdafcb16071 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx
@@ -55,6 +55,7 @@ import {
getColumnSorter,
getEntityName,
getFrequentlyJoinedColumns,
+ highlightSearchArrayElement,
highlightSearchText,
searchInColumns,
} from '../../../utils/EntityUtils';
@@ -251,7 +252,7 @@ const SchemaTable = ({
- {stringToHTML(highlightSearchText(displayValue, searchText))}
+ {highlightSearchArrayElement(dataTypeDisplay, searchText)}
);
};
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 757e69de9f71..b0e77f33a64c 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
@@ -10,6 +10,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { render } from '@testing-library/react';
+import React from 'react';
import { getEntityDetailsPath } from '../constants/constants';
import { EntityTabs, EntityType } from '../enums/entity.enum';
import { ExplorePageTabs } from '../enums/Explore.enum';
@@ -23,6 +25,7 @@ import {
getEntityLinkFromType,
getEntityOverview,
highlightEntityNameAndDescription,
+ highlightSearchArrayElement,
highlightSearchText,
} from './EntityUtils';
import {
@@ -255,4 +258,101 @@ describe('EntityUtils unit tests', () => {
}
);
});
+
+ describe('highlightSearchArrayElement method', () => {
+ it('should highlight the searchText in the text', () => {
+ const result = highlightSearchArrayElement(mockText, 'highlightText');
+ const { container } = render(<>{result}>); // Render the result to check JSX output
+
+ // Check if the correct part of the text is wrapped in a with the correct class
+ const highlighted = container.querySelector('.text-highlighter');
+
+ expect(highlighted).toBeInTheDocument();
+ expect(highlighted?.textContent).toBe('highlightText');
+ });
+
+ it('should highlight multiple occurrences of the searchText', () => {
+ const result = highlightSearchArrayElement(
+ 'Data testing environment, Manually test data',
+ 'data'
+ );
+ const { container } = render(<>{result}>);
+
+ // Check that there are two highlighted parts (one for each 'hello')
+ const highlightedElements =
+ container.querySelectorAll('.text-highlighter');
+
+ expect(highlightedElements).toHaveLength(2);
+ expect(highlightedElements[0].textContent).toBe('Data');
+ expect(highlightedElements[1].textContent).toBe('data');
+ });
+
+ it('should not modify parts of the text that do not match searchText', () => {
+ const result = highlightSearchArrayElement(mockText, 'highlightText');
+ const { container } = render(<>{result}>);
+
+ // Ensure the non-matching part is plain text
+ const nonHighlighted = container.textContent;
+
+ expect(nonHighlighted).toContain('description');
+ });
+
+ it('should not wrap searchText in the result if it does not appear in text', () => {
+ const result = highlightSearchArrayElement(mockText, 'foo');
+ const { container } = render(<>{result}>);
+
+ // Ensure that no parts of the text are highlighted
+ const highlighted = container.querySelector('.text-highlighter');
+
+ expect(highlighted).toBeNull();
+ });
+
+ it('should handle case-insensitive search', () => {
+ const result = highlightSearchArrayElement(mockText, 'HighlightText');
+ const { container } = render(<>{result}>);
+
+ const highlighted = container.querySelector('.text-highlighter');
+
+ expect(highlighted).toBeInTheDocument();
+ expect(highlighted?.textContent).toBe('highlightText');
+ });
+
+ it('should return an empty string if no text is provided', () => {
+ const result = highlightSearchArrayElement('', 'test');
+
+ expect(result).toBe('');
+ });
+
+ it('should return an empty string if no searchText is provided', () => {
+ const result = highlightSearchArrayElement(mockText, '');
+
+ expect(result).toBe(mockText);
+ });
+
+ it('should return empty string if both text and searchText are missing', () => {
+ const result = highlightSearchArrayElement('', '');
+
+ expect(result).toBe('');
+ });
+
+ const falsyTestCases = [
+ { text: null, searchText: 'test', expected: '' },
+ { text: 'mockText', searchText: null, expected: 'mockText' },
+ { text: null, searchText: null, expected: '' },
+ { text: 0 as any, searchText: '', expected: 0 },
+ { text: false as any, searchText: '', expected: false },
+ ];
+
+ it.each(falsyTestCases)(
+ 'should return expected when text or searchText is null or falsy',
+ ({ text, searchText, expected }) => {
+ const result = highlightSearchArrayElement(
+ text ?? undefined,
+ searchText ?? undefined
+ );
+
+ expect(result).toBe(expected);
+ }
+ );
+ });
});
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 7a4eb3fc908d..1a613534a46a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
@@ -2551,5 +2551,32 @@ export const highlightSearchText = (
const regex = new RegExp(`(${searchText})`, 'gi');
- return text.replace(regex, `$1 `);
+ return text.replace(regex, `$1 `);
+};
+
+/**
+ * It searches for a given text in a given string and returns an array that contains the string parts that have
+ * highlighted element if match found.
+ * @param text - The text to search in.
+ * @param searchText - The text to search for.
+ * @returns An Array of string or JSX.Element which contains highlighted element.
+ */
+export const highlightSearchArrayElement = (
+ text?: string,
+ searchText?: string
+): string | (string | JSX.Element)[] => {
+ if (!searchText || !text) {
+ return text ?? '';
+ }
+ const stringParts = text.split(new RegExp(`(${searchText})`, 'gi'));
+
+ return stringParts.map((part, index) =>
+ part.toLowerCase() === (searchText ?? '').toLowerCase() ? (
+
+ {part}
+
+ ) : (
+ part
+ )
+ );
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts
index 295ef7a91681..7d6a4a520e7d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts
@@ -143,4 +143,4 @@ export const mockText =
export const mockSearchText = 'test';
export const mockHighlightedResult =
- 'This is a test description to verify highlightText method.';
+ 'This is a test description to verify highlightText method.';
From d5360073c63c7654f7db59c824f35eec873a6733 Mon Sep 17 00:00:00 2001
From: Karan Hotchandani <33024356+karanh37@users.noreply.github.com>
Date: Thu, 19 Dec 2024 23:05:55 +0530
Subject: [PATCH 022/294] Fix flaky e2e tests (#19038)
* fix lineage flaky tests
* fix glossary flakiness
(cherry picked from commit 9a76b07025fade6285ddcd45819fa93c40fe6806)
---
.../src/main/resources/ui/playwright/utils/glossary.ts | 2 ++
.../src/main/resources/ui/playwright/utils/lineage.ts | 7 +++----
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts
index d694d659f3db..361e19fc0aa0 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts
@@ -1071,7 +1071,9 @@ export const approveTagsTask = async (
await taskResolve;
await redirectToHomePage(page);
+ const glossaryTermsResponse = page.waitForResponse('/api/v1/glossaryTerms*');
await sidebarClick(page, SidebarItem.GLOSSARY);
+ await glossaryTermsResponse;
await selectActiveGlossary(page, entity.data.displayName);
const tagVisibility = await page.isVisible(
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/lineage.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/lineage.ts
index 1c8f2cf5aa69..9dcdbb85e250 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/lineage.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/lineage.ts
@@ -133,10 +133,9 @@ export const dragAndDropNode = async (
await page.hover(originSelector);
await page.mouse.down();
const box = (await destinationElement.boundingBox())!;
- const x = (box.x + box.width / 2) * 0.25; // 0.25 as zoom factor
- const y = (box.y + box.height / 2) * 0.25; // 0.25 as zoom factor
- await page.mouse.move(x, y);
- await destinationElement.hover();
+ const x = box.x + 250;
+ const y = box.y + box.height / 2;
+ await page.mouse.move(x, y, { steps: 20 });
await page.mouse.up();
};
From ae4658f85de20b78f16f22ae2e236fa0f85a656d Mon Sep 17 00:00:00 2001
From: IceS2
Date: Thu, 19 Dec 2024 10:20:21 +0100
Subject: [PATCH 023/294] MINOR: Fix Table constraint relationships for
SoftDeleted entities. (#19129)
* Update TableRepository.java
* Fix Checkstyle
---
.../java/org/openmetadata/service/jdbi3/TableRepository.java | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java
index 0247bf0b7cdb..308b7adbd8e4 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java
@@ -1138,7 +1138,7 @@ private void validateTableConstraints(Table table) {
String toParent = FullyQualifiedName.getParentFQN(column);
String columnName = FullyQualifiedName.getColumnName(column);
try {
- Table toTable = findByName(toParent, NON_DELETED);
+ Table toTable = findByName(toParent, ALL);
validateColumn(toTable, columnName);
} catch (EntityNotFoundException e) {
throw new EntitySpecViolationException("Table not found: " + toParent);
@@ -1232,8 +1232,7 @@ private void addConstraintRelationship(Table table, List constr
for (String column : constraint.getReferredColumns()) {
String toParent = FullyQualifiedName.getParentFQN(column);
try {
- EntityReference toTable =
- Entity.getEntityReferenceByName(TABLE, toParent, NON_DELETED);
+ EntityReference toTable = Entity.getEntityReferenceByName(TABLE, toParent, ALL);
addRelationship(
table.getId(), toTable.getId(), TABLE, TABLE, Relationship.RELATED_TO);
} catch (EntityNotFoundException e) {
From 11796977c12a45e76146df6bad4f5ffe66b3c6fc Mon Sep 17 00:00:00 2001
From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Date: Thu, 19 Dec 2024 18:58:06 +0530
Subject: [PATCH 024/294] chore(ui): improve data insight chart and tooltip
rendering (#19143)
---
.../DataInsight/TotalEntityInsightSummary.component.tsx | 4 ++--
.../resources/ui/src/interface/data-insight.interface.ts | 1 +
.../src/main/resources/ui/src/utils/DataInsightUtils.tsx | 5 ++++-
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/TotalEntityInsightSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/TotalEntityInsightSummary.component.tsx
index 446270600c57..0ae910cd95d5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/TotalEntityInsightSummary.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataInsight/TotalEntityInsightSummary.component.tsx
@@ -13,7 +13,7 @@
import { Button, Col, Row } from 'antd';
import { Gutter } from 'antd/lib/grid/row';
import classNames from 'classnames';
-import { includes, toLower } from 'lodash';
+import { includes, startCase, toLower } from 'lodash';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { updateActiveChartFilter } from '../../utils/ChartUtils';
@@ -114,7 +114,7 @@ const TotalEntityInsightSummary = ({
onMouseEnter={() => handleLegendMouseEnter(entity)}
onMouseLeave={handleLegendMouseLeave}>
{
dateTimeFormatter?: (date?: number) => string;
valueFormatter?: (value: number | string, key?: string) => string | number;
timeStampKey?: string;
+ transformLabel?: boolean;
}
export interface UIKpiResult extends KpiResult {
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataInsightUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataInsightUtils.tsx
index 297c08e425bf..7d0dca2fc50d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DataInsightUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataInsightUtils.tsx
@@ -149,6 +149,7 @@ export const CustomTooltip = (props: DataInsightChartTooltipProps) => {
dateTimeFormatter = formatDate,
isPercentage,
timeStampKey = 'timestampValue',
+ transformLabel = true,
} = props;
if (active && payload && payload.length) {
@@ -172,7 +173,9 @@ export const CustomTooltip = (props: DataInsightChartTooltipProps) => {
- {startCase(entry.name ?? (entry.dataKey as string))}
+ {transformLabel
+ ? startCase(entry.name ?? (entry.dataKey as string))
+ : entry.name ?? (entry.dataKey as string)}
{valueFormatter
From a0e755731de2f3698a71608662031a737681e08a Mon Sep 17 00:00:00 2001
From: Siddhant <86899184+Siddhanttimeline@users.noreply.github.com>
Date: Thu, 19 Dec 2024 22:21:26 +0530
Subject: [PATCH 025/294] fix: Encode userName and correct placeholder for
userResetPasswordLink (#19139)
---
.../openmetadata/service/security/auth/BasicAuthenticator.java | 2 +-
.../java/org/openmetadata/service/util/email/EmailUtil.java | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java
index 9d33d87927d1..a46809417aa3 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java
@@ -184,7 +184,7 @@ public void sendEmailVerification(UriInfo uriInfo, User user) throws IOException
String.format(
"%s/users/registrationConfirmation?user=%s&token=%s",
getSmtpSettings().getOpenMetadataUrl(),
- user.getFullyQualifiedName(),
+ URLEncoder.encode(user.getFullyQualifiedName(), StandardCharsets.UTF_8),
mailVerificationToken);
try {
EmailUtil.sendEmailVerification(emailVerificationLink, user);
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/email/EmailUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/email/EmailUtil.java
index ee3ab278f213..1cdfbac8010e 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/util/email/EmailUtil.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/email/EmailUtil.java
@@ -30,6 +30,7 @@
import static org.openmetadata.service.util.email.TemplateConstants.INVITE_RANDOM_PASSWORD_TEMPLATE;
import static org.openmetadata.service.util.email.TemplateConstants.INVITE_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.PASSWORD;
+import static org.openmetadata.service.util.email.TemplateConstants.PASSWORD_RESET_LINKKEY;
import static org.openmetadata.service.util.email.TemplateConstants.PASSWORD_RESET_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.REPORT_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.SUPPORT_URL;
@@ -179,7 +180,7 @@ public static void sendPasswordResetLink(
.add(ENTITY, getSmtpSettings().getEmailingEntity())
.add(SUPPORT_URL, getSmtpSettings().getSupportUrl())
.add(USERNAME, user.getName())
- .add(EMAIL_VERIFICATION_LINKKEY, passwordResetLink)
+ .add(PASSWORD_RESET_LINKKEY, passwordResetLink)
.add(EXPIRATION_TIME_KEY, DEFAULT_EXPIRATION_TIME)
.build();
From 88bbddb6da2ccccc882dd1f159fa97060fe881da Mon Sep 17 00:00:00 2001
From: Mohit Yadav <105265192+mohityadav766@users.noreply.github.com>
Date: Mon, 16 Dec 2024 15:55:12 +0530
Subject: [PATCH 026/294] Search Index read entities from index mapping
(#19084)
(cherry picked from commit 2888e379984d1342f00b86c81ff15dca2cb0c297)
---
.../bundles/searchIndex/SearchIndexApp.java | 89 +------------------
.../service/search/SearchRepository.java | 5 ++
.../SearchIndexingApplication.json | 62 ++++++-------
3 files changed, 40 insertions(+), 116 deletions(-)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/searchIndex/SearchIndexApp.java b/openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/searchIndex/SearchIndexApp.java
index fd34c2027f5c..1bb8cb283618 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/searchIndex/SearchIndexApp.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/searchIndex/SearchIndexApp.java
@@ -1,47 +1,8 @@
package org.openmetadata.service.apps.bundles.searchIndex;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
-import static org.openmetadata.service.Entity.API_COLLCECTION;
-import static org.openmetadata.service.Entity.API_ENDPOINT;
-import static org.openmetadata.service.Entity.API_SERVICE;
-import static org.openmetadata.service.Entity.CHART;
-import static org.openmetadata.service.Entity.CLASSIFICATION;
-import static org.openmetadata.service.Entity.CONTAINER;
-import static org.openmetadata.service.Entity.DASHBOARD;
-import static org.openmetadata.service.Entity.DASHBOARD_DATA_MODEL;
-import static org.openmetadata.service.Entity.DASHBOARD_SERVICE;
-import static org.openmetadata.service.Entity.DATABASE;
-import static org.openmetadata.service.Entity.DATABASE_SCHEMA;
-import static org.openmetadata.service.Entity.DATABASE_SERVICE;
-import static org.openmetadata.service.Entity.DATA_PRODUCT;
-import static org.openmetadata.service.Entity.DOMAIN;
-import static org.openmetadata.service.Entity.ENTITY_REPORT_DATA;
-import static org.openmetadata.service.Entity.GLOSSARY;
-import static org.openmetadata.service.Entity.GLOSSARY_TERM;
-import static org.openmetadata.service.Entity.INGESTION_PIPELINE;
-import static org.openmetadata.service.Entity.MESSAGING_SERVICE;
-import static org.openmetadata.service.Entity.METADATA_SERVICE;
-import static org.openmetadata.service.Entity.METRIC;
-import static org.openmetadata.service.Entity.MLMODEL;
-import static org.openmetadata.service.Entity.MLMODEL_SERVICE;
-import static org.openmetadata.service.Entity.PIPELINE;
-import static org.openmetadata.service.Entity.PIPELINE_SERVICE;
-import static org.openmetadata.service.Entity.QUERY;
-import static org.openmetadata.service.Entity.SEARCH_INDEX;
-import static org.openmetadata.service.Entity.SEARCH_SERVICE;
-import static org.openmetadata.service.Entity.STORAGE_SERVICE;
-import static org.openmetadata.service.Entity.STORED_PROCEDURE;
-import static org.openmetadata.service.Entity.TABLE;
-import static org.openmetadata.service.Entity.TAG;
-import static org.openmetadata.service.Entity.TEAM;
-import static org.openmetadata.service.Entity.TEST_CASE;
import static org.openmetadata.service.Entity.TEST_CASE_RESOLUTION_STATUS;
import static org.openmetadata.service.Entity.TEST_CASE_RESULT;
-import static org.openmetadata.service.Entity.TEST_SUITE;
-import static org.openmetadata.service.Entity.TOPIC;
-import static org.openmetadata.service.Entity.USER;
-import static org.openmetadata.service.Entity.WEB_ANALYTIC_ENTITY_VIEW_REPORT_DATA;
-import static org.openmetadata.service.Entity.WEB_ANALYTIC_USER_ACTIVITY_REPORT_DATA;
import static org.openmetadata.service.apps.scheduler.AbstractOmAppJobListener.APP_RUN_STATS;
import static org.openmetadata.service.apps.scheduler.AbstractOmAppJobListener.WEBSOCKET_STATUS_CHANNEL;
import static org.openmetadata.service.apps.scheduler.AppScheduler.ON_DEMAND_JOB;
@@ -99,53 +60,8 @@
@Slf4j
public class SearchIndexApp extends AbstractNativeApplication {
-
private static final String ALL = "all";
- public static final Set ALL_ENTITIES =
- Set.of(
- TABLE,
- DASHBOARD,
- TOPIC,
- PIPELINE,
- INGESTION_PIPELINE,
- SEARCH_INDEX,
- USER,
- TEAM,
- GLOSSARY,
- GLOSSARY_TERM,
- MLMODEL,
- TAG,
- CLASSIFICATION,
- QUERY,
- CONTAINER,
- DATABASE,
- DATABASE_SCHEMA,
- TEST_CASE,
- TEST_SUITE,
- CHART,
- DASHBOARD_DATA_MODEL,
- DATABASE_SERVICE,
- MESSAGING_SERVICE,
- DASHBOARD_SERVICE,
- PIPELINE_SERVICE,
- MLMODEL_SERVICE,
- STORAGE_SERVICE,
- METADATA_SERVICE,
- SEARCH_SERVICE,
- ENTITY_REPORT_DATA,
- WEB_ANALYTIC_ENTITY_VIEW_REPORT_DATA,
- WEB_ANALYTIC_USER_ACTIVITY_REPORT_DATA,
- DOMAIN,
- STORED_PROCEDURE,
- DATA_PRODUCT,
- TEST_CASE_RESOLUTION_STATUS,
- TEST_CASE_RESULT,
- API_SERVICE,
- API_ENDPOINT,
- API_COLLCECTION,
- METRIC);
-
public static final Set TIME_SERIES_ENTITIES =
Set.of(
ReportData.ReportDataType.ENTITY_REPORT_DATA.value(),
@@ -180,8 +96,9 @@ public void init(App app) {
JsonUtils.convertValue(app.getAppConfiguration(), EventPublisherJob.class)
.withStats(new Stats());
- if (request.getEntities().contains(ALL)) {
- request.setEntities(ALL_ENTITIES);
+ if (request.getEntities().size() == 1 && request.getEntities().contains(ALL)) {
+ SearchRepository searchRepo = Entity.getSearchRepo();
+ request.setEntities(searchRepo.getSearchEntities());
}
jobData = request;
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java
index 98f4477afdf1..d1e368fdc463 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java
@@ -38,6 +38,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
@@ -1057,4 +1058,8 @@ public List getEntitiesContainingFQNFromES(
}
return new ArrayList<>();
}
+
+ public Set getSearchEntities() {
+ return new HashSet<>(entityIndexMap.keySet());
+ }
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationSchemas/SearchIndexingApplication.json b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationSchemas/SearchIndexingApplication.json
index 4957d1979fa9..ff25b2618b6c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationSchemas/SearchIndexingApplication.json
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationSchemas/SearchIndexingApplication.json
@@ -66,46 +66,48 @@
"type": "string",
"enum": [
"table",
- "dashboard",
- "topic",
- "pipeline",
- "ingestionPipeline",
- "searchIndex",
- "user",
- "team",
- "glossary",
- "glossaryTerm",
- "mlmodel",
- "tag",
- "classification",
- "query",
- "container",
- "database",
"databaseSchema",
- "testCase",
+ "container",
"testSuite",
- "chart",
- "dashboardDataModel",
- "databaseService",
- "messagingService",
- "dashboardService",
- "pipelineService",
"mlmodelService",
- "storageService",
+ "pipelineService",
+ "messagingService",
+ "entityReportData",
+ "ingestionPipeline",
+ "database",
"metadataService",
"searchService",
- "entityReportData",
+ "aggregatedCostAnalysisReportData",
+ "tag",
+ "dashboard",
+ "rawCostAnalysisReportData",
"webAnalyticEntityViewReportData",
- "webAnalyticUserActivityReportData",
- "domain",
"storedProcedure",
"dataProduct",
- "testCaseResolutionStatus",
- "testCaseResult",
+ "databaseService",
+ "dashboardService",
+ "query",
"apiService",
- "apiEndpoint",
+ "searchIndex",
+ "testCaseResult",
"apiCollection",
- "metric"
+ "team",
+ "mlmodel",
+ "classification",
+ "glossaryTerm",
+ "testCaseResolutionStatus",
+ "dashboardDataModel",
+ "pipeline",
+ "glossary",
+ "apiEndpoint",
+ "storageService",
+ "metric",
+ "webAnalyticUserActivityReportData",
+ "domain",
+ "topic",
+ "chart",
+ "user",
+ "testCase"
]
},
"default": ["all"],
From 564d33d69bb4e5b850c5ba1276028928684c74e1 Mon Sep 17 00:00:00 2001
From: Mohit Yadav <105265192+mohityadav766@users.noreply.github.com>
Date: Sun, 15 Dec 2024 01:24:03 +0530
Subject: [PATCH 027/294] Add Algorithm option for validation in yaml (#19049)
* Add algorithm option in authentication
* ENtity Repository code remove
* Keep Default Value
* Fix Test
---------
Co-authored-by: Siddhant <86899184+Siddhanttimeline@users.noreply.github.com>
(cherry picked from commit 50ae01e2ceeae8180178d24ddc66065e4b96cef0)
---
conf/openmetadata.yaml | 1 +
.../service/OpenMetadataApplication.java | 5 ++++-
.../service/security/JwtFilter.java | 6 +++++-
.../security/jwt/JWTTokenGenerator.java | 20 +++++++++++++++++--
.../security/JWTTokenGeneratorTest.java | 4 +++-
.../authenticationConfiguration.json | 6 ++++++
.../authenticationConfiguration.ts | 17 +++++++++++++---
7 files changed, 51 insertions(+), 8 deletions(-)
diff --git a/conf/openmetadata.yaml b/conf/openmetadata.yaml
index dedceb705dd9..cf7f07b7f286 100644
--- a/conf/openmetadata.yaml
+++ b/conf/openmetadata.yaml
@@ -180,6 +180,7 @@ authenticationConfiguration:
# This will only be valid when provider type specified is customOidc
providerName: ${CUSTOM_OIDC_AUTHENTICATION_PROVIDER_NAME:-""}
publicKeyUrls: ${AUTHENTICATION_PUBLIC_KEYS:-[http://localhost:8585/api/v1/system/config/jwks]}
+ tokenValidationAlgorithm: ${AUTHENTICATION_TOKEN_VALIDATION_ALGORITHM:-"RS256"}
authority: ${AUTHENTICATION_AUTHORITY:-https://accounts.google.com}
clientId: ${AUTHENTICATION_CLIENT_ID:-""}
callbackUrl: ${AUTHENTICATION_CALLBACK_URL:-""}
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java b/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java
index fdb07d0bcba9..8efa36c4bc47 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java
@@ -190,7 +190,10 @@ public void run(OpenMetadataApplicationConfig catalogConfig, Environment environ
EntityMaskerFactory.createEntityMasker();
// Instantiate JWT Token Generator
- JWTTokenGenerator.getInstance().init(catalogConfig.getJwtTokenConfiguration());
+ JWTTokenGenerator.getInstance()
+ .init(
+ catalogConfig.getAuthenticationConfiguration().getTokenValidationAlgorithm(),
+ catalogConfig.getJwtTokenConfiguration());
// Set the Database type for choosing correct queries from annotations
jdbi.getConfig(SqlObjects.class)
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
index 0e57365b069e..60f1bb688daf 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
@@ -22,6 +22,7 @@
import static org.openmetadata.service.security.SecurityUtil.validatePrincipalClaimsMapping;
import static org.openmetadata.service.security.jwt.JWTTokenGenerator.ROLES_CLAIM;
import static org.openmetadata.service.security.jwt.JWTTokenGenerator.TOKEN_TYPE;
+import static org.openmetadata.service.security.jwt.JWTTokenGenerator.getAlgorithm;
import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkProvider;
@@ -71,6 +72,7 @@ public class JwtFilter implements ContainerRequestFilter {
private boolean enforcePrincipalDomain;
private AuthProvider providerType;
private boolean useRolesFromProvider = false;
+ private AuthenticationConfiguration.TokenValidationAlgorithm tokenValidationAlgorithm;
private static final List DEFAULT_PUBLIC_KEY_URLS =
Arrays.asList(
@@ -123,6 +125,7 @@ public JwtFilter(
this.principalDomain = authorizerConfiguration.getPrincipalDomain();
this.enforcePrincipalDomain = authorizerConfiguration.getEnforcePrincipalDomain();
this.useRolesFromProvider = authorizerConfiguration.getUseRolesFromProvider();
+ this.tokenValidationAlgorithm = authenticationConfiguration.getTokenValidationAlgorithm();
}
@VisibleForTesting
@@ -224,7 +227,8 @@ public Map validateJwtAndGetClaims(String token) {
// Validate JWT with public key
Jwk jwk = jwkProvider.get(jwt.getKeyId());
- Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
+ Algorithm algorithm =
+ getAlgorithm(tokenValidationAlgorithm, (RSAPublicKey) jwk.getPublicKey(), null);
try {
algorithm.verify(jwt);
} catch (RuntimeException runtimeException) {
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/jwt/JWTTokenGenerator.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/jwt/JWTTokenGenerator.java
index 868175326469..21aaeeeef9b2 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/security/jwt/JWTTokenGenerator.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/jwt/JWTTokenGenerator.java
@@ -37,6 +37,7 @@
import java.util.Set;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
+import org.openmetadata.schema.api.security.AuthenticationConfiguration;
import org.openmetadata.schema.api.security.jwt.JWTTokenConfiguration;
import org.openmetadata.schema.auth.JWTAuthMechanism;
import org.openmetadata.schema.auth.JWTTokenExpiry;
@@ -56,6 +57,7 @@ public class JWTTokenGenerator {
@Getter private RSAPublicKey publicKey;
private String issuer;
private String kid;
+ private AuthenticationConfiguration.TokenValidationAlgorithm tokenValidationAlgorithm;
private JWTTokenGenerator() {
/* Private constructor for singleton */
@@ -66,7 +68,9 @@ public static JWTTokenGenerator getInstance() {
}
/** Expected to be initialized only once during application start */
- public void init(JWTTokenConfiguration jwtTokenConfiguration) {
+ public void init(
+ AuthenticationConfiguration.TokenValidationAlgorithm algorithm,
+ JWTTokenConfiguration jwtTokenConfiguration) {
try {
if (jwtTokenConfiguration.getRsaprivateKeyFilePath() != null
&& !jwtTokenConfiguration.getRsaprivateKeyFilePath().isEmpty()
@@ -84,6 +88,7 @@ public void init(JWTTokenConfiguration jwtTokenConfiguration) {
publicKey = (RSAPublicKey) kf.generatePublic(spec);
issuer = jwtTokenConfiguration.getJwtissuer();
kid = jwtTokenConfiguration.getKeyId();
+ tokenValidationAlgorithm = algorithm;
}
} catch (Exception ex) {
LOG.error("Failed to initialize JWTTokenGenerator ", ex);
@@ -141,7 +146,7 @@ public JWTAuthMechanism getJwtAuthMechanism(
}
}
JWTAuthMechanism jwtAuthMechanism = new JWTAuthMechanism().withJWTTokenExpiry(expiry);
- Algorithm algorithm = Algorithm.RSA256(null, privateKey);
+ Algorithm algorithm = getAlgorithm(tokenValidationAlgorithm, null, privateKey);
String token =
JWT.create()
.withIssuer(issuer)
@@ -214,4 +219,15 @@ public Date getTokenExpiryFromJWT(String token) {
return jwt.getExpiresAt();
}
+
+ public static Algorithm getAlgorithm(
+ AuthenticationConfiguration.TokenValidationAlgorithm algorithm,
+ RSAPublicKey publicKey,
+ RSAPrivateKey privateKey) {
+ return switch (algorithm) {
+ case RS_256 -> Algorithm.RSA256(publicKey, privateKey);
+ case RS_384 -> Algorithm.RSA384(publicKey, privateKey);
+ case RS_512 -> Algorithm.RSA512(publicKey, privateKey);
+ };
+ }
}
diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/security/JWTTokenGeneratorTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/security/JWTTokenGeneratorTest.java
index b7d935f5c624..555a079ddd30 100644
--- a/openmetadata-service/src/test/java/org/openmetadata/service/security/JWTTokenGeneratorTest.java
+++ b/openmetadata-service/src/test/java/org/openmetadata/service/security/JWTTokenGeneratorTest.java
@@ -15,6 +15,7 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
+import org.openmetadata.schema.api.security.AuthenticationConfiguration;
import org.openmetadata.schema.api.security.jwt.JWTTokenConfiguration;
import org.openmetadata.schema.auth.JWTAuthMechanism;
import org.openmetadata.schema.auth.JWTTokenExpiry;
@@ -38,7 +39,8 @@ public void setup() {
jwtTokenConfiguration.setRsaprivateKeyFilePath(rsaPrivateKeyPath);
jwtTokenConfiguration.setRsapublicKeyFilePath(rsaPublicKeyPath);
jwtTokenGenerator = JWTTokenGenerator.getInstance();
- jwtTokenGenerator.init(jwtTokenConfiguration);
+ jwtTokenGenerator.init(
+ AuthenticationConfiguration.TokenValidationAlgorithm.RS_256, jwtTokenConfiguration);
}
@Test
diff --git a/openmetadata-spec/src/main/resources/json/schema/configuration/authenticationConfiguration.json b/openmetadata-spec/src/main/resources/json/schema/configuration/authenticationConfiguration.json
index 670401107ca8..ac7d5075ecae 100644
--- a/openmetadata-spec/src/main/resources/json/schema/configuration/authenticationConfiguration.json
+++ b/openmetadata-spec/src/main/resources/json/schema/configuration/authenticationConfiguration.json
@@ -46,6 +46,12 @@
"type": "string"
}
},
+ "tokenValidationAlgorithm": {
+ "description": "Token Validation Algorithm to use.",
+ "type": "string",
+ "enum": ["RS256", "RS384", "RS512"],
+ "default": "RS256"
+ },
"authority": {
"description": "Authentication Authority",
"type": "string"
diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/configuration/authenticationConfiguration.ts b/openmetadata-ui/src/main/resources/ui/src/generated/configuration/authenticationConfiguration.ts
index c21521c1562a..41f559faee8a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/generated/configuration/authenticationConfiguration.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/generated/configuration/authenticationConfiguration.ts
@@ -10,9 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
- /**
+/**
* This schema defines the Authentication Configuration.
*/
export interface AuthenticationConfiguration {
@@ -69,6 +67,10 @@ export interface AuthenticationConfiguration {
* Saml Configuration that is applicable only when the provider is Saml
*/
samlConfiguration?: SamlSSOClientConfig;
+ /**
+ * Token Validation Algorithm to use.
+ */
+ tokenValidationAlgorithm?: TokenValidationAlgorithm;
}
/**
@@ -492,3 +494,12 @@ export interface SP {
*/
spX509Certificate?: string;
}
+
+/**
+ * Token Validation Algorithm to use.
+ */
+export enum TokenValidationAlgorithm {
+ Rs256 = "RS256",
+ Rs384 = "RS384",
+ Rs512 = "RS512",
+}
From db9c2bcfc7370309ad4ca722b2fde59eba3d44c6 Mon Sep 17 00:00:00 2001
From: Ashish Gupta
Date: Tue, 31 Dec 2024 17:12:15 +0530
Subject: [PATCH 028/294] fix tag page flaky playwright test (#19150)
* fix tag page flaky playwright failures
* removed commented code
(cherry picked from commit ceeba2ad752b5e4df00bb89cee31faf1db3f4f73)
---
.../ui/playwright/support/tag/TagClass.ts | 1 +
.../src/main/resources/ui/playwright/utils/tag.ts | 15 +++++++++------
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/tag/TagClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/tag/TagClass.ts
index 4d15a077f808..b0c86b708052 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/support/tag/TagClass.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/support/tag/TagClass.ts
@@ -66,6 +66,7 @@ export class TagClass {
async visitPage(page: Page) {
await visitClassificationPage(
page,
+ this.responseData.classification.name,
this.responseData.classification.displayName
);
await page.getByTestId(this.data.name).click();
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts
index f5539666bc0f..7252fe61ae90 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts
@@ -38,23 +38,26 @@ export const TAG_INVALID_NAMES = {
export const visitClassificationPage = async (
page: Page,
- classificationName: string
+ classificationName: string,
+ classificationDisplayName: string
) => {
await redirectToHomePage(page);
const classificationResponse = page.waitForResponse(
'/api/v1/classifications?**'
);
+ const fetchTags = page.waitForResponse(
+ `/api/v1/tags?*parent=${classificationName}**`
+ );
await sidebarClick(page, SidebarItem.TAGS);
await classificationResponse;
- const fetchTags = page.waitForResponse('/api/v1/tags?*parent=*');
await page
- .locator(`[data-testid="side-panel-classification"]`)
- .filter({ hasText: classificationName })
+ .getByTestId('data-summary-container')
+ .getByText(classificationDisplayName)
.click();
await expect(page.locator('.activeCategory')).toContainText(
- classificationName
+ classificationDisplayName
);
await fetchTags;
@@ -350,7 +353,7 @@ export const editTagPageDescription = async (page: Page, tag: TagClass) => {
};
export const verifyCertificationTagPageUI = async (page: Page) => {
- await visitClassificationPage(page, 'Certification');
+ await visitClassificationPage(page, 'Certification', 'Certification');
const res = page.waitForResponse(`/api/v1/tags/name/*`);
await page.getByTestId('Gold').click();
await res;
From 4d0b72ee2f8f4d2a19b9a53ddccd25516452b4c2 Mon Sep 17 00:00:00 2001
From: Sachin Chaurasiya
Date: Fri, 27 Dec 2024 20:57:37 +0530
Subject: [PATCH 029/294] feat(#15380): replace description editor with block
editor (#19003)
---
.../decorators/FeedMessageDecorator.java | 4 +-
.../ChangeEventParserResourceTest.java | 10 +-
.../e2e/Features/QueryEntity.spec.ts | 2 +-
.../e2e/Flow/AddRoleAndAssignToUser.spec.ts | 4 +-
.../e2e/Flow/ApiServiceRest.spec.ts | 2 +-
.../e2e/Pages/DataQualityAndProfiler.spec.ts | 12 +-
.../e2e/Pages/SearchIndexApplication.spec.ts | 2 +-
.../ui/playwright/e2e/Pages/Tag.spec.ts | 11 +-
.../ui/playwright/e2e/Pages/Tags.spec.ts | 4 +-
.../ui/playwright/e2e/Pages/Teams.spec.ts | 5 +-
.../ui/playwright/e2e/Pages/TestCases.spec.ts | 7 +-
.../ui/playwright/e2e/Pages/TestSuite.spec.ts | 2 +-
.../playwright/e2e/Pages/UserDetails.spec.ts | 1 +
.../VersionPages/EntityVersionPages.spec.ts | 8 +-
.../ServiceEntityVersionPage.spec.ts | 8 +-
.../resources/ui/playwright/utils/alert.ts | 8 +-
.../resources/ui/playwright/utils/common.ts | 6 +-
.../ui/playwright/utils/customProperty.ts | 26 +-
.../resources/ui/playwright/utils/domain.ts | 2 +-
.../resources/ui/playwright/utils/entity.ts | 26 +-
.../resources/ui/playwright/utils/glossary.ts | 6 +-
.../ui/playwright/utils/importUtils.ts | 9 +-
.../main/resources/ui/playwright/utils/tag.ts | 5 +-
.../resources/ui/playwright/utils/team.ts | 10 +-
.../resources/ui/playwright/utils/user.ts | 5 +-
.../src/assets/svg/ic-format-block-quote.svg | 3 +
.../ui/src/assets/svg/ic-format-highlight.svg | 3 +
.../assets/svg/ic-format-horizontal-line.svg | 3 +
.../src/assets/svg/ic-format-image-inline.svg | 4 +
.../APIEndpointSchema/APIEndpointSchema.tsx | 6 +-
.../FeedCardBody/FeedCardBody.test.tsx | 2 +-
.../FeedCardBody/FeedCardBody.tsx | 6 +-
.../FeedCardBody/FeedCardBodyV1.tsx | 6 +-
.../CustomPropertyFeed.component.tsx | 4 +-
.../DescriptionFeed/DescriptionFeed.test.tsx | 2 +-
.../DescriptionFeed/DescriptionFeed.tsx | 4 +-
.../BlockEditor/BarMenu/BarMenu.tsx | 153 ++
.../BlockEditor/BarMenu/bar-menu.less | 57 +
.../BlockEditor/BlockEditor.interface.ts | 26 +
.../components/BlockEditor/BlockEditor.tsx | 54 +-
.../components/BlockEditor/EditorSlots.tsx | 15 +-
.../BlockEditor/Extensions/diff-view.ts | 45 +-
.../components/BlockEditor/block-editor.less | 24 +-
.../ContainerChildren.test.tsx | 12 +
.../ContainerChildren/ContainerChildren.tsx | 4 +-
.../ContainerDataModel.test.tsx | 2 +-
.../DashboardDetails.test.tsx | 2 +-
.../DashboardVersion.component.tsx | 4 +-
.../DashboardVersion.test.tsx | 2 +-
.../DataModel/DataModels/DataModelsTable.tsx | 4 +-
.../AddDataQualityTest/EditTestCaseModal.tsx | 1 -
.../components/TestCaseForm.tsx | 1 -
.../TestCaseStatusModal.component.tsx | 6 +-
.../AddTestSuiteForm/AddTestSuiteForm.tsx | 1 -
.../DatabaseSchemaTable.tsx | 4 +-
.../Profiler/TableProfiler/ColumnSummary.tsx | 4 +-
.../Database/SchemaTable/SchemaTable.test.tsx | 2 +-
.../TableDataCardBody.test.tsx | 2 +-
.../TableDataCardBody/TableDataCardBody.tsx | 4 +-
.../TableDescription.component.tsx | 4 +-
.../TableDescription.test.tsx | 2 +-
.../SubDomainsTable.component.tsx | 4 +-
.../Entity/Task/TaskTab/TaskTab.component.tsx | 4 -
...TaskTabIncidentManagerHeader.component.tsx | 4 +-
.../TaskTabIncidentManagerHeader.test.tsx | 2 +-
.../VersionTable/VersionTable.component.tsx | 12 +-
.../Entity/VersionTable/VersionTable.test.tsx | 2 +-
.../DataProductSummary.component.tsx | 4 +-
.../SummaryListItems.component.tsx | 4 +-
.../SummaryListItems.test.tsx | 2 +-
.../GlossaryTermTab.component.tsx | 4 +-
.../GlossaryTermTab/GlossaryTermTab.test.tsx | 2 +-
.../MlModelFeaturesList.test.tsx | 2 +-
.../MlModelVersion.component.tsx | 4 +-
.../ModalWithMarkdownEditor.tsx | 6 +-
.../Modals/WhatsNewModal/ChangeLogs.tsx | 6 +-
.../Modals/WhatsNewModal/FeaturesCarousel.tsx | 4 +-
.../PersonaDetailsCard/PersonaDetailsCard.tsx | 4 +-
.../PipelineVersion.component.tsx | 8 +-
.../PipelineVersion/PipelineVersion.test.tsx | 2 +-
.../ApplicationCard.component.tsx | 4 +-
.../ApplicationCard/ApplicationCard.test.tsx | 12 +
.../MarketPlaceAppDetails.component.tsx | 4 +-
.../MarketPlaceAppDetails.test.tsx | 2 +-
.../Bot/BotListV1/BotListV1.component.tsx | 4 +-
.../CustomPropertyTable.test.tsx | 2 +-
.../CustomProperty/CustomPropertyTable.tsx | 4 +-
.../IngestionListTable.test.tsx | 2 +-
.../IngestionListTable/IngestionListTable.tsx | 4 +-
.../Settings/Services/Services.test.tsx | 2 +-
.../components/Settings/Services/Services.tsx | 6 +-
.../Team/TeamDetails/RolesAndPoliciesList.tsx | 4 +-
.../Team/TeamDetails/TeamHierarchy.tsx | 4 +-
.../SuggestionsAlert.test.tsx | 14 +
.../SuggestionsAlert/SuggestionsAlert.tsx | 4 +-
.../Topic/TopicDetails/TopicDetails.test.tsx | 2 +-
.../Topic/TopicSchema/TopicSchema.test.tsx | 2 +-
.../Topic/TopicSchema/TopicSchema.tsx | 6 +-
.../AirflowMessageBanner.tsx | 4 +-
.../CustomPropertyTable/ExtensionTable.tsx | 4 +-
.../PropertyValue.test.tsx | 2 +-
.../CustomPropertyTable/PropertyValue.tsx | 8 +-
.../EntityDescription/DescriptionV1.tsx | 11 +-
.../common/RichTextEditor/EditorToolBar.ts | 61 -
.../RichTextEditor.interface.ts | 10 +-
.../RichTextEditor/RichTextEditor.test.tsx | 77 -
.../common/RichTextEditor/RichTextEditor.tsx | 146 +-
.../RichTextEditorPreviewerV1.tsx | 104 ++
.../rich-text-editor-previewerV1.less | 40 +
.../RichTextEditor/rich-text-editor.less | 1603 -----------------
.../SummaryTagsDescription.component.tsx | 4 +-
.../common/TierCard/TierCard.test.tsx | 2 +-
.../components/common/TierCard/TierCard.tsx | 4 +-
.../APICollectionPage/APIEndpointsTab.tsx | 4 +-
.../AddNotificationPage.tsx | 1 -
.../AddObservabilityPage.tsx | 1 -
.../AddQueryPage/AddQueryPage.component.tsx | 1 -
.../AlertDetailsPage.test.tsx | 19 +-
.../CustomPageSettings/CustomPageSettings.tsx | 4 +-
.../pages/DataInsightPage/KPIList.test.tsx | 2 +-
.../ui/src/pages/DataInsightPage/KPIList.tsx | 4 +-
.../DatabaseDetailsPage.test.tsx | 2 +-
.../DatabaseSchemaPage/SchemaTablesTab.tsx | 4 +-
.../ui/src/pages/KPIPage/AddKPIPage.tsx | 1 -
.../ui/src/pages/KPIPage/EditKPIPage.tsx | 1 -
.../MetricListPage/MetricListPage.tsx | 4 +-
.../NotificationListPage.tsx | 3 +-
.../ObservabilityAlertsPage.tsx | 3 +-
.../AddPolicyPage/AddPolicyPage.tsx | 1 -
.../PoliciesDetailPage.test.tsx | 2 +-
.../PoliciesDetailPage/PoliciesDetailPage.tsx | 4 +-
.../PoliciesDetailsList.component.tsx | 4 +-
.../PoliciesListPage.test.tsx | 2 +-
.../PoliciesListPage/PoliciesListPage.tsx | 4 +-
.../pages/PoliciesPage/RuleForm/RuleForm.tsx | 1 -
.../AddAttributeModal.test.tsx | 2 +-
.../AddAttributeModal/AddAttributeModal.tsx | 4 +-
.../RolesPage/AddRolePage/AddRolePage.tsx | 1 -
.../RolesDetailPage/RolesDetailPage.test.tsx | 2 +-
.../RolesDetailPageList.component.tsx | 4 +-
.../RolesListPage/RolesListPage.test.tsx | 2 +-
.../RolesPage/RolesListPage/RolesListPage.tsx | 4 +-
.../ServiceVersionMainTabContent.test.tsx | 2 +-
.../StoredProcedureTab.test.tsx | 2 +-
.../StoredProcedure/StoredProcedureTab.tsx | 4 +-
.../ui/src/pages/TagsPage/TagsForm.tsx | 2 +-
.../ui/src/pages/TagsPage/TagsPage.test.tsx | 2 +-
.../RequestDescriptionPage.tsx | 9 +-
.../UpdateDescriptionPage.tsx | 5 +-
.../TasksPage/shared/DescriptionTabs.test.tsx | 2 +-
.../TasksPage/shared/DescriptionTabs.tsx | 16 +-
.../TasksPage/shared/DescriptionTask.tsx | 1 -
.../src/pages/TasksPage/shared/DiffView.tsx | 8 +-
.../ui/src/pages/TeamsPage/AddTeamForm.tsx | 5 +-
.../ui/src/utils/BlockEditorUtils.test.ts | 74 +
.../ui/src/utils/BlockEditorUtils.ts | 85 +-
.../ui/src/utils/ClassificationUtils.tsx | 4 +-
.../ui/src/utils/Database/Database.util.tsx | 4 +-
.../__snapshots__/Database.util.test.tsx.snap | 2 +-
.../ui/src/utils/EntityVersionUtils.tsx | 4 +-
.../main/resources/ui/src/utils/FeedUtils.tsx | 6 +-
.../src/utils/ServiceMainTabContentUtils.tsx | 4 +-
.../main/resources/ui/src/utils/TagsUtils.tsx | 4 +-
.../main/resources/ui/src/utils/formUtils.tsx | 2 +-
164 files changed, 1039 insertions(+), 2204 deletions(-)
create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-block-quote.svg
create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-highlight.svg
create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-horizontal-line.svg
create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-image-inline.svg
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BarMenu/BarMenu.tsx
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BarMenu/bar-menu.less
delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/EditorToolBar.ts
delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/RichTextEditor.test.tsx
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/RichTextEditorPreviewerV1.tsx
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/rich-text-editor-previewerV1.less
delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/rich-text-editor.less
create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/BlockEditorUtils.test.ts
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/formatter/decorators/FeedMessageDecorator.java b/openmetadata-service/src/main/java/org/openmetadata/service/formatter/decorators/FeedMessageDecorator.java
index 411391e2772c..cedd3f6fae06 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/formatter/decorators/FeedMessageDecorator.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/formatter/decorators/FeedMessageDecorator.java
@@ -37,7 +37,7 @@ public String getLineBreak() {
@Override
public String getAddMarker() {
- return "";
+ return "";
}
@Override
@@ -47,7 +47,7 @@ public String getAddMarkerClose() {
@Override
public String getRemoveMarker() {
- return "";
+ return "";
}
@Override
diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/ChangeEventParserResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/ChangeEventParserResourceTest.java
index 29bb0247b19e..36ee3fc80adb 100644
--- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/ChangeEventParserResourceTest.java
+++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/ChangeEventParserResourceTest.java
@@ -141,7 +141,7 @@ void testEntityReferenceFormat() {
assertEquals(1, threadWithMessages.size());
assertEquals(
- "Added **owners**: User One ",
+ "Added **owners**: User One ",
threadWithMessages.get(0).getMessage());
}
@@ -156,8 +156,8 @@ void testUpdateOfString() {
assertEquals(1, threadMessages.size());
assertEquals(
- "Updated **description**: old "
- + "new description",
+ "Updated **description**: old "
+ + "new description",
threadMessages.get(0).getMessage());
// test if it updates correctly with one add and one delete change
@@ -225,7 +225,7 @@ void testMajorSchemaChange() {
assertEquals(1, threadWithMessages.size());
assertEquals(
- "Updated **columns**: lo_order priority ",
+ "Updated **columns**: lo_order priority ",
threadWithMessages.get(0).getMessage());
// Simulate a change of datatype change in column
@@ -261,7 +261,7 @@ void testMajorSchemaChange() {
assertEquals(1, threadWithMessages.size());
assertEquals(
- "Updated **columns**: lo_orderpriority , newColumn ",
+ "Updated **columns**: lo_orderpriority , newColumn ",
threadWithMessages.get(0).getMessage());
}
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/QueryEntity.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/QueryEntity.spec.ts
index b16719709108..01e70e1e6994 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/QueryEntity.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/QueryEntity.spec.ts
@@ -140,7 +140,7 @@ test('Query Entity', async ({ page }) => {
// Update Description
await page.click(`[data-testid="edit-description"]`);
- await page.fill(descriptionBox, 'updated description');
+ await page.locator(descriptionBox).fill('updated description');
const updateDescriptionResponse = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/queries/') &&
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AddRoleAndAssignToUser.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AddRoleAndAssignToUser.spec.ts
index 0158fc12adb7..96ee92ff9295 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AddRoleAndAssignToUser.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AddRoleAndAssignToUser.spec.ts
@@ -53,7 +53,7 @@ test.describe.serial('Add role and assign it to the user', () => {
await page.click('[data-testid="add-role"]');
await page.fill('[data-testid="name"]', roleName);
- await page.fill(descriptionBox, `description for ${roleName}`);
+ await page.locator(descriptionBox).fill(`description for ${roleName}`);
await page.click('[data-testid="policies"]');
await page.click('[title="Data Consumer Policy"]');
@@ -87,7 +87,7 @@ test.describe.serial('Add role and assign it to the user', () => {
await page.fill('[data-testid="email"]', user.email);
await page.fill('[data-testid="displayName"]', userDisplayName);
- await page.fill(descriptionBox, 'Adding user');
+ await page.locator(descriptionBox).fill('Adding user');
const generatePasswordResponse = page.waitForResponse(
`/api/v1/users/generateRandomPwd`
);
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ApiServiceRest.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ApiServiceRest.spec.ts
index d9b46f3d5148..0f66d1dd1f05 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ApiServiceRest.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ApiServiceRest.spec.ts
@@ -40,7 +40,7 @@ test.describe('API service', () => {
// step 1
await page.getByTestId('service-name').fill(apiServiceConfig.name);
- await page.fill(descriptionBox, apiServiceConfig.description);
+ await page.locator(descriptionBox).fill(apiServiceConfig.description);
await page.getByTestId('next-button').click();
// step 2
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts
index 6d767449500c..41d4f38993f2 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts
@@ -90,7 +90,7 @@ test('Table test case', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => {
'#tableTestForm_params_columnName',
NEW_TABLE_TEST_CASE.field
);
- await page.fill(descriptionBox, NEW_TABLE_TEST_CASE.description);
+ await page.locator(descriptionBox).fill(NEW_TABLE_TEST_CASE.description);
await page.click('[data-testid="submit-test"]');
await page.waitForSelector('[data-testid="success-line"]');
@@ -200,7 +200,7 @@ test('Column test case', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => {
'#tableTestForm_params_maxLength',
NEW_COLUMN_TEST_CASE.max
);
- await page.fill(descriptionBox, NEW_COLUMN_TEST_CASE.description);
+ await page.locator(descriptionBox).fill(NEW_COLUMN_TEST_CASE.description);
await page.click('[data-testid="submit-test"]');
await page.waitForSelector('[data-testid="success-line"]');
@@ -395,7 +395,7 @@ test(
// Edit test case description
await page.click(`[data-testid="edit-${testCaseName}"]`);
- await page.fill(descriptionBox, 'Test case description');
+ await page.locator(descriptionBox).fill('Test case description');
const updateTestCaseResponse2 = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testCases/') &&
@@ -407,7 +407,11 @@ test(
expect(body2).toEqual(
JSON.stringify([
- { op: 'add', path: '/description', value: 'Test case description' },
+ {
+ op: 'add',
+ path: '/description',
+ value: 'Test case description
',
+ },
])
);
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/SearchIndexApplication.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/SearchIndexApplication.spec.ts
index 9f8d15d1ef53..b4a0e491b5a0 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/SearchIndexApplication.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/SearchIndexApplication.spec.ts
@@ -114,7 +114,7 @@ test('Search Index Application', async ({ page }) => {
await page.getByTestId('tree-select-widget').click();
// uncheck the entity
- await page.getByRole('tree').getByTitle('Topic').click();
+ await page.getByRole('tree').getByTitle('Table').click();
await page.click(
'[data-testid="select-widget"] > .ant-select-selector > .ant-select-selection-item'
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tag.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tag.spec.ts
index 8ef44d871d39..4cdb8e0f56bc 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tag.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tag.spec.ts
@@ -18,7 +18,12 @@ import { TagClass } from '../../support/tag/TagClass';
import { TeamClass } from '../../support/team/TeamClass';
import { UserClass } from '../../support/user/UserClass';
import { performAdminLogin } from '../../utils/admin';
-import { getApiContext, redirectToHomePage, uuid } from '../../utils/common';
+import {
+ descriptionBox,
+ getApiContext,
+ redirectToHomePage,
+ uuid,
+} from '../../utils/common';
import {
addAssetsToTag,
editTagPageDescription,
@@ -172,9 +177,9 @@ test.describe('Tag Page with Admin Roles', () => {
await expect(adminPage.getByRole('dialog')).toBeVisible();
- await adminPage.locator('.toastui-editor-pseudo-clipboard').clear();
+ await adminPage.locator(descriptionBox).clear();
await adminPage
- .locator('.toastui-editor-pseudo-clipboard')
+ .locator(descriptionBox)
.fill(`This is updated test description for tag ${tag.data.name}.`);
const editDescription = adminPage.waitForResponse(`/api/v1/tags/*`);
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tags.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tags.spec.ts
index 3cfc7619465f..d7ccd9ba9298 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tags.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tags.spec.ts
@@ -225,7 +225,7 @@ test('Classification Page', async ({ page }) => {
'[data-testid="displayName"]',
NEW_CLASSIFICATION.displayName
);
- await page.fill(descriptionBox, NEW_CLASSIFICATION.description);
+ await page.locator(descriptionBox).fill(NEW_CLASSIFICATION.description);
await page.click('[data-testid="mutually-exclusive-button"]');
const createTagCategoryResponse = page.waitForResponse(
@@ -261,7 +261,7 @@ test('Classification Page', async ({ page }) => {
await page.fill('[data-testid="name"]', NEW_TAG.name);
await page.fill('[data-testid="displayName"]', NEW_TAG.displayName);
- await page.fill(descriptionBox, NEW_TAG.description);
+ await page.locator(descriptionBox).fill(NEW_TAG.description);
await page.fill('[data-testid="icon-url"]', NEW_TAG.icon);
await page.fill('[data-testid="tags_color-color-input"]', NEW_TAG.color);
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Teams.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Teams.spec.ts
index 0e8a26acec81..b4ce23d496ae 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Teams.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Teams.spec.ts
@@ -23,6 +23,7 @@ import { performAdminLogin } from '../../utils/admin';
import {
createNewPage,
descriptionBox,
+ descriptionBoxReadOnly,
getApiContext,
redirectToHomePage,
toastNotification,
@@ -303,7 +304,9 @@ test.describe('Teams Page', () => {
// Validating the updated description
await expect(
- page.locator('[data-testid="asset-description-container"] p')
+ page.locator(
+ `[data-testid="asset-description-container"] ${descriptionBoxReadOnly}`
+ )
).toContainText(updatedDescription);
});
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestCases.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestCases.spec.ts
index 56bef624f085..c9b69c526bd3 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestCases.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestCases.spec.ts
@@ -291,10 +291,9 @@ test('Column Values To Be Not Null', async ({ page }) => {
await page.click(
`[data-testid="${NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.type}"]`
);
- await page.fill(
- descriptionBox,
- NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.description
- );
+ await page
+ .locator(descriptionBox)
+ .fill(NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.description);
await page.click('[data-testid="submit-test"]');
await page.waitForSelector('[data-testid="success-line"]');
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestSuite.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestSuite.spec.ts
index 45addd132221..14cde3d2309c 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestSuite.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestSuite.spec.ts
@@ -73,7 +73,7 @@ test('Logical TestSuite', async ({ page }) => {
await test.step('Create', async () => {
await page.click('[data-testid="add-test-suite-btn"]');
await page.fill('[data-testid="test-suite-name"]', NEW_TEST_SUITE.name);
- await page.fill(descriptionBox, NEW_TEST_SUITE.description);
+ await page.locator(descriptionBox).fill(NEW_TEST_SUITE.description);
await page.click('[data-testid="submit-button"]');
const getTestCase = page.waitForResponse(
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/UserDetails.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/UserDetails.spec.ts
index ad32bf9abf85..2c46f488be11 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/UserDetails.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/UserDetails.spec.ts
@@ -155,6 +155,7 @@ test.describe('User with different Roles', () => {
.getByTestId('edit-description')
.click();
+ await userPage.click(descriptionBox);
await userPage.locator(descriptionBox).clear();
const removeUserDescription = userPage.waitForResponse(
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts
index 6b8ae01172a2..2fc799eadfc2 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts
@@ -22,7 +22,11 @@ import { SearchIndexClass } from '../../support/entity/SearchIndexClass';
import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass';
import { TableClass } from '../../support/entity/TableClass';
import { TopicClass } from '../../support/entity/TopicClass';
-import { createNewPage, redirectToHomePage } from '../../utils/common';
+import {
+ createNewPage,
+ descriptionBoxReadOnly,
+ redirectToHomePage,
+} from '../../utils/common';
import { addMultiOwner, assignTier } from '../../utils/entity';
const entities = [
@@ -123,7 +127,7 @@ entities.forEach((EntityClass) => {
await expect(
page.locator(
- '[data-testid="viewer-container"] [data-testid="diff-added"]'
+ `[data-testid="asset-description-container"] ${descriptionBoxReadOnly} [data-testid="diff-added"]`
)
).toBeVisible();
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/ServiceEntityVersionPage.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/ServiceEntityVersionPage.spec.ts
index d083c18c1ecc..c7df9756dd9e 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/ServiceEntityVersionPage.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/ServiceEntityVersionPage.spec.ts
@@ -23,7 +23,11 @@ import { MlmodelServiceClass } from '../../support/entity/service/MlmodelService
import { PipelineServiceClass } from '../../support/entity/service/PipelineServiceClass';
import { SearchIndexServiceClass } from '../../support/entity/service/SearchIndexServiceClass';
import { StorageServiceClass } from '../../support/entity/service/StorageServiceClass';
-import { createNewPage, redirectToHomePage } from '../../utils/common';
+import {
+ createNewPage,
+ descriptionBoxReadOnly,
+ redirectToHomePage,
+} from '../../utils/common';
import { addMultiOwner, assignTier } from '../../utils/entity';
const entities = [
@@ -122,7 +126,7 @@ entities.forEach((EntityClass) => {
await expect(
page.locator(
- '[data-testid="viewer-container"] [data-testid="diff-added"]'
+ `[data-testid="asset-description-container"] ${descriptionBoxReadOnly} [data-testid="diff-added"]`
)
).toBeVisible();
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/alert.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/alert.ts
index e424bc5f885c..37aae920a894 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/alert.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/alert.ts
@@ -37,7 +37,7 @@ import {
toastNotification,
uuid,
} from './common';
-import { getEntityDisplayName } from './entity';
+import { getEntityDisplayName, getTextFromHtmlString } from './entity';
import { validateFormNameFieldInput } from './form';
import {
addFilterWithUsersListInput,
@@ -502,9 +502,9 @@ export const verifyAlertDetails = async ({
);
if (description) {
- // Check alert name
- await expect(page.getByTestId('alert-description')).toContainText(
- description
+ // Check alert description
+ await expect(page.getByTestId('markdown-parser')).toContainText(
+ getTextFromHtmlString(description)
);
}
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
index 446e4220cdec..08bc68ec4d18 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
@@ -19,8 +19,10 @@ import { sidebarClick } from './sidebar';
export const uuid = () => randomUUID().split('-')[0];
-export const descriptionBox =
- '.toastui-editor-md-container > .toastui-editor > .ProseMirror';
+export const descriptionBox = '.om-block-editor[contenteditable="true"]';
+export const descriptionBoxReadOnly =
+ '.om-block-editor[contenteditable="false"]';
+
export const INVALID_NAMES = {
MAX_LENGTH:
'a87439625b1c2d3e4f5061728394a5b6c7d8e90a1b2c3d4e5f67890aba87439625b1c2d3e4f5061728394a5b6c7d8e90a1b2c3d4e5f67890abName can be a maximum of 128 characters',
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts
index 994b61e70e10..64461e0f47be 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts
@@ -21,7 +21,12 @@ import {
ENTITY_PATH,
} from '../support/entity/Entity.interface';
import { UserClass } from '../support/user/UserClass';
-import { clickOutside, descriptionBox, uuid } from './common';
+import {
+ clickOutside,
+ descriptionBox,
+ descriptionBoxReadOnly,
+ uuid,
+} from './common';
export enum CustomPropertyType {
STRING = 'String',
@@ -107,16 +112,9 @@ export const setValueForProperty = async (data: {
const patchRequest = page.waitForResponse(`/api/v1/${endpoint}/*`);
switch (propertyType) {
case 'markdown':
- await page
- .locator(
- '.toastui-editor-md-container > .toastui-editor > .ProseMirror'
- )
- .isVisible();
- await page
- .locator(
- '.toastui-editor-md-container > .toastui-editor > .ProseMirror'
- )
- .fill(value);
+ await page.locator(descriptionBox).isVisible();
+ await page.click(descriptionBox);
+ await page.keyboard.type(value);
await page.locator('[data-testid="save"]').click();
break;
@@ -282,6 +280,10 @@ export const validateValueForProperty = async (data: {
await expect(
page.getByRole('row', { name: `${values[0]} ${values[1]}` })
).toBeVisible();
+ } else if (propertyType === 'markdown') {
+ await expect(
+ container.locator(descriptionBoxReadOnly).last()
+ ).toContainText(value.replace(/\*|_/gi, ''));
} else if (
![
'entityReference',
@@ -681,7 +683,7 @@ export const addCustomPropertiesForEntity = async ({
// Description
- await page.fill(descriptionBox, customPropertyData.description);
+ await page.locator(descriptionBox).fill(customPropertyData.description);
const createPropertyPromise = page.waitForResponse(
'/api/v1/metadata/types/name/*?fields=customProperties'
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts
index 3704966267e1..b769ef573e70 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts
@@ -151,7 +151,7 @@ const fillCommonFormItems = async (
) => {
await page.locator('[data-testid="name"]').fill(entity.name);
await page.locator('[data-testid="display-name"]').fill(entity.displayName);
- await page.fill(descriptionBox, entity.description);
+ await page.locator(descriptionBox).fill(entity.description);
if (!isEmpty(entity.owners) && !isUndefined(entity.owners)) {
await addOwner({
page,
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
index 36043e4573ca..5e54306fe450 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
@@ -24,7 +24,7 @@ import {
} from '../constant/delete';
import { ES_RESERVED_CHARACTERS } from '../constant/entity';
import { EntityTypeEndpoint } from '../support/entity/Entity.interface';
-import { clickOutside, redirectToHomePage } from './common';
+import { clickOutside, descriptionBox, redirectToHomePage } from './common';
export const visitEntityPage = async (data: {
page: Page;
@@ -330,9 +330,9 @@ export const updateDescription = async (
isModal = false
) => {
await page.getByTestId('edit-description').click();
- await page.locator('.ProseMirror').first().click();
- await page.locator('.ProseMirror').first().clear();
- await page.locator('.ProseMirror').first().fill(description);
+ await page.locator(descriptionBox).first().click();
+ await page.locator(descriptionBox).first().clear();
+ await page.locator(descriptionBox).first().fill(description);
await page.getByTestId('save').click();
if (isModal) {
@@ -783,10 +783,7 @@ const announcementForm = async (
await page.fill('#endTime', `${data.endDate}`);
await page.press('#startTime', 'Enter');
- await page.fill(
- '.toastui-editor-md-container > .toastui-editor > .ProseMirror',
- data.description
- );
+ await page.locator(descriptionBox).fill(data.description);
await page.locator('#announcement-submit').scrollIntoViewIfNeeded();
const announcementSubmit = page.waitForResponse(
@@ -1355,3 +1352,16 @@ export const getEntityDisplayName = (entity?: {
}) => {
return entity?.displayName || entity?.name || '';
};
+
+/**
+ *
+ * @param description HTML string
+ * @returns Text from HTML string
+ */
+export const getTextFromHtmlString = (description?: string): string => {
+ if (!description) {
+ return '';
+ }
+
+ return description.replace(/<[^>]*>/g, '').trim();
+};
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts
index 361e19fc0aa0..4d2a85554609 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts
@@ -28,6 +28,7 @@ import { GlossaryTerm } from '../support/glossary/GlossaryTerm';
import {
clickOutside,
closeFirstPopupAlert,
+ descriptionBox,
getApiContext,
INVALID_NAMES,
NAME_MAX_LENGTH_VALIDATION_ERROR,
@@ -47,9 +48,6 @@ type TaskEntity = {
const GLOSSARY_NAME_VALIDATION_ERROR = 'Name size must be between 1 and 128';
-export const descriptionBox =
- '.toastui-editor-md-container > .toastui-editor > .ProseMirror';
-
export const checkDisplayName = async (page: Page, displayName: string) => {
await expect(page.getByTestId('entity-header-display-name')).toHaveText(
displayName
@@ -244,7 +242,7 @@ export const createGlossary = async (
await page.fill('[data-testid="name"]', glossaryData.name);
- await page.fill(descriptionBox, glossaryData.description);
+ await page.locator(descriptionBox).fill(glossaryData.description);
await expect(
page.locator('[data-testid="form-item-alert"]')
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts
index ced6575cb1c1..b1bbed0d03d1 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts
@@ -46,14 +46,9 @@ export const fillDescriptionDetails = async (
description: string
) => {
await page.locator('.InovuaReactDataGrid__cell--cell-active').press('Enter');
- await page.click(
- '.toastui-editor-md-container > .toastui-editor > .ProseMirror'
- );
+ await page.click(descriptionBox);
- await page.fill(
- '.toastui-editor-md-container > .toastui-editor > .ProseMirror',
- description
- );
+ await page.fill(descriptionBox, description);
await page.click('[data-testid="save"]');
await page.click('.InovuaReactDataGrid__cell--cell-active');
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts
index 7252fe61ae90..cffea876f52b 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts
@@ -22,6 +22,7 @@ import { TableClass } from '../support/entity/TableClass';
import { TopicClass } from '../support/entity/TopicClass';
import { TagClass } from '../support/tag/TagClass';
import {
+ descriptionBox,
getApiContext,
NAME_MIN_MAX_LENGTH_VALIDATION_ERROR,
NAME_VALIDATION_ERROR,
@@ -338,9 +339,9 @@ export const editTagPageDescription = async (page: Page, tag: TagClass) => {
await expect(page.getByRole('dialog')).toBeVisible();
- await page.locator('.toastui-editor-pseudo-clipboard').clear();
+ await page.locator(descriptionBox).clear();
await page
- .locator('.toastui-editor-pseudo-clipboard')
+ .locator(descriptionBox)
.fill(`This is updated test description for tag ${tag.data.name}.`);
const editDescription = page.waitForResponse(`/api/v1/tags/*`);
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts
index f77452977e78..503984e340bb 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts
@@ -40,12 +40,8 @@ export const createTeam = async (page: Page, isPublic?: boolean) => {
await page.getByTestId('isJoinable-switch-button').click();
}
- await page
- .locator('.toastui-editor-md-container > .toastui-editor > .ProseMirror')
- .isVisible();
- await page
- .locator('.toastui-editor-md-container > .toastui-editor > .ProseMirror')
- .fill(teamData.description);
+ await page.locator(descriptionBox).isVisible();
+ await page.locator(descriptionBox).fill(teamData.description);
const createTeamResponse = page.waitForResponse('/api/v1/teams');
@@ -226,7 +222,7 @@ export const addTeamHierarchy = async (
await page.click(`.ant-select-dropdown [title="${teamDetails.teamType}"]`);
}
- await page.fill(descriptionBox, teamDetails.description);
+ await page.locator(descriptionBox).fill(teamDetails.description);
// Saving the created team
const saveTeamResponse = page.waitForResponse('/api/v1/teams');
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts
index 20926d5b9576..8a6d184398a3 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts
@@ -30,6 +30,7 @@ import { SidebarItem } from '../constant/sidebar';
import { UserClass } from '../support/user/UserClass';
import {
descriptionBox,
+ descriptionBoxReadOnly,
getAuthContext,
getToken,
redirectToHomePage,
@@ -265,7 +266,7 @@ export const editDescription = async (
// Verify the updated description
const description = page.locator(
- '[data-testid="asset-description-container"] .toastui-editor-contents > p'
+ `[data-testid="asset-description-container"] ${descriptionBoxReadOnly}`
);
await expect(description).toContainText(updatedDescription);
@@ -707,7 +708,7 @@ export const addUser = async (
await page.fill('[data-testid="displayName"]', name);
- await page.fill(descriptionBox, 'Adding new user');
+ await page.locator(descriptionBox).fill('Adding new user');
await page.click(':nth-child(2) > .ant-radio > .ant-radio-input');
await page.fill('#password', password);
diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-block-quote.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-block-quote.svg
new file mode 100644
index 000000000000..a626260650fd
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-block-quote.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-highlight.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-highlight.svg
new file mode 100644
index 000000000000..efeb1f6fce77
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-highlight.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-horizontal-line.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-horizontal-line.svg
new file mode 100644
index 000000000000..aeb9db3e5ad4
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-horizontal-line.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-image-inline.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-image-inline.svg
new file mode 100644
index 000000000000..c67825ead370
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-image-inline.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointSchema/APIEndpointSchema.tsx b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointSchema/APIEndpointSchema.tsx
index 0ebbf5a8e45e..f766cc1e34a1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointSchema/APIEndpointSchema.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointSchema/APIEndpointSchema.tsx
@@ -51,7 +51,7 @@ import {
updateFieldDescription,
updateFieldTags,
} from '../../../utils/TableUtils';
-import RichTextEditorPreviewer from '../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../common/RichTextEditor/RichTextEditorPreviewerV1';
import ToggleExpandButton from '../../common/ToggleExpandButton/ToggleExpandButton';
import { ColumnFilter } from '../../Database/ColumnFilter/ColumnFilter.component';
import TableDescription from '../../Database/TableDescription/TableDescription.component';
@@ -201,7 +201,7 @@ const APIEndpointSchema: FC = ({
{isVersionView ? (
-
+
) : (
getEntityName(record)
)}
@@ -216,7 +216,7 @@ const APIEndpointSchema: FC = ({
(dataType: DataType, record: Field) => (
{isVersionView ? (
-
) : (
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBody.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBody.test.tsx
index f02d1777f7d9..586d18b08c1f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBody.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBody.test.tsx
@@ -37,7 +37,7 @@ jest.mock('../../../../utils/FeedUtils', () => ({
},
}));
-jest.mock('../../../common/RichTextEditor/RichTextEditorPreviewer', () => {
+jest.mock('../../../common/RichTextEditor/RichTextEditorPreviewerV1', () => {
return jest.fn().mockReturnValue(RichText Preview
);
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBody.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBody.tsx
index 796470193d8e..3d8b1ca3c905 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBody.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBody.tsx
@@ -21,7 +21,7 @@ import {
getFrontEndFormat,
MarkdownToHTMLConverter,
} from '../../../../utils/FeedUtils';
-import RichTextEditorPreviewer from '../../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1';
import ActivityFeedEditor from '../../ActivityFeedEditor/ActivityFeedEditor';
import Reactions from '../../Reactions/Reactions';
import { FeedBodyProp } from '../ActivityFeedCard.interface';
@@ -88,7 +88,7 @@ const FeedCardBody: FC = ({
onTextChange={handleMessageUpdate}
/>
) : (
-
@@ -114,7 +114,7 @@ const FeedCardBody: FC = ({
{postMessage}
-
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.tsx
index 499cc9830ca2..0e03ff70b1f1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.tsx
@@ -18,7 +18,6 @@ import React, { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import ActivityFeedEditor from '../../../../components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor';
-import RichTextEditorPreviewer from '../../../../components/common/RichTextEditor/RichTextEditorPreviewer';
import { ASSET_CARD_STYLES } from '../../../../constants/Feeds.constants';
import { EntityType } from '../../../../enums/entity.enum';
import { CardStyle } from '../../../../generated/entity/feed/thread';
@@ -34,6 +33,7 @@ import {
getFrontEndFormat,
MarkdownToHTMLConverter,
} from '../../../../utils/FeedUtils';
+import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1';
import ExploreSearchCard from '../../../ExploreV1/ExploreSearchCard/ExploreSearchCard';
import DescriptionFeed from '../../ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed';
import TagsFeed from '../../ActivityFeedCardV2/FeedCardBody/TagsFeed/TagsFeed';
@@ -118,7 +118,7 @@ const FeedCardBodyV1 = ({
}
return (
-
@@ -192,7 +192,7 @@ const FeedCardBodyV1 = ({
-
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/CustomPropertyFeed/CustomPropertyFeed.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/CustomPropertyFeed/CustomPropertyFeed.component.tsx
index bba62c4d7b94..a65cf2546161 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/CustomPropertyFeed/CustomPropertyFeed.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/CustomPropertyFeed/CustomPropertyFeed.component.tsx
@@ -13,7 +13,7 @@
import React, { useMemo } from 'react';
import { getTextDiffCustomProperty } from '../../../../../utils/EntityVersionUtils';
-import RichTextEditorPreviewer from '../../../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../../../common/RichTextEditor/RichTextEditorPreviewerV1';
import { CustomPropertyFeedProps } from './CustomPropertyFeed.interface';
function CustomPropertyFeed({ feed }: Readonly) {
@@ -28,7 +28,7 @@ function CustomPropertyFeed({ feed }: Readonly) {
);
return (
-
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed.test.tsx
index 8bebd16ed3cb..d860f59b5aa1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed.test.tsx
@@ -20,7 +20,7 @@ import {
} from '../../../../../mocks/DescriptionFeed.mock';
import DescriptionFeed from './DescriptionFeed';
-jest.mock('../../../../common/RichTextEditor/RichTextEditorPreviewer', () => {
+jest.mock('../../../../common/RichTextEditor/RichTextEditorPreviewerV1', () => {
return jest.fn().mockReturnValue(RichTextEditorPreviewer
);
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed.tsx
index f6f72abbc1e7..16a004167aaf 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed.tsx
@@ -19,7 +19,7 @@ import {
getFieldOperationIcon,
getFrontEndFormat,
} from '../../../../../utils/FeedUtils';
-import RichTextEditorPreviewer from '../../../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../../../common/RichTextEditor/RichTextEditorPreviewerV1';
import { DescriptionFeedProps } from './DescriptionFeed.interface';
function DescriptionFeed({ feed }: Readonly) {
@@ -45,7 +45,7 @@ function DescriptionFeed({ feed }: Readonly) {
{operationIcon}
-
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BarMenu/BarMenu.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BarMenu/BarMenu.tsx
new file mode 100644
index 000000000000..d718b61cd9e0
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BarMenu/BarMenu.tsx
@@ -0,0 +1,153 @@
+/*
+ * 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 classNames from 'classnames';
+import { noop, uniqueId } from 'lodash';
+import React, { FC, Fragment } from 'react';
+import BlockQuoteIcon from '../../../assets/svg/ic-format-block-quote.svg';
+import BoldIcon from '../../../assets/svg/ic-format-bold.svg';
+import UnorderedListIcon from '../../../assets/svg/ic-format-bullet-list.svg';
+import CodeBlockIcon from '../../../assets/svg/ic-format-code-block.svg';
+import HighlightIcon from '../../../assets/svg/ic-format-highlight.svg';
+import HorizontalLineIcon from '../../../assets/svg/ic-format-horizontal-line.svg';
+import ImageIcon from '../../../assets/svg/ic-format-image-inline.svg';
+import InlineCodeIcon from '../../../assets/svg/ic-format-inline-code.svg';
+import ItalicIcon from '../../../assets/svg/ic-format-italic.svg';
+import LinkIcon from '../../../assets/svg/ic-format-link.svg';
+import OrderedListIcon from '../../../assets/svg/ic-format-numbered-list.svg';
+import StrikeIcon from '../../../assets/svg/ic-format-strike.svg';
+import { BarMenuProps } from '../BlockEditor.interface';
+import './bar-menu.less';
+
+const BarMenu: FC = ({ editor, onLinkToggle }) => {
+ const formats = [
+ [
+ {
+ name: 'bold',
+ icon: BoldIcon,
+ command: () => editor.chain().focus().toggleBold().run(),
+ isActive: () => editor.isActive('bold'),
+ },
+ {
+ name: 'italic',
+ icon: ItalicIcon,
+ command: () => editor.chain().focus().toggleItalic().run(),
+ isActive: () => editor.isActive('italic'),
+ },
+ {
+ name: 'strike',
+ icon: StrikeIcon,
+ command: () => editor.chain().focus().toggleStrike().run(),
+ isActive: () => editor.isActive('strike'),
+ },
+ ],
+ [
+ {
+ name: 'inline-code',
+ icon: InlineCodeIcon,
+ command: () => editor.chain().focus().toggleCode().run(),
+ isActive: () => editor.isActive('code'),
+ },
+ {
+ name: 'highlight',
+ icon: HighlightIcon,
+ command: () => noop,
+ isActive: () => editor.isActive('highlight'),
+ },
+ ],
+ [
+ {
+ name: 'unordered-list',
+ icon: UnorderedListIcon,
+ command: () => editor.chain().focus().toggleBulletList().run(),
+ isActive: () => editor.isActive('bulletList'),
+ },
+ {
+ name: 'ordered-list',
+ icon: OrderedListIcon,
+ command: () => editor.chain().focus().toggleOrderedList().run(),
+ isActive: () => editor.isActive('orderedList'),
+ },
+ ],
+ [
+ {
+ name: 'link',
+ icon: LinkIcon,
+ command: () => {
+ editor.chain().focus().setLink({ href: '' }).run();
+ onLinkToggle?.();
+ },
+ isActive: () => editor.isActive('link'),
+ },
+ {
+ name: 'image',
+ icon: ImageIcon,
+ command: () => editor.chain().focus().setImage({ src: '' }).run(),
+ isActive: () => editor.isActive('image'),
+ },
+ {
+ name: 'code-block',
+ icon: CodeBlockIcon,
+ command: () => editor.chain().focus().toggleCodeBlock().run(),
+ isActive: () => editor.isActive('codeBlock'),
+ },
+ {
+ name: 'block-quote',
+ icon: BlockQuoteIcon,
+ command: () => editor.chain().focus().toggleBlockquote().run(),
+ isActive: () => editor.isActive('blockquote'),
+ },
+ {
+ name: 'horizontal-line',
+ icon: HorizontalLineIcon,
+ command: () => editor.chain().focus().setHorizontalRule().run(),
+ isActive: () => false,
+ },
+ ],
+ ];
+
+ return (
+
+ {formats.map((format, index) => {
+ return (
+
+
+ {format.map((item) => {
+ return (
+
+
+
+ );
+ })}
+
+ {index !== formats.length - 1 && (
+
+ )}
+
+ );
+ })}
+
+ );
+};
+
+export default BarMenu;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BarMenu/bar-menu.less b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BarMenu/bar-menu.less
new file mode 100644
index 000000000000..1dcb7bcef7c1
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BarMenu/bar-menu.less
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+@button-bg-color-hover: #ecedee;
+@button-bg-color: #e8e8e9;
+@format-group-separator-color: #e5e7eb;
+@menu-bg-color: #f7f9fc;
+
+.bar-menu-wrapper {
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ background-color: @menu-bg-color;
+ display: flex;
+ flex-direction: row;
+ padding: 8px;
+ border-bottom: 1px solid @format-group-separator-color;
+ gap: 1rem;
+
+ .bar-menu-wrapper--format-group {
+ display: flex;
+ flex-direction: row;
+ gap: 0.5rem;
+
+ .bar-menu-wrapper--format-group--button {
+ background-color: transparent;
+ border: none;
+ border-radius: 0.375rem;
+ cursor: pointer;
+
+ &:hover {
+ background-color: @button-bg-color-hover;
+ }
+
+ &.active {
+ background-color: @button-bg-color;
+ }
+ .bar-menu-wrapper--format--button--icon {
+ width: 28px;
+ height: 28px;
+ }
+ }
+ }
+
+ .bar-menu-wrapper--format-group--separator {
+ border-left: 1px solid @format-group-separator-color;
+ }
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.interface.ts
index dba44a126c7c..2ebf3a73696f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.interface.ts
@@ -10,8 +10,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { Editor } from '@tiptap/react';
import { SuggestionKeyDownProps } from '@tiptap/suggestion';
+export type MenuType = 'bubble' | 'bar';
+
export interface SuggestionItem {
id: string;
name: string;
@@ -27,4 +30,27 @@ export interface ExtensionRef {
export interface EditorSlotsRef {
onMouseDown: (e: React.MouseEvent) => void;
+ onLinkToggle: () => void;
+}
+
+export interface BlockEditorRef {
+ editor: Editor | null;
+}
+export interface BlockEditorProps {
+ content?: string;
+ editable?: boolean;
+ onChange?: (htmlContent: string) => void;
+ menuType?: MenuType;
+ autoFocus?: boolean;
+ placeholder?: string;
+}
+
+export interface EditorSlotsProps {
+ editor: Editor | null;
+ menuType: MenuType;
+}
+
+export interface BarMenuProps {
+ editor: Editor;
+ onLinkToggle?: () => void;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.tsx
index 5c45c975c317..3ae7f649474e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.tsx
@@ -10,7 +10,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Editor, EditorContent } from '@tiptap/react';
+import { EditorContent } from '@tiptap/react';
+import classNames from 'classnames';
import { isNil } from 'lodash';
import React, {
forwardRef,
@@ -21,26 +22,33 @@ import React, {
import { useTranslation } from 'react-i18next';
import { EDITOR_OPTIONS } from '../../constants/BlockEditor.constants';
import { formatContent, setEditorContent } from '../../utils/BlockEditorUtils';
+import BarMenu from './BarMenu/BarMenu';
import './block-editor.less';
-import { EditorSlotsRef } from './BlockEditor.interface';
+import {
+ BlockEditorProps,
+ BlockEditorRef,
+ EditorSlotsRef,
+} from './BlockEditor.interface';
import EditorSlots from './EditorSlots';
import { extensions } from './Extensions';
import { useCustomEditor } from './hooks/useCustomEditor';
-export interface BlockEditorRef {
- editor: Editor | null;
-}
-export interface BlockEditorProps {
- content?: string;
- editable?: boolean;
- onChange?: (htmlContent: string) => void;
-}
-
const BlockEditor = forwardRef(
- ({ content = '', editable = true, onChange }, ref) => {
+ (
+ {
+ content = '',
+ editable = true,
+ menuType = 'bubble',
+ autoFocus,
+ placeholder,
+ onChange,
+ },
+ ref
+ ) => {
const { i18n } = useTranslation();
const editorSlots = useRef(null);
+ // this hook to initialize the editor
const editor = useCustomEditor({
...EDITOR_OPTIONS,
extensions,
@@ -54,14 +62,18 @@ const BlockEditor = forwardRef(
editorProps: {
attributes: {
class: 'om-block-editor',
+ ...(autoFocus ? { autofocus: 'true' } : {}),
},
},
+ autofocus: autoFocus,
});
+ // this hook to expose the editor instance
useImperativeHandle(ref, () => ({
editor,
}));
+ // this effect to handle the content change
useEffect(() => {
if (isNil(editor) || editor.isDestroyed || content === undefined) {
return;
@@ -77,6 +89,7 @@ const BlockEditor = forwardRef(
});
}, [content, editor]);
+ // this effect to handle the editable state
useEffect(() => {
if (
isNil(editor) ||
@@ -91,6 +104,7 @@ const BlockEditor = forwardRef(
setTimeout(() => editor.setEditable(editable));
}, [editable, editor]);
+ // this effect to handle the RTL and LTR direction
useEffect(() => {
const editorWrapper = document.getElementById('block-editor-wrapper');
if (!editorWrapper) {
@@ -106,12 +120,24 @@ const BlockEditor = forwardRef(
}, [i18n]);
return (
-
+
+ {menuType === 'bar' && !isNil(editor) && (
+
+ )}
-
+
);
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/EditorSlots.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/EditorSlots.tsx
index e3f56d35d120..cb4a54a61528 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/EditorSlots.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/EditorSlots.tsx
@@ -14,19 +14,15 @@ import { Editor, ReactRenderer } from '@tiptap/react';
import { isEmpty, isNil } from 'lodash';
import React, { forwardRef, useImperativeHandle, useState } from 'react';
import tippy, { Instance, Props } from 'tippy.js';
-import { EditorSlotsRef } from './BlockEditor.interface';
+import { EditorSlotsProps, EditorSlotsRef } from './BlockEditor.interface';
import BlockMenu from './BlockMenu/BlockMenu';
import BubbleMenu from './BubbleMenu/BubbleMenu';
import LinkModal, { LinkData } from './LinkModal/LinkModal';
import LinkPopup from './LinkPopup/LinkPopup';
import TableMenu from './TableMenu/TableMenu';
-interface EditorSlotsProps {
- editor: Editor | null;
-}
-
const EditorSlots = forwardRef
(
- ({ editor }, ref) => {
+ ({ editor, menuType }, ref) => {
const [isLinkModalOpen, setIsLinkModalOpen] = useState(false);
const handleLinkToggle = () => {
@@ -142,12 +138,17 @@ const EditorSlots = forwardRef(
}
};
- const menus = !isNil(editor) && (
+ /**
+ * render the bubble menu only if the editor is available
+ * and the menu type is bubble
+ */
+ const menus = !isNil(editor) && menuType === 'bubble' && (
);
useImperativeHandle(ref, () => ({
onMouseDown: handleLinkPopup,
+ onLinkToggle: handleLinkToggle,
}));
if (isNil(editor)) {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/diff-view.ts b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/diff-view.ts
index 0aa240200fcb..5533bc669308 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/diff-view.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/diff-view.ts
@@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { mergeAttributes, Node } from '@tiptap/core';
+import { Node } from '@tiptap/core';
export default Node.create({
name: 'diffView',
@@ -23,18 +23,55 @@ export default Node.create({
class: {
default: '',
},
+ 'data-testid': {
+ default: '',
+ parseHTML: (element) => element.getAttribute('data-testid'),
+ renderHTML: (attributes) => {
+ if (!attributes['data-testid']) {
+ return {};
+ }
+
+ return {
+ 'data-testid': attributes['data-testid'],
+ };
+ },
+ },
+ 'data-diff': {
+ default: true,
+ parseHTML: (element) => element.getAttribute('data-diff'),
+ renderHTML: (attributes) => {
+ if (!attributes['data-diff']) {
+ return {};
+ }
+
+ return {
+ 'data-diff': attributes['data-diff'],
+ };
+ },
+ },
};
},
parseHTML() {
return [
{
- tag: 'diff-view',
+ tag: 'span[data-diff]',
},
];
},
- renderHTML({ HTMLAttributes }) {
- return ['diff-view', mergeAttributes(HTMLAttributes), 0];
+ renderHTML({ HTMLAttributes, node }) {
+ const diffNode = document.createElement('span');
+
+ Object.keys(HTMLAttributes).forEach((key) => {
+ diffNode.setAttribute(key, HTMLAttributes[key]);
+ });
+
+ diffNode.setAttribute('data-diff', 'true');
+ diffNode.innerHTML = node.textContent;
+
+ return {
+ dom: diffNode,
+ };
},
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/block-editor.less b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/block-editor.less
index b3853974b9f4..6141fa7ff5bc 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/block-editor.less
+++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/block-editor.less
@@ -203,6 +203,28 @@
.tippy-box {
max-width: 400px !important;
}
+
+ &.block-editor-wrapper--bar-menu {
+ border: 1px solid #dadde6;
+ border-radius: 4px;
+ .om-block-editor {
+ max-height: 300px;
+ overflow: auto;
+ padding-left: 50px;
+ padding-right: 50px;
+ padding-top: 8px;
+ &:focus-visible {
+ outline: none;
+ }
+ }
+ }
+
+ // if contenteditable is false, remove padding-bottom from last p tag
+ .om-block-editor[contenteditable='false'] {
+ > p:last-child {
+ padding-bottom: 0;
+ }
+ }
}
.menu-wrapper {
@@ -388,7 +410,7 @@
width: 100%;
}
}
-.om-image-node-embed-link-btn-col {
+body .om-image-node-embed-link-btn-col {
display: flex;
justify-content: flex-end;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerChildren/ContainerChildren.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerChildren/ContainerChildren.test.tsx
index d21604ae6abe..6a1914b02a77 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerChildren/ContainerChildren.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerChildren/ContainerChildren.test.tsx
@@ -39,6 +39,15 @@ const mockDataProps = {
};
describe('ContainerChildren', () => {
+ beforeEach(() => {
+ jest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ jest.runOnlyPendingTimers();
+ jest.useRealTimers();
+ });
+
it('Should call fetch container function on load', () => {
render(
@@ -88,6 +97,9 @@ describe('ContainerChildren', () => {
);
+ // Fast-forward until all timers have been executed
+ jest.runAllTimers();
+
const richTextPreviewers = screen.getAllByTestId('viewer-container');
expect(richTextPreviewers).toHaveLength(2);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerChildren/ContainerChildren.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerChildren/ContainerChildren.tsx
index 405187c63ea9..30892f78a478 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerChildren/ContainerChildren.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerChildren/ContainerChildren.tsx
@@ -21,7 +21,7 @@ import { Container } from '../../../generated/entity/data/container';
import { EntityReference } from '../../../generated/type/entityReference';
import { getColumnSorter, getEntityName } from '../../../utils/EntityUtils';
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
-import RichTextEditorPreviewer from '../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../common/RichTextEditor/RichTextEditorPreviewerV1';
import Table from '../../common/Table/Table';
interface ContainerChildrenProps {
@@ -66,7 +66,7 @@ const ContainerChildren: FC = ({
render: (description: EntityReference['description']) => (
<>
{description ? (
-
+
) : (
{t('label.no-entity', {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.test.tsx
index 6511b6fec06f..0c1372021156 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.test.tsx
@@ -110,7 +110,7 @@ jest.mock('../../../utils/ContainerDetailUtils', () => ({
}));
jest.mock(
- '../../../components/common/RichTextEditor/RichTextEditorPreviewer',
+ '../../../components/common/RichTextEditor/RichTextEditorPreviewerV1',
() =>
jest
.fn()
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardDetails/DashboardDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardDetails/DashboardDetails.test.tsx
index 3b74b8e89cba..a87e3f79ea4a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardDetails/DashboardDetails.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardDetails/DashboardDetails.test.tsx
@@ -99,7 +99,7 @@ jest.mock('../../common/TabsLabel/TabsLabel.component', () => {
jest.mock('../../common/EntityDescription/DescriptionV1', () => {
return jest.fn().mockReturnValue(Description Component
);
});
-jest.mock('../../common/RichTextEditor/RichTextEditorPreviewer', () => {
+jest.mock('../../common/RichTextEditor/RichTextEditorPreviewerV1', () => {
return jest.fn().mockReturnValue(RichTextEditorPreviwer
);
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.component.tsx
index 395002a904bb..ef13f05e6b3c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.component.tsx
@@ -39,7 +39,7 @@ import {
import { CustomPropertyTable } from '../../common/CustomPropertyTable/CustomPropertyTable';
import DescriptionV1 from '../../common/EntityDescription/DescriptionV1';
import Loader from '../../common/Loader/Loader';
-import RichTextEditorPreviewer from '../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../common/RichTextEditor/RichTextEditorPreviewerV1';
import TabsLabel from '../../common/TabsLabel/TabsLabel.component';
import DataAssetsVersionHeader from '../../DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader';
import DataProductsContainer from '../../DataProducts/DataProductsContainer/DataProductsContainer.component';
@@ -133,7 +133,7 @@ const DashboardVersion: FC = ({
key: 'description',
render: (text) =>
text ? (
-
+
) : (
{t('label.no-description')}
),
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.test.tsx
index 2943e2cf4e75..19a9e725fe85 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.test.tsx
@@ -26,7 +26,7 @@ import { DashboardVersionProp } from './DashboardVersion.interface';
const mockPush = jest.fn();
-jest.mock('../../common/RichTextEditor/RichTextEditorPreviewer', () => {
+jest.mock('../../common/RichTextEditor/RichTextEditorPreviewerV1', () => {
return jest
.fn()
.mockImplementation(() => RichTextEditorPreviewer.component
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelsTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelsTable.tsx
index 1ac09ad1089b..fd72c640b8e8 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelsTable.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelsTable.tsx
@@ -36,7 +36,7 @@ import { showErrorToast } from '../../../../utils/ToastUtils';
import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import NextPrevious from '../../../common/NextPrevious/NextPrevious';
import { NextPreviousProps } from '../../../common/NextPrevious/NextPrevious.interface';
-import RichTextEditorPreviewer from '../../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1';
import Table from '../../../common/Table/Table';
const DataModelTable = () => {
@@ -86,7 +86,7 @@ const DataModelTable = () => {
key: 'description',
render: (description: ServicePageData['description']) =>
!isUndefined(description) && description.trim() ? (
-
+
) : (
{t('label.no-entity', {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/EditTestCaseModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/EditTestCaseModal.tsx
index 645a2914ded2..d81dd6a40776 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/EditTestCaseModal.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/EditTestCaseModal.tsx
@@ -323,7 +323,6 @@ const EditTestCaseModal: React.FC = ({
trigger="onTextChange"
valuePropName="initialValue">
= ({
name="description"
trigger="onTextChange">
();
const [isLoading, setIsLoading] = useState(false);
const [options, setOptions] = useState([]);
@@ -216,7 +214,6 @@ export const TestCaseStatusModal = ({
},
]}>
form.setFieldValue(
[
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/AddTestSuiteForm/AddTestSuiteForm.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/AddTestSuiteForm/AddTestSuiteForm.tsx
index bd6a7621f19b..2b587d39bd50 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/AddTestSuiteForm/AddTestSuiteForm.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/AddTestSuiteForm/AddTestSuiteForm.tsx
@@ -104,7 +104,6 @@ const AddTestSuiteForm: React.FC = ({
form.setFieldsValue({ description: value })}
/>
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/DatabaseSchema/DatabaseSchemaTable/DatabaseSchemaTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/DatabaseSchema/DatabaseSchemaTable/DatabaseSchemaTable.tsx
index 64148e9108e6..19858045499e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/DatabaseSchema/DatabaseSchemaTable/DatabaseSchemaTable.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/DatabaseSchema/DatabaseSchemaTable/DatabaseSchemaTable.tsx
@@ -48,7 +48,7 @@ import DisplayName from '../../../common/DisplayName/DisplayName';
import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import NextPrevious from '../../../common/NextPrevious/NextPrevious';
import { PagingHandlerParams } from '../../../common/NextPrevious/NextPrevious.interface';
-import RichTextEditorPreviewer from '../../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1';
import Searchbar from '../../../common/SearchBarComponent/SearchBar.component';
import Table from '../../../common/Table/Table';
import { EntityName } from '../../../Modals/EntityNameModal/EntityNameModal.interface';
@@ -244,7 +244,7 @@ export const DatabaseSchemaTable = ({
key: 'description',
render: (text: string) =>
text?.trim() ? (
-
+
) : (
{t('label.no-entity', { entity: t('label.description') })}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnSummary.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnSummary.tsx
index 1b7758190ae4..a0967669f6fa 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnSummary.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnSummary.tsx
@@ -15,7 +15,7 @@ import { isEmpty } from 'lodash';
import React, { FC } from 'react';
import { Column } from '../../../../generated/entity/data/container';
import { getEntityName } from '../../../../utils/EntityUtils';
-import RichTextEditorPreviewer from '../../../common/RichTextEditor/RichTextEditorPreviewer';
+import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1';
import TagsViewer from '../../../Tag/TagsViewer/TagsViewer';
interface ColumnSummaryProps {
@@ -31,7 +31,7 @@ const ColumnSummary: FC