mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-29 01:42:08 +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