mirror of
				https://github.com/datahub-project/datahub.git
				synced 2025-10-30 18:26:58 +00:00 
			
		
		
		
	feat(embed): embed lookup route (#8033)
This commit is contained in:
		
							parent
							
								
									72bcfc53af
								
							
						
					
					
						commit
						d3dc2f22a5
					
				| @ -7,6 +7,7 @@ import { PageRoutes } from '../conf/Global'; | |||||||
| import EmbeddedPage from './embed/EmbeddedPage'; | import EmbeddedPage from './embed/EmbeddedPage'; | ||||||
| import { useEntityRegistry } from './useEntityRegistry'; | import { useEntityRegistry } from './useEntityRegistry'; | ||||||
| import AppProviders from './AppProviders'; | import AppProviders from './AppProviders'; | ||||||
|  | import EmbedLookup from './embed/lookup'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Container for all views behind an authentication wall. |  * Container for all views behind an authentication wall. | ||||||
| @ -20,6 +21,7 @@ export const ProtectedRoutes = (): JSX.Element => { | |||||||
|                 <Layout> |                 <Layout> | ||||||
|                     <Switch> |                     <Switch> | ||||||
|                         <Route exact path="/" render={() => <HomePage />} /> |                         <Route exact path="/" render={() => <HomePage />} /> | ||||||
|  |                         <Route exact path={PageRoutes.EMBED_LOOKUP} render={() => <EmbedLookup />} /> | ||||||
|                         {entityRegistry.getEntities().map((entity) => ( |                         {entityRegistry.getEntities().map((entity) => ( | ||||||
|                             <Route |                             <Route | ||||||
|                                 key={`${entity.getPathName()}/${PageRoutes.EMBED}`} |                                 key={`${entity.getPathName()}/${PageRoutes.EMBED}`} | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import { DataHubViewType, EntityType, RecommendationRenderType, ScenarioType } from '../../types.generated'; | import { DataHubViewType, EntityType, RecommendationRenderType, ScenarioType } from '../../types.generated'; | ||||||
|  | import { EmbedLookupNotFoundReason } from '../embed/lookup/constants'; | ||||||
| import { Direction } from '../lineage/types'; | import { Direction } from '../lineage/types'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -68,6 +69,7 @@ export enum EventType { | |||||||
|     DeselectQuickFilterEvent, |     DeselectQuickFilterEvent, | ||||||
|     EmbedProfileViewEvent, |     EmbedProfileViewEvent, | ||||||
|     EmbedProfileViewInDataHubEvent, |     EmbedProfileViewInDataHubEvent, | ||||||
|  |     EmbedLookupNotFoundEvent, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -519,6 +521,12 @@ export interface EmbedProfileViewInDataHubEvent extends BaseEvent { | |||||||
|     entityUrn: string; |     entityUrn: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export interface EmbedLookupNotFoundEvent extends BaseEvent { | ||||||
|  |     type: EventType.EmbedLookupNotFoundEvent; | ||||||
|  |     url: string; | ||||||
|  |     reason: EmbedLookupNotFoundReason; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Event consisting of a union of specific event types. |  * Event consisting of a union of specific event types. | ||||||
|  */ |  */ | ||||||
| @ -585,4 +593,5 @@ export type Event = | |||||||
|     | SelectQuickFilterEvent |     | SelectQuickFilterEvent | ||||||
|     | DeselectQuickFilterEvent |     | DeselectQuickFilterEvent | ||||||
|     | EmbedProfileViewEvent |     | EmbedProfileViewEvent | ||||||
|     | EmbedProfileViewInDataHubEvent; |     | EmbedProfileViewInDataHubEvent | ||||||
|  |     | EmbedLookupNotFoundEvent; | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								datahub-web-react/src/app/embed/lookup/EmbedLookup.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								datahub-web-react/src/app/embed/lookup/EmbedLookup.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | import React from 'react'; | ||||||
|  | import { useParams } from 'react-router'; | ||||||
|  | import styled from 'styled-components'; | ||||||
|  | import { ErrorSection } from '../../shared/error/ErrorSection'; | ||||||
|  | import useGetEntityByUrl from './useGetEntityByUrl'; | ||||||
|  | import LookupNotFound from './LookupNotFound'; | ||||||
|  | import LookupFoundMultiple from './LookupFoundMultiple'; | ||||||
|  | import LookupLoading from './LookupLoading'; | ||||||
|  | import GoToLookup from './GoToLookup'; | ||||||
|  | 
 | ||||||
|  | type RouteParams = { | ||||||
|  |     url: string; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const PageContainer = styled.div` | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: center; | ||||||
|  |     height: 85vh; | ||||||
|  | `;
 | ||||||
|  | 
 | ||||||
|  | const EmbedLookup = () => { | ||||||
|  |     const { url: encodedUrl } = useParams<RouteParams>(); | ||||||
|  |     const decodedUrl = decodeURIComponent(encodedUrl); | ||||||
|  |     const { count, entity, error, loading } = useGetEntityByUrl(decodedUrl); | ||||||
|  | 
 | ||||||
|  |     const getContent = () => { | ||||||
|  |         if (loading) return <LookupLoading />; | ||||||
|  |         if (error) return <ErrorSection />; | ||||||
|  |         if (count === 0 || !entity) return <LookupNotFound url={encodedUrl} />; | ||||||
|  |         if (count > 1) return <LookupFoundMultiple url={encodedUrl} />; | ||||||
|  |         return <GoToLookup entityType={entity.type} entityUrn={entity.urn} />; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return <PageContainer>{getContent()}</PageContainer>; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default EmbedLookup; | ||||||
							
								
								
									
										19
									
								
								datahub-web-react/src/app/embed/lookup/GoToLookup.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								datahub-web-react/src/app/embed/lookup/GoToLookup.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | import React, { useEffect } from 'react'; | ||||||
|  | import { useHistory } from 'react-router'; | ||||||
|  | import { EntityType } from '../../../types.generated'; | ||||||
|  | import { useEntityRegistry } from '../../useEntityRegistry'; | ||||||
|  | import { PageRoutes } from '../../../conf/Global'; | ||||||
|  | import { urlEncodeUrn } from '../../entity/shared/utils'; | ||||||
|  | import LookupLoading from './LookupLoading'; | ||||||
|  | 
 | ||||||
|  | const GoToLookup = ({ entityType, entityUrn }: { entityType: EntityType; entityUrn: string }) => { | ||||||
|  |     const registry = useEntityRegistry(); | ||||||
|  |     const history = useHistory(); | ||||||
|  |     useEffect(() => { | ||||||
|  |         const entityUrl = `${PageRoutes.EMBED}/${registry.getPathName(entityType)}/${urlEncodeUrn(entityUrn)}`; | ||||||
|  |         history.push(entityUrl); | ||||||
|  |     }, [entityType, entityUrn, history, registry]); | ||||||
|  |     return <LookupLoading />; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default GoToLookup; | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | import React, { useEffect } from 'react'; | ||||||
|  | import useEmbedLookupAnalytics from './useEmbedAnalytics'; | ||||||
|  | import NonExistentEntityPage from '../../entity/shared/entity/NonExistentEntityPage'; | ||||||
|  | 
 | ||||||
|  | const LookupFoundMultiple = ({ url }: { url: string }) => { | ||||||
|  |     const { trackLookupMultipleFoundEvent } = useEmbedLookupAnalytics(); | ||||||
|  |     useEffect(() => { | ||||||
|  |         trackLookupMultipleFoundEvent(url); | ||||||
|  |     }, [trackLookupMultipleFoundEvent, url]); | ||||||
|  |     return <NonExistentEntityPage />; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default LookupFoundMultiple; | ||||||
							
								
								
									
										8
									
								
								datahub-web-react/src/app/embed/lookup/LookupLoading.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								datahub-web-react/src/app/embed/lookup/LookupLoading.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | import { LoadingOutlined } from '@ant-design/icons'; | ||||||
|  | import styled from 'styled-components'; | ||||||
|  | 
 | ||||||
|  | const LookupLoading = styled(LoadingOutlined)` | ||||||
|  |     font-size: 50px; | ||||||
|  | `;
 | ||||||
|  | 
 | ||||||
|  | export default LookupLoading; | ||||||
							
								
								
									
										13
									
								
								datahub-web-react/src/app/embed/lookup/LookupNotFound.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								datahub-web-react/src/app/embed/lookup/LookupNotFound.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | import React, { useEffect } from 'react'; | ||||||
|  | import useEmbedLookupAnalytics from './useEmbedAnalytics'; | ||||||
|  | import NonExistentEntityPage from '../../entity/shared/entity/NonExistentEntityPage'; | ||||||
|  | 
 | ||||||
|  | const LookupNotFound = ({ url }: { url: string }) => { | ||||||
|  |     const { trackLookupNotFoundEvent } = useEmbedLookupAnalytics(); | ||||||
|  |     useEffect(() => { | ||||||
|  |         trackLookupNotFoundEvent(url); | ||||||
|  |     }, [trackLookupNotFoundEvent, url]); | ||||||
|  |     return <NonExistentEntityPage />; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default LookupNotFound; | ||||||
							
								
								
									
										7
									
								
								datahub-web-react/src/app/embed/lookup/constants.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								datahub-web-react/src/app/embed/lookup/constants.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | export const EMBED_LOOKUP_NOT_FOUND_REASON = { | ||||||
|  |     NO_ENTITY_FOUND: 'NO_ENTITY_FOUND', | ||||||
|  |     MULTIPLE_ENTITIES_FOUND: 'MULTIPLE_ENTITIES_FOUND', | ||||||
|  | } as const; | ||||||
|  | 
 | ||||||
|  | export type EmbedLookupNotFoundReason = | ||||||
|  |     typeof EMBED_LOOKUP_NOT_FOUND_REASON[keyof typeof EMBED_LOOKUP_NOT_FOUND_REASON]; | ||||||
							
								
								
									
										3
									
								
								datahub-web-react/src/app/embed/lookup/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								datahub-web-react/src/app/embed/lookup/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | import EmbedLookup from './EmbedLookup'; | ||||||
|  | 
 | ||||||
|  | export default EmbedLookup; | ||||||
							
								
								
									
										26
									
								
								datahub-web-react/src/app/embed/lookup/useEmbedAnalytics.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								datahub-web-react/src/app/embed/lookup/useEmbedAnalytics.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | import { useCallback } from 'react'; | ||||||
|  | import analytics from '../../analytics/analytics'; | ||||||
|  | import { EventType } from '../../analytics'; | ||||||
|  | import { EMBED_LOOKUP_NOT_FOUND_REASON } from './constants'; | ||||||
|  | 
 | ||||||
|  | const useEmbedLookupAnalytics = () => { | ||||||
|  |     const trackLookupNotFoundEvent = useCallback((url: string) => { | ||||||
|  |         analytics.event({ | ||||||
|  |             type: EventType.EmbedLookupNotFoundEvent, | ||||||
|  |             url, | ||||||
|  |             reason: EMBED_LOOKUP_NOT_FOUND_REASON.NO_ENTITY_FOUND, | ||||||
|  |         }); | ||||||
|  |     }, []); | ||||||
|  | 
 | ||||||
|  |     const trackLookupMultipleFoundEvent = useCallback((url: string) => { | ||||||
|  |         analytics.event({ | ||||||
|  |             type: EventType.EmbedLookupNotFoundEvent, | ||||||
|  |             url, | ||||||
|  |             reason: EMBED_LOOKUP_NOT_FOUND_REASON.MULTIPLE_ENTITIES_FOUND, | ||||||
|  |         }); | ||||||
|  |     }, []); | ||||||
|  | 
 | ||||||
|  |     return { trackLookupNotFoundEvent, trackLookupMultipleFoundEvent } as const; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default useEmbedLookupAnalytics; | ||||||
							
								
								
									
										44
									
								
								datahub-web-react/src/app/embed/lookup/useGetEntityByUrl.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								datahub-web-react/src/app/embed/lookup/useGetEntityByUrl.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | import { useMemo } from 'react'; | ||||||
|  | import { useGetSearchResultsForMultipleQuery } from '../../../graphql/search.generated'; | ||||||
|  | import { FilterOperator } from '../../../types.generated'; | ||||||
|  | import { UnionType } from '../../search/utils/constants'; | ||||||
|  | import { generateOrFilters } from '../../search/utils/generateOrFilters'; | ||||||
|  | 
 | ||||||
|  | const URL_FIELDS = ['externalUrl', 'chartUrl', 'dashboardUrl'] as const; | ||||||
|  | 
 | ||||||
|  | const useGetEntityByUrl = (externalUrl: string) => { | ||||||
|  |     const { data, loading, error } = useGetSearchResultsForMultipleQuery({ | ||||||
|  |         variables: { | ||||||
|  |             input: { | ||||||
|  |                 query: '*', | ||||||
|  |                 start: 0, | ||||||
|  |                 count: 2, | ||||||
|  |                 orFilters: generateOrFilters( | ||||||
|  |                     UnionType.OR, | ||||||
|  |                     URL_FIELDS.map((field) => ({ | ||||||
|  |                         field, | ||||||
|  |                         values: [externalUrl], | ||||||
|  |                         condition: FilterOperator.Equal, | ||||||
|  |                     })), | ||||||
|  |                 ), | ||||||
|  |             }, | ||||||
|  |         }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const entities = data?.searchAcrossEntities?.searchResults.map((result) => result.entity) ?? []; | ||||||
|  |     const count = entities.length; | ||||||
|  |     const entity = count === 1 ? entities[0] : null; | ||||||
|  | 
 | ||||||
|  |     return useMemo( | ||||||
|  |         () => | ||||||
|  |             ({ | ||||||
|  |                 count, | ||||||
|  |                 entity, | ||||||
|  |                 loading, | ||||||
|  |                 error, | ||||||
|  |             } as const), | ||||||
|  |         [count, entity, error, loading], | ||||||
|  |     ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default useGetEntityByUrl; | ||||||
| @ -24,6 +24,7 @@ import { EmbedTab } from '../shared/tabs/Embed/EmbedTab'; | |||||||
| import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; | import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; | ||||||
| import DataProductSection from '../shared/containers/profile/sidebar/DataProduct/DataProductSection'; | import DataProductSection from '../shared/containers/profile/sidebar/DataProduct/DataProductSection'; | ||||||
| import { getDataProduct } from '../shared/utils'; | import { getDataProduct } from '../shared/utils'; | ||||||
|  | import EmbeddedProfile from '../shared/embed/EmbeddedProfile'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Definition of the DataHub Chart entity. |  * Definition of the DataHub Chart entity. | ||||||
| @ -237,4 +238,13 @@ export class ChartEntity implements Entity<Chart> { | |||||||
|             EntityCapabilityType.DATA_PRODUCTS, |             EntityCapabilityType.DATA_PRODUCTS, | ||||||
|         ]); |         ]); | ||||||
|     }; |     }; | ||||||
|  | 
 | ||||||
|  |     renderEmbeddedProfile = (urn: string) => ( | ||||||
|  |         <EmbeddedProfile | ||||||
|  |             urn={urn} | ||||||
|  |             entityType={EntityType.Chart} | ||||||
|  |             useEntityQuery={useGetChartQuery} | ||||||
|  |             getOverrideProperties={this.getOverridePropertiesFromEntity} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domai | |||||||
| import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; | import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; | ||||||
| import DataProductSection from '../shared/containers/profile/sidebar/DataProduct/DataProductSection'; | import DataProductSection from '../shared/containers/profile/sidebar/DataProduct/DataProductSection'; | ||||||
| import { getDataProduct } from '../shared/utils'; | import { getDataProduct } from '../shared/utils'; | ||||||
|  | import EmbeddedProfile from '../shared/embed/EmbeddedProfile'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Definition of the DataHub Container entity. |  * Definition of the DataHub Container entity. | ||||||
| @ -186,4 +187,13 @@ export class ContainerEntity implements Entity<Container> { | |||||||
|             EntityCapabilityType.DATA_PRODUCTS, |             EntityCapabilityType.DATA_PRODUCTS, | ||||||
|         ]); |         ]); | ||||||
|     }; |     }; | ||||||
|  | 
 | ||||||
|  |     renderEmbeddedProfile = (urn: string) => ( | ||||||
|  |         <EmbeddedProfile | ||||||
|  |             urn={urn} | ||||||
|  |             entityType={EntityType.Container} | ||||||
|  |             useEntityQuery={useGetContainerQuery} | ||||||
|  |             getOverrideProperties={this.getOverridePropertiesFromEntity} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  | |||||||
| @ -78,7 +78,7 @@ export const ErrorSection = (): JSX.Element => { | |||||||
|                     </DetailParagraph> |                     </DetailParagraph> | ||||||
|                     <ResourceList> |                     <ResourceList> | ||||||
|                         {resources.map((resource) => ( |                         {resources.map((resource) => ( | ||||||
|                             <ResourceListItem> |                             <ResourceListItem key={resource.path}> | ||||||
|                                 <a href={resource.path}>{resource.label}</a> |                                 <a href={resource.path}>{resource.label}</a> | ||||||
|                             </ResourceListItem> |                             </ResourceListItem> | ||||||
|                         ))} |                         ))} | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ export enum PageRoutes { | |||||||
|     GLOSSARY = '/glossary', |     GLOSSARY = '/glossary', | ||||||
|     SETTINGS_VIEWS = '/settings/views', |     SETTINGS_VIEWS = '/settings/views', | ||||||
|     EMBED = '/embed', |     EMBED = '/embed', | ||||||
|  |     EMBED_LOOKUP = '/embed/lookup/:url', | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
| @ -67,7 +67,8 @@ public enum DataHubUsageEventType { | |||||||
|   SELECT_QUICK_FILTER_EVENT("SelectQuickFilterEvent"), |   SELECT_QUICK_FILTER_EVENT("SelectQuickFilterEvent"), | ||||||
|   DESELECT_QUICK_FILTER_EVENT("DeselectQuickFilterEvent"), |   DESELECT_QUICK_FILTER_EVENT("DeselectQuickFilterEvent"), | ||||||
|   EMBED_PROFILE_VIEW_EVENT("EmbedProfileViewEvent"), |   EMBED_PROFILE_VIEW_EVENT("EmbedProfileViewEvent"), | ||||||
|   EMBED_PROFILE_VIEW_IN_DATAHUB_EVENT("EmbedProfileViewInDataHubEvent"); |   EMBED_PROFILE_VIEW_IN_DATAHUB_EVENT("EmbedProfileViewInDataHubEvent"), | ||||||
|  |   EMBED_LOOKUP_NOT_FOUND_EVENT("EmbedLookupNotFoundEvent"); | ||||||
| 
 | 
 | ||||||
|   private final String type; |   private final String type; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Joshua Eilers
						Joshua Eilers