diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider.tsx index 9ac6f281090..06618f2e4c3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider.tsx @@ -137,7 +137,7 @@ const ActivityFeedProvider = ({ children, user }: Props) => { setLoading(false); } }, - [] + [currentUser, user] ); // Here value is the post message and id can be thread id or post id. diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicSchema/TopicSchema.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicSchema/TopicSchema.tsx index e631b7d2fc1..b0333e07002 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicSchema/TopicSchema.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicSchema/TopicSchema.tsx @@ -275,16 +275,14 @@ const TopicSchemaFields: FC = ({ return ( - - + {messageSchema?.schemaType && ( + {t('label.schema')} - {schemaTypePlaceholder ?? ( - {messageSchema?.schemaType ?? ''} - )} - - + {schemaTypePlaceholder ?? {messageSchema.schemaType}} + + )} {isEmpty(messageSchema?.schemaFields) && isEmpty(messageSchema?.schemaText) ? ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx new file mode 100644 index 00000000000..d7616ce71cf --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx @@ -0,0 +1,320 @@ +/* + * 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 { act, render, screen } from '@testing-library/react'; +import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider'; +import React from 'react'; +import { getTableDetailsByFQN } from 'rest/tableAPI'; +import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils'; +import TableDetailsPageV1 from './TableDetailsPageV1'; + +const mockEntityPermissionByFqn = jest + .fn() + .mockImplementation(() => DEFAULT_ENTITY_PERMISSION); + +jest.mock('components/PermissionProvider/PermissionProvider', () => ({ + usePermissionProvider: jest.fn().mockImplementation(() => ({ + getEntityPermissionByFqn: mockEntityPermissionByFqn, + })), +})); + +jest.mock('rest/tableAPI', () => ({ + getTableDetailsByFQN: jest.fn().mockImplementation(() => + Promise.resolve({ + name: 'test', + id: '123', + }) + ), + addFollower: jest.fn(), + patchTableDetails: jest.fn(), + removeFollower: jest.fn(), + restoreTable: jest.fn(), +})); + +jest.mock('utils/CommonUtils', () => ({ + getCurrentUserId: jest.fn(), + getFeedCounts: jest.fn(), + getPartialNameFromTableFQN: jest.fn().mockImplementation(() => 'fqn'), + getTableFQNFromColumnFQN: jest.fn(), + refreshPage: jest.fn(), + sortTagsCaseInsensitive: jest.fn(), +})); + +jest.mock('rest/queryAPI', () => ({ + getQueriesList: jest.fn(), +})); + +jest.mock( + 'components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.component', + () => ({ + ActivityFeedTab: jest + .fn() + .mockImplementation(() =>

testActivityFeedTab

), + }) +); + +jest.mock( + 'components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel', + () => { + return jest.fn().mockImplementation(() =>

testActivityThreadPanel

); + } +); + +jest.mock('components/common/description/DescriptionV1', () => { + return jest.fn().mockImplementation(() =>

testDescriptionV1

); +}); +jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () => { + return jest.fn().mockImplementation(() =>

testErrorPlaceHolder

); +}); + +jest.mock('components/common/QueryViewer/QueryViewer.component', () => { + return jest.fn().mockImplementation(() =>

testQueryViewer

); +}); + +jest.mock('components/containers/PageLayoutV1', () => { + return jest.fn().mockImplementation(({ children }) =>

{children}

); +}); + +jest.mock( + 'components/DataAssets/DataAssetsHeader/DataAssetsHeader.component', + () => ({ + DataAssetsHeader: jest + .fn() + .mockImplementation(() =>

testDataAssetsHeader

), + }) +); + +jest.mock('components/Entity/EntityLineage/EntityLineage.component', () => { + return jest.fn().mockImplementation(() =>

testEntityLineage

); +}); + +jest.mock('components/SampleDataTable/SampleDataTable.component', () => { + return jest.fn().mockImplementation(() =>

testSampleDataTable

); +}); + +jest.mock('components/SchemaTab/SchemaTab.component', () => { + return jest.fn().mockImplementation(() =>

testSchemaTab

); +}); + +jest.mock('components/TableProfiler/TableProfilerV1', () => { + return jest.fn().mockImplementation(() =>

testTableProfilerV1

); +}); + +jest.mock('components/TableQueries/TableQueries', () => { + return jest.fn().mockImplementation(() =>

testTableQueries

); +}); + +jest.mock('components/TabsLabel/TabsLabel.component', () => { + return jest.fn().mockImplementation(({ name }) =>

{name}

); +}); + +jest.mock('components/Tag/TagsContainerV2/TagsContainerV2', () => { + return jest.fn().mockImplementation(() =>

testTagsContainerV2

); +}); + +jest.mock('./FrequentlyJoinedTables/FrequentlyJoinedTables.component', () => ({ + FrequentlyJoinedTables: jest + .fn() + .mockImplementation(() =>

testFrequentlyJoinedTables

), +})); + +jest.mock('./FrequentlyJoinedTables/FrequentlyJoinedTables.component', () => ({ + FrequentlyJoinedTables: jest + .fn() + .mockImplementation(() =>

testFrequentlyJoinedTables

), +})); + +jest.mock( + 'components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider', + () => ({ + useActivityFeedProvider: jest.fn().mockImplementation(() => ({ + postFeed: jest.fn(), + deleteFeed: jest.fn(), + updateFeed: jest.fn(), + })), + __esModule: true, + default: 'ActivityFeedProvider', + }) +); + +jest.mock('react-router-dom', () => ({ + useParams: jest + .fn() + .mockImplementation(() => ({ datasetFQN: 'fqn', tab: 'schema' })), + useHistory: jest.fn().mockImplementation(() => ({})), +})); + +jest.mock('components/TourProvider/TourProvider', () => ({ + useTourProvider: jest.fn().mockImplementation(() => ({ + isTourOpen: false, + activeTabForTourDatasetPage: 'schema', + isTourPage: false, + })), +})); + +jest.mock('components/Loader/Loader', () => { + return jest.fn().mockImplementation(() => <>testLoader); +}); + +jest.useFakeTimers(); + +describe('TestDetailsPageV1 component', () => { + it('TableDetailsPageV1 should fetch permissions', () => { + render(); + + expect(mockEntityPermissionByFqn).toHaveBeenCalledWith('table', 'fqn'); + }); + + it('TableDetailsPageV1 should not fetch table details if permission is there', () => { + render(); + + expect(getTableDetailsByFQN).not.toHaveBeenCalled(); + }); + + it('TableDetailsPageV1 should fetch table details with basic fields', async () => { + (usePermissionProvider as jest.Mock).mockImplementationOnce(() => ({ + getEntityPermissionByFqn: jest.fn().mockImplementationOnce(() => ({ + ViewBasic: true, + })), + })); + + await act(async () => { + render(); + }); + + expect(getTableDetailsByFQN).toHaveBeenCalledWith( + 'fqn', + 'columns,followers,joins,tags,owner,dataModel,tableConstraints,extension,viewDefinition' + ); + }); + + it('TableDetailsPageV1 should fetch table details with all the permitted fields', async () => { + (usePermissionProvider as jest.Mock).mockImplementationOnce(() => ({ + getEntityPermissionByFqn: jest.fn().mockImplementationOnce(() => ({ + ViewAll: true, + ViewBasic: true, + ViewUsage: true, + })), + })); + + await act(async () => { + render(); + }); + + expect(getTableDetailsByFQN).toHaveBeenCalledWith( + 'fqn', + 'columns,followers,joins,tags,owner,dataModel,tableConstraints,extension,viewDefinition,usageSummary,testSuite' + ); + }); + + it('TableDetailsPageV1 should render page for ViewBasic permissions', async () => { + (usePermissionProvider as jest.Mock).mockImplementationOnce(() => ({ + getEntityPermissionByFqn: jest.fn().mockImplementationOnce(() => ({ + ViewBasic: true, + })), + })); + + await act(async () => { + render(); + }); + + expect(getTableDetailsByFQN).toHaveBeenCalledWith( + 'fqn', + 'columns,followers,joins,tags,owner,dataModel,tableConstraints,extension,viewDefinition' + ); + + expect(await screen.findByText('testDataAssetsHeader')).toBeInTheDocument(); + expect(await screen.findByText('label.schema')).toBeInTheDocument(); + expect( + await screen.findByText('label.activity-feed-and-task-plural') + ).toBeInTheDocument(); + expect(await screen.findByText('label.sample-data')).toBeInTheDocument(); + expect(await screen.findByText('label.query-plural')).toBeInTheDocument(); + expect(await screen.findByText('label.lineage')).toBeInTheDocument(); + + expect( + await screen.findByText('label.custom-property-plural') + ).toBeInTheDocument(); + expect( + await screen.findByText('label.profiler-amp-data-quality') + ).toBeInTheDocument(); + }); + + it('TableDetailsPageV1 should dbt tab if data is present', async () => { + (usePermissionProvider as jest.Mock).mockImplementationOnce(() => ({ + getEntityPermissionByFqn: jest.fn().mockImplementationOnce(() => ({ + ViewBasic: true, + })), + })); + + (getTableDetailsByFQN as jest.Mock).mockImplementationOnce(() => + Promise.resolve({ + name: 'test', + id: '123', + tableFqn: 'fqn', + dataModel: { sql: 'somequery' }, + }) + ); + + await act(async () => { + render(); + }); + + expect(await screen.findByText('label.dbt-lowercase')).toBeInTheDocument(); + expect(screen.queryByText('label.view-definition')).not.toBeInTheDocument(); + }); + + it('TableDetailsPageV1 should render view defination if data is present', async () => { + (usePermissionProvider as jest.Mock).mockImplementationOnce(() => ({ + getEntityPermissionByFqn: jest.fn().mockImplementationOnce(() => ({ + ViewBasic: true, + })), + })); + + (getTableDetailsByFQN as jest.Mock).mockImplementationOnce(() => + Promise.resolve({ + name: 'test', + id: '123', + viewDefinition: 'viewDefinition', + }) + ); + + await act(async () => { + render(); + }); + + expect(screen.queryByText('label.dbt-lowercase')).not.toBeInTheDocument(); + + expect( + await screen.findByText('label.view-definition') + ).toBeInTheDocument(); + }); + + it('TableDetailsPageV1 should render schemaTab by default', async () => { + (usePermissionProvider as jest.Mock).mockImplementationOnce(() => ({ + getEntityPermissionByFqn: jest.fn().mockImplementationOnce(() => ({ + ViewBasic: true, + })), + })); + + await act(async () => { + render(); + }); + + expect(getTableDetailsByFQN).toHaveBeenCalledWith( + 'fqn', + 'columns,followers,joins,tags,owner,dataModel,tableConstraints,extension,viewDefinition' + ); + + expect(await screen.findByText('testSchemaTab')).toBeInTheDocument(); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DatasetDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DatasetDetailsUtils.ts index 91bd06354ac..52fb76c16f2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DatasetDetailsUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DatasetDetailsUtils.ts @@ -1,5 +1,5 @@ /* - * Copyright 2022 Collate. + * 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 @@ -13,6 +13,5 @@ import { TabSpecificField } from '../enums/entity.enum'; -export const defaultFields = `${TabSpecificField.COLUMNS}, -${TabSpecificField.FOLLOWERS}, ${TabSpecificField.JOINS}, ${TabSpecificField.TAGS}, ${TabSpecificField.OWNER}, -${TabSpecificField.DATAMODEL},${TabSpecificField.TABLE_CONSTRAINTS},${TabSpecificField.EXTENSION},${TabSpecificField.VIEW_DEFINITION}`; +// eslint-disable-next-line max-len +export const defaultFields = `${TabSpecificField.COLUMNS},${TabSpecificField.FOLLOWERS},${TabSpecificField.JOINS},${TabSpecificField.TAGS},${TabSpecificField.OWNER},${TabSpecificField.DATAMODEL},${TabSpecificField.TABLE_CONSTRAINTS},${TabSpecificField.EXTENSION},${TabSpecificField.VIEW_DEFINITION}`;