From d3dc2f22a5be40125296b3f33f3f77e4dc0ff61b Mon Sep 17 00:00:00 2001 From: Joshua Eilers Date: Fri, 19 May 2023 11:42:46 -0700 Subject: [PATCH] feat(embed): embed lookup route (#8033) --- datahub-web-react/src/app/ProtectedRoutes.tsx | 2 + datahub-web-react/src/app/analytics/event.ts | 11 ++++- .../src/app/embed/lookup/EmbedLookup.tsx | 38 ++++++++++++++++ .../src/app/embed/lookup/GoToLookup.tsx | 19 ++++++++ .../app/embed/lookup/LookupFoundMultiple.tsx | 13 ++++++ .../src/app/embed/lookup/LookupLoading.tsx | 8 ++++ .../src/app/embed/lookup/LookupNotFound.tsx | 13 ++++++ .../src/app/embed/lookup/constants.ts | 7 +++ .../src/app/embed/lookup/index.tsx | 3 ++ .../src/app/embed/lookup/useEmbedAnalytics.ts | 26 +++++++++++ .../src/app/embed/lookup/useGetEntityByUrl.ts | 44 +++++++++++++++++++ .../src/app/entity/chart/ChartEntity.tsx | 10 +++++ .../app/entity/container/ContainerEntity.tsx | 10 +++++ .../src/app/shared/error/ErrorSection.tsx | 2 +- datahub-web-react/src/conf/Global.ts | 1 + .../datahubusage/DataHubUsageEventType.java | 3 +- 16 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 datahub-web-react/src/app/embed/lookup/EmbedLookup.tsx create mode 100644 datahub-web-react/src/app/embed/lookup/GoToLookup.tsx create mode 100644 datahub-web-react/src/app/embed/lookup/LookupFoundMultiple.tsx create mode 100644 datahub-web-react/src/app/embed/lookup/LookupLoading.tsx create mode 100644 datahub-web-react/src/app/embed/lookup/LookupNotFound.tsx create mode 100644 datahub-web-react/src/app/embed/lookup/constants.ts create mode 100644 datahub-web-react/src/app/embed/lookup/index.tsx create mode 100644 datahub-web-react/src/app/embed/lookup/useEmbedAnalytics.ts create mode 100644 datahub-web-react/src/app/embed/lookup/useGetEntityByUrl.ts diff --git a/datahub-web-react/src/app/ProtectedRoutes.tsx b/datahub-web-react/src/app/ProtectedRoutes.tsx index 83f987614f..469e0d6030 100644 --- a/datahub-web-react/src/app/ProtectedRoutes.tsx +++ b/datahub-web-react/src/app/ProtectedRoutes.tsx @@ -7,6 +7,7 @@ import { PageRoutes } from '../conf/Global'; import EmbeddedPage from './embed/EmbeddedPage'; import { useEntityRegistry } from './useEntityRegistry'; import AppProviders from './AppProviders'; +import EmbedLookup from './embed/lookup'; /** * Container for all views behind an authentication wall. @@ -20,6 +21,7 @@ export const ProtectedRoutes = (): JSX.Element => { } /> + } /> {entityRegistry.getEntities().map((entity) => ( { + const { url: encodedUrl } = useParams(); + const decodedUrl = decodeURIComponent(encodedUrl); + const { count, entity, error, loading } = useGetEntityByUrl(decodedUrl); + + const getContent = () => { + if (loading) return ; + if (error) return ; + if (count === 0 || !entity) return ; + if (count > 1) return ; + return ; + }; + + return {getContent()}; +}; + +export default EmbedLookup; diff --git a/datahub-web-react/src/app/embed/lookup/GoToLookup.tsx b/datahub-web-react/src/app/embed/lookup/GoToLookup.tsx new file mode 100644 index 0000000000..d02ad78032 --- /dev/null +++ b/datahub-web-react/src/app/embed/lookup/GoToLookup.tsx @@ -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 ; +}; + +export default GoToLookup; diff --git a/datahub-web-react/src/app/embed/lookup/LookupFoundMultiple.tsx b/datahub-web-react/src/app/embed/lookup/LookupFoundMultiple.tsx new file mode 100644 index 0000000000..5e0e5f65af --- /dev/null +++ b/datahub-web-react/src/app/embed/lookup/LookupFoundMultiple.tsx @@ -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 ; +}; + +export default LookupFoundMultiple; diff --git a/datahub-web-react/src/app/embed/lookup/LookupLoading.tsx b/datahub-web-react/src/app/embed/lookup/LookupLoading.tsx new file mode 100644 index 0000000000..47be4ee83c --- /dev/null +++ b/datahub-web-react/src/app/embed/lookup/LookupLoading.tsx @@ -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; diff --git a/datahub-web-react/src/app/embed/lookup/LookupNotFound.tsx b/datahub-web-react/src/app/embed/lookup/LookupNotFound.tsx new file mode 100644 index 0000000000..a9ac735aa0 --- /dev/null +++ b/datahub-web-react/src/app/embed/lookup/LookupNotFound.tsx @@ -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 ; +}; + +export default LookupNotFound; diff --git a/datahub-web-react/src/app/embed/lookup/constants.ts b/datahub-web-react/src/app/embed/lookup/constants.ts new file mode 100644 index 0000000000..7faba45dba --- /dev/null +++ b/datahub-web-react/src/app/embed/lookup/constants.ts @@ -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]; diff --git a/datahub-web-react/src/app/embed/lookup/index.tsx b/datahub-web-react/src/app/embed/lookup/index.tsx new file mode 100644 index 0000000000..ae1764a0b4 --- /dev/null +++ b/datahub-web-react/src/app/embed/lookup/index.tsx @@ -0,0 +1,3 @@ +import EmbedLookup from './EmbedLookup'; + +export default EmbedLookup; diff --git a/datahub-web-react/src/app/embed/lookup/useEmbedAnalytics.ts b/datahub-web-react/src/app/embed/lookup/useEmbedAnalytics.ts new file mode 100644 index 0000000000..41c6e36b30 --- /dev/null +++ b/datahub-web-react/src/app/embed/lookup/useEmbedAnalytics.ts @@ -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; diff --git a/datahub-web-react/src/app/embed/lookup/useGetEntityByUrl.ts b/datahub-web-react/src/app/embed/lookup/useGetEntityByUrl.ts new file mode 100644 index 0000000000..209114e301 --- /dev/null +++ b/datahub-web-react/src/app/embed/lookup/useGetEntityByUrl.ts @@ -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; diff --git a/datahub-web-react/src/app/entity/chart/ChartEntity.tsx b/datahub-web-react/src/app/entity/chart/ChartEntity.tsx index 3b67469967..4c7048c3c0 100644 --- a/datahub-web-react/src/app/entity/chart/ChartEntity.tsx +++ b/datahub-web-react/src/app/entity/chart/ChartEntity.tsx @@ -24,6 +24,7 @@ import { EmbedTab } from '../shared/tabs/Embed/EmbedTab'; import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; import DataProductSection from '../shared/containers/profile/sidebar/DataProduct/DataProductSection'; import { getDataProduct } from '../shared/utils'; +import EmbeddedProfile from '../shared/embed/EmbeddedProfile'; /** * Definition of the DataHub Chart entity. @@ -237,4 +238,13 @@ export class ChartEntity implements Entity { EntityCapabilityType.DATA_PRODUCTS, ]); }; + + renderEmbeddedProfile = (urn: string) => ( + + ); } diff --git a/datahub-web-react/src/app/entity/container/ContainerEntity.tsx b/datahub-web-react/src/app/entity/container/ContainerEntity.tsx index 1f123d6134..5ac93b2482 100644 --- a/datahub-web-react/src/app/entity/container/ContainerEntity.tsx +++ b/datahub-web-react/src/app/entity/container/ContainerEntity.tsx @@ -16,6 +16,7 @@ import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domai import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; import DataProductSection from '../shared/containers/profile/sidebar/DataProduct/DataProductSection'; import { getDataProduct } from '../shared/utils'; +import EmbeddedProfile from '../shared/embed/EmbeddedProfile'; /** * Definition of the DataHub Container entity. @@ -186,4 +187,13 @@ export class ContainerEntity implements Entity { EntityCapabilityType.DATA_PRODUCTS, ]); }; + + renderEmbeddedProfile = (urn: string) => ( + + ); } diff --git a/datahub-web-react/src/app/shared/error/ErrorSection.tsx b/datahub-web-react/src/app/shared/error/ErrorSection.tsx index a4e96b6f4a..9faf36cf88 100644 --- a/datahub-web-react/src/app/shared/error/ErrorSection.tsx +++ b/datahub-web-react/src/app/shared/error/ErrorSection.tsx @@ -78,7 +78,7 @@ export const ErrorSection = (): JSX.Element => { {resources.map((resource) => ( - + {resource.label} ))} diff --git a/datahub-web-react/src/conf/Global.ts b/datahub-web-react/src/conf/Global.ts index c40eb6f3eb..b16dd1eaac 100644 --- a/datahub-web-react/src/conf/Global.ts +++ b/datahub-web-react/src/conf/Global.ts @@ -27,6 +27,7 @@ export enum PageRoutes { GLOSSARY = '/glossary', SETTINGS_VIEWS = '/settings/views', EMBED = '/embed', + EMBED_LOOKUP = '/embed/lookup/:url', } /** diff --git a/metadata-io/src/main/java/com/linkedin/metadata/datahubusage/DataHubUsageEventType.java b/metadata-io/src/main/java/com/linkedin/metadata/datahubusage/DataHubUsageEventType.java index acfbce7609..6a67260720 100644 --- a/metadata-io/src/main/java/com/linkedin/metadata/datahubusage/DataHubUsageEventType.java +++ b/metadata-io/src/main/java/com/linkedin/metadata/datahubusage/DataHubUsageEventType.java @@ -67,7 +67,8 @@ public enum DataHubUsageEventType { SELECT_QUICK_FILTER_EVENT("SelectQuickFilterEvent"), DESELECT_QUICK_FILTER_EVENT("DeselectQuickFilterEvent"), 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;