mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-31 18:48:35 +00:00 
			
		
		
		
	fix(ui): all assets not showing on my data widget (#13703)
* changed the api call to fetch owned assets in my data widget * fixed unit tests
This commit is contained in:
		
							parent
							
								
									de544d2dca
								
							
						
					
					
						commit
						a72fcec57f
					
				| @ -17,15 +17,20 @@ import { observer } from 'mobx-react'; | ||||
| import React, { useCallback, useEffect, useState } from 'react'; | ||||
| import { useTranslation } from 'react-i18next'; | ||||
| import { Link } from 'react-router-dom'; | ||||
| import AppState from '../../../AppState'; | ||||
| import { getUserPath, ROUTES } from '../../../constants/constants'; | ||||
| import { AssetsType } from '../../../enums/entity.enum'; | ||||
| import { EntityReference } from '../../../generated/entity/type'; | ||||
| import { | ||||
|   getUserPath, | ||||
|   INITIAL_PAGING_VALUE, | ||||
|   PAGE_SIZE, | ||||
|   ROUTES, | ||||
| } from '../../../constants/constants'; | ||||
| import { SearchIndex } from '../../../enums/search.enum'; | ||||
| import { WidgetCommonProps } from '../../../pages/CustomizablePage/CustomizablePage.interface'; | ||||
| import { getUserById } from '../../../rest/userAPI'; | ||||
| import { searchData } from '../../../rest/miscAPI'; | ||||
| import { Transi18next } from '../../../utils/CommonUtils'; | ||||
| import { getEntityName } from '../../../utils/EntityUtils'; | ||||
| import { getEntityIcon, getEntityLink } from '../../../utils/TableUtils'; | ||||
| import { useAuthContext } from '../../authentication/auth-provider/AuthProvider'; | ||||
| import { SourceType } from '../../searched-data/SearchedData.interface'; | ||||
| import EntityListSkeleton from '../../Skeleton/MyData/EntityListSkeleton/EntityListSkeleton.component'; | ||||
| import './MyDataWidget.less'; | ||||
| 
 | ||||
| @ -35,34 +40,43 @@ const MyDataWidgetInternal = ({ | ||||
|   widgetKey, | ||||
| }: WidgetCommonProps) => { | ||||
|   const { t } = useTranslation(); | ||||
|   const currentUserDetails = AppState.getCurrentUserDetails(); | ||||
|   const { currentUser } = useAuthContext(); | ||||
|   const [isLoading, setIsLoading] = useState(true); | ||||
|   const [data, setData] = useState<EntityReference[]>([]); | ||||
|   const [data, setData] = useState<SourceType[]>([]); | ||||
|   const [totalOwnedAssetsCount, setTotalOwnedAssetsCount] = useState<number>(0); | ||||
| 
 | ||||
|   const fetchMyDataAssets = async () => { | ||||
|     if (!currentUserDetails || !currentUserDetails.id) { | ||||
|       return; | ||||
|     } | ||||
|     setIsLoading(true); | ||||
|     try { | ||||
|       const userData = await getUserById(currentUserDetails?.id, 'owns'); | ||||
|     if (!isUndefined(currentUser)) { | ||||
|       setIsLoading(true); | ||||
|       try { | ||||
|         const teamsIds = (currentUser.teams ?? []).map((team) => team.id); | ||||
|         const mergedIds = [ | ||||
|           ...teamsIds.map((id) => `owner.id:${id}`), | ||||
|           `owner.id:${currentUser.id}`, | ||||
|         ].join(' OR '); | ||||
| 
 | ||||
|       if (userData) { | ||||
|         const includeData = Object.values(AssetsType); | ||||
|         const owns: EntityReference[] = userData.owns ?? []; | ||||
| 
 | ||||
|         const includedOwnsData = owns.filter((data) => | ||||
|           includeData.includes(data.type as AssetsType) | ||||
|         const queryFilter = `(${mergedIds})`; | ||||
|         const res = await searchData( | ||||
|           '', | ||||
|           INITIAL_PAGING_VALUE, | ||||
|           PAGE_SIZE, | ||||
|           queryFilter, | ||||
|           '', | ||||
|           '', | ||||
|           SearchIndex.ALL | ||||
|         ); | ||||
| 
 | ||||
|         setData(includedOwnsData.slice(0, 8)); | ||||
|         setTotalOwnedAssetsCount(includedOwnsData.length); | ||||
|         // Extract useful details from the Response
 | ||||
|         const totalOwnedAssets = res?.data?.hits?.total.value ?? 0; | ||||
|         const ownedAssets = res?.data?.hits?.hits; | ||||
| 
 | ||||
|         setData(ownedAssets.map((hit) => hit._source).slice(0, 8)); | ||||
|         setTotalOwnedAssetsCount(totalOwnedAssets); | ||||
|       } catch (err) { | ||||
|         setData([]); | ||||
|       } finally { | ||||
|         setIsLoading(false); | ||||
|       } | ||||
|     } catch (err) { | ||||
|       setData([]); | ||||
|     } finally { | ||||
|       setIsLoading(false); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
| @ -72,7 +86,7 @@ const MyDataWidgetInternal = ({ | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     fetchMyDataAssets(); | ||||
|   }, [currentUserDetails]); | ||||
|   }, [currentUser]); | ||||
| 
 | ||||
|   return ( | ||||
|     <Card className="my-data-widget-container card-widget" loading={isLoading}> | ||||
| @ -86,7 +100,7 @@ const MyDataWidgetInternal = ({ | ||||
|               {data.length ? ( | ||||
|                 <Link | ||||
|                   data-testid="view-all-link" | ||||
|                   to={getUserPath(currentUserDetails?.name || '', 'mydata')}> | ||||
|                   to={getUserPath(currentUser?.name ?? '', 'mydata')}> | ||||
|                   <span className="text-grey-muted font-normal text-xs"> | ||||
|                     {t('label.view-all')}{' '} | ||||
|                     <span data-testid="my-data-total-count"> | ||||
| @ -132,14 +146,14 @@ const MyDataWidgetInternal = ({ | ||||
|                     <Link | ||||
|                       className="" | ||||
|                       to={getEntityLink( | ||||
|                         item.type || '', | ||||
|                         item.entityType ?? '', | ||||
|                         item.fullyQualifiedName as string | ||||
|                       )}> | ||||
|                       <Button | ||||
|                         className="entity-button flex-center p-0 m--ml-1" | ||||
|                         icon={ | ||||
|                           <div className="entity-button-icon m-r-xs"> | ||||
|                             {getEntityIcon(item.type || '')} | ||||
|                             {getEntityIcon(item.entityType ?? '')} | ||||
|                           </div> | ||||
|                         } | ||||
|                         type="text"> | ||||
|  | ||||
| @ -13,15 +13,46 @@ | ||||
| import { act, render, screen } from '@testing-library/react'; | ||||
| import React from 'react'; | ||||
| import { MemoryRouter } from 'react-router-dom'; | ||||
| import { getUserById } from '../../../rest/userAPI'; | ||||
| import { User } from '../../../generated/entity/teams/user'; | ||||
| import { searchData } from '../../../rest/miscAPI'; | ||||
| import { MyDataWidget } from './MyDataWidget.component'; | ||||
| 
 | ||||
| const userDetails = { | ||||
|   id: '123', | ||||
| const mockUserData: User = { | ||||
|   name: 'testUser1', | ||||
|   email: 'testUser1@email.com', | ||||
|   id: '113', | ||||
| }; | ||||
| 
 | ||||
| jest.mock('../../../rest/userAPI', () => ({ | ||||
|   getUserById: jest.fn().mockImplementation(() => | ||||
| const mockSearchAPIResponse = { | ||||
|   data: { | ||||
|     hits: { | ||||
|       hits: [ | ||||
|         { | ||||
|           _source: { | ||||
|             id: '1', | ||||
|             name: 'test 1', | ||||
|             fullyQualifiedName: 'test-1', | ||||
|             type: 'table', | ||||
|           }, | ||||
|         }, | ||||
|         { | ||||
|           _source: { | ||||
|             id: '2', | ||||
|             name: 'test 2', | ||||
|             fullyQualifiedName: 'test-2', | ||||
|             type: 'table', | ||||
|           }, | ||||
|         }, | ||||
|       ], | ||||
|       total: { | ||||
|         value: 2, | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| jest.mock('../../../rest/miscAPI', () => ({ | ||||
|   searchData: jest.fn().mockImplementation(() => | ||||
|     Promise.resolve({ | ||||
|       owns: [], | ||||
|     }) | ||||
| @ -37,10 +68,10 @@ jest.mock('../../../utils/TableUtils', () => ({ | ||||
|   getEntityIcon: jest.fn().mockImplementation((obj) => obj.name), | ||||
| })); | ||||
| 
 | ||||
| jest.mock('./../../../AppState', () => ({ | ||||
|   getCurrentUserDetails: jest.fn().mockImplementation(() => { | ||||
|     return userDetails; | ||||
|   }), | ||||
| jest.mock('../../authentication/auth-provider/AuthProvider', () => ({ | ||||
|   useAuthContext: jest.fn(() => ({ | ||||
|     currentUser: mockUserData, | ||||
|   })), | ||||
| })); | ||||
| 
 | ||||
| jest.mock( | ||||
| @ -56,7 +87,15 @@ describe('MyDataWidget component', () => { | ||||
|       render(<MyDataWidget widgetKey="widgetKey" />, { wrapper: MemoryRouter }); | ||||
|     }); | ||||
| 
 | ||||
|     expect(getUserById).toHaveBeenCalledWith('123', 'owns'); | ||||
|     expect(searchData).toHaveBeenCalledWith( | ||||
|       '', | ||||
|       1, | ||||
|       10, | ||||
|       '(owner.id:113)', | ||||
|       '', | ||||
|       '', | ||||
|       'all' | ||||
|     ); | ||||
|   }); | ||||
| 
 | ||||
|   it.skip('should render header', () => { | ||||
| @ -84,23 +123,8 @@ describe('MyDataWidget component', () => { | ||||
|   }); | ||||
| 
 | ||||
|   it('should render view all for data present', async () => { | ||||
|     (getUserById as jest.Mock).mockImplementationOnce(() => | ||||
|       Promise.resolve({ | ||||
|         owns: [ | ||||
|           { | ||||
|             id: '1', | ||||
|             name: 'test 1', | ||||
|             fullyQualifiedName: 'test-1', | ||||
|             type: 'table', | ||||
|           }, | ||||
|           { | ||||
|             id: '2', | ||||
|             name: 'test 2', | ||||
|             fullyQualifiedName: 'test-2', | ||||
|             type: 'table', | ||||
|           }, | ||||
|         ], | ||||
|       }) | ||||
|     (searchData as jest.Mock).mockImplementationOnce(() => | ||||
|       Promise.resolve(mockSearchAPIResponse) | ||||
|     ); | ||||
|     act(() => { | ||||
|       render( | ||||
| @ -114,22 +138,7 @@ describe('MyDataWidget component', () => { | ||||
|   }); | ||||
| 
 | ||||
|   it('should render table names', async () => { | ||||
|     (getUserById as jest.Mock).mockResolvedValueOnce({ | ||||
|       owns: [ | ||||
|         { | ||||
|           id: '1', | ||||
|           name: 'test 1', | ||||
|           fullyQualifiedName: 'test-1', | ||||
|           type: 'table', | ||||
|         }, | ||||
|         { | ||||
|           id: '2', | ||||
|           name: 'test 2', | ||||
|           fullyQualifiedName: 'test-2', | ||||
|           type: 'table', | ||||
|         }, | ||||
|       ], | ||||
|     }); | ||||
|     (searchData as jest.Mock).mockResolvedValueOnce(mockSearchAPIResponse); | ||||
|     act(() => { | ||||
|       render( | ||||
|         <MemoryRouter> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aniket Katkar
						Aniket Katkar