diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/glossary/GlossaryTermUtils.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/glossary/GlossaryTermUtils.java index 3c8e9977e3..93b6ab53d5 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/glossary/GlossaryTermUtils.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/glossary/GlossaryTermUtils.java @@ -3,6 +3,7 @@ package com.linkedin.datahub.graphql.types.glossary; import com.linkedin.common.urn.GlossaryTermUrn; import java.net.URISyntaxException; +import java.util.regex.Pattern; public class GlossaryTermUtils { @@ -15,4 +16,12 @@ public class GlossaryTermUtils { throw new RuntimeException(String.format("Failed to retrieve glossary with urn %s, invalid urn", urnStr)); } } + + public static String getGlossaryTermName(String hierarchicalName) { + if (hierarchicalName.contains(".")) { + String[] nodes = hierarchicalName.split(Pattern.quote(".")); + return nodes[nodes.length - 1]; + } + return hierarchicalName; + } } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/glossary/mappers/GlossaryTermMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/glossary/mappers/GlossaryTermMapper.java index b09707f2c8..0f0c324bc8 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/glossary/mappers/GlossaryTermMapper.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/glossary/mappers/GlossaryTermMapper.java @@ -5,6 +5,8 @@ import javax.annotation.Nonnull; import com.linkedin.datahub.graphql.generated.GlossaryTerm; import com.linkedin.datahub.graphql.generated.EntityType; import com.linkedin.datahub.graphql.types.mappers.ModelMapper; +import com.linkedin.datahub.graphql.types.common.mappers.OwnershipMapper; +import com.linkedin.datahub.graphql.types.glossary.GlossaryTermUtils; /** * Maps Pegasus {@link RecordTemplate} objects to objects conforming to the GQL schema. @@ -24,8 +26,11 @@ public class GlossaryTermMapper implements ModelMapper { register.register(new TagEntity()); register.register(new DataFlowEntity()); register.register(new DataJobEntity()); + register.register(new GlossaryTermEntity()); return register; }, []); diff --git a/datahub-web-react/src/Mocks.tsx b/datahub-web-react/src/Mocks.tsx index 6e8443ee9f..b2dc532a1e 100644 --- a/datahub-web-react/src/Mocks.tsx +++ b/datahub-web-react/src/Mocks.tsx @@ -8,7 +8,7 @@ import { GetSearchResultsQuery, } from './graphql/search.generated'; import { GetUserDocument } from './graphql/user.generated'; -import { Dataset, DataFlow, DataJob, EntityType, PlatformType } from './types.generated'; +import { Dataset, DataFlow, DataJob, GlossaryTerm, EntityType, PlatformType } from './types.generated'; import { GetTagDocument } from './graphql/tag.generated'; const user1 = { @@ -412,6 +412,36 @@ export const dataset7WithSelfReferentialLineage = { ], }, }; +const glossaryTerm1 = { + urn: 'urn:li:glossaryTerm:1', + type: EntityType.GlossaryTerm, + name: 'Another glossary term', + ownership: { + owners: [ + { + owner: { + ...user1, + }, + type: 'DATAOWNER', + }, + { + owner: { + ...user2, + }, + type: 'DELEGATE', + }, + ], + lastModified: { + time: 0, + }, + }, + glossaryTermInfo: { + definition: 'New glossary term', + termSource: 'termSource', + sourceRef: 'sourceRef', + sourceURI: 'sourceURI', + }, +} as GlossaryTerm; const sampleTag = { urn: 'urn:li:tag:abc-sample-tag', @@ -866,6 +896,59 @@ export const mocks = [ } as GetSearchResultsQuery, }, }, + { + request: { + query: GetSearchResultsDocument, + variables: { + input: { + type: 'GLOSSARY_TERM', + query: 'tags:abc-sample-tag', + start: 0, + count: 1, + filters: [], + }, + }, + }, + result: { + data: { + __typename: 'Query', + search: { + __typename: 'SearchResults', + start: 0, + count: 1, + total: 1, + searchResults: [ + { + entity: { + __typename: 'GLOSSARY_TERM', + ...glossaryTerm1, + }, + matchedFields: [], + }, + ], + facets: [ + { + field: 'origin', + aggregations: [ + { + value: 'PROD', + count: 3, + }, + ], + }, + { + field: 'platform', + aggregations: [ + { value: 'hdfs', count: 1 }, + { value: 'mysql', count: 1 }, + { value: 'kafka', count: 1 }, + ], + }, + ], + }, + } as GetSearchResultsQuery, + }, + }, { request: { query: GetSearchResultsDocument, diff --git a/datahub-web-react/src/app/entity/glossaryTerm/GlossaryTermEntity.tsx b/datahub-web-react/src/app/entity/glossaryTerm/GlossaryTermEntity.tsx new file mode 100644 index 0000000000..caf217ccaf --- /dev/null +++ b/datahub-web-react/src/app/entity/glossaryTerm/GlossaryTermEntity.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import { BookFilled, BookOutlined } from '@ant-design/icons'; +import { EntityType, GlossaryTerm } from '../../../types.generated'; +import { Entity, IconStyleType, PreviewType } from '../Entity'; +import { Preview } from './preview/Preview'; + +/** + * Definition of the DataHub Dataset entity. + */ +export class GlossaryTermEntity implements Entity { + type: EntityType = EntityType.GlossaryTerm; + + icon = (fontSize: number, styleType: IconStyleType) => { + if (styleType === IconStyleType.TAB_VIEW) { + return ; + } + + if (styleType === IconStyleType.HIGHLIGHT) { + return ; + } + + return ( + + ); + }; + + isSearchEnabled = () => true; + + isBrowseEnabled = () => true; + + getAutoCompleteFieldName = () => 'name'; + + isLineageEnabled = () => false; + + getPathName = () => 'glossary'; + + getCollectionName = () => 'Business Glossary'; + + renderProfile = (urn: string) =>
Coming soon.... {urn}
; + + renderSearch = () =>
; + + renderPreview = (_: PreviewType, data: GlossaryTerm) => { + return ( + + ); + }; +} diff --git a/datahub-web-react/src/app/entity/glossaryTerm/preview/Preview.tsx b/datahub-web-react/src/app/entity/glossaryTerm/preview/Preview.tsx new file mode 100644 index 0000000000..f2a9165763 --- /dev/null +++ b/datahub-web-react/src/app/entity/glossaryTerm/preview/Preview.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { BookOutlined } from '@ant-design/icons'; +import { EntityType, Owner } from '../../../../types.generated'; +import DefaultPreviewCard from '../../../preview/DefaultPreviewCard'; +import { useEntityRegistry } from '../../../useEntityRegistry'; + +export const Preview = ({ + urn, + name, + definition, + owners, +}: { + urn: string; + name: string; + definition?: string | null; + owners?: Array | null; +}): JSX.Element => { + const entityRegistry = useEntityRegistry(); + return ( + } + /> + ); +}; diff --git a/datahub-web-react/src/app/entity/glossaryTerm/preview/__tests__/Preview.test.tsx b/datahub-web-react/src/app/entity/glossaryTerm/preview/__tests__/Preview.test.tsx new file mode 100644 index 0000000000..ae98a8cea9 --- /dev/null +++ b/datahub-web-react/src/app/entity/glossaryTerm/preview/__tests__/Preview.test.tsx @@ -0,0 +1,20 @@ +import { render } from '@testing-library/react'; +import React from 'react'; +import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer'; +import { Preview } from '../Preview'; + +describe('Preview', () => { + it('renders', () => { + const { getByText } = render( + + + , + ); + expect(getByText('definition')).toBeInTheDocument(); + }); +}); diff --git a/datahub-web-react/src/app/preview/DefaultPreviewCard.tsx b/datahub-web-react/src/app/preview/DefaultPreviewCard.tsx index b54d2f62e9..19cd69f050 100644 --- a/datahub-web-react/src/app/preview/DefaultPreviewCard.tsx +++ b/datahub-web-react/src/app/preview/DefaultPreviewCard.tsx @@ -10,6 +10,7 @@ import TagGroup from '../shared/tags/TagGroup'; interface Props { name: string; logoUrl?: string; + logoComponent?: JSX.Element; url: string; description: string; type?: string; @@ -46,6 +47,7 @@ const styles = { export default function DefaultPreviewCard({ name, logoUrl, + logoComponent, url, description, type, @@ -62,16 +64,19 @@ export default function DefaultPreviewCard({ - {logoUrl && } + {logoUrl ? : logoComponent || ''} + {name} - } size={16}> - {type} - {platform} - {qualifier && {qualifier}} - + {(type || platform || qualifier) && ( + } size={16}> + {type} + {platform} + {qualifier && {qualifier}} + + )} diff --git a/datahub-web-react/src/graphql/browse.graphql b/datahub-web-react/src/graphql/browse.graphql index c968ecd102..096391e57f 100644 --- a/datahub-web-react/src/graphql/browse.graphql +++ b/datahub-web-react/src/graphql/browse.graphql @@ -48,6 +48,22 @@ query getBrowseResults($input: BrowseInput!) { ...globalTagsFields } } + ... on GlossaryTerm { + name + ownership { + ...ownershipFields + } + glossaryTermInfo { + definition + termSource + sourceRef + sourceUrl + customProperties { + key + value + } + } + } ... on Chart { urn type diff --git a/datahub-web-react/src/utils/test-utils/TestPageContainer.tsx b/datahub-web-react/src/utils/test-utils/TestPageContainer.tsx index 797eb6adbb..987c25ab5a 100644 --- a/datahub-web-react/src/utils/test-utils/TestPageContainer.tsx +++ b/datahub-web-react/src/utils/test-utils/TestPageContainer.tsx @@ -12,6 +12,7 @@ import { EntityRegistryContext } from '../../entityRegistryContext'; import { TagEntity } from '../../app/entity/tag/Tag'; import defaultThemeConfig from '../../conf/theme/theme_light.config.json'; +import { GlossaryTermEntity } from '../../app/entity/glossaryTerm/GlossaryTermEntity'; type Props = { children: React.ReactNode; @@ -26,6 +27,7 @@ export function getTestEntityRegistry() { entityRegistry.register(new TagEntity()); entityRegistry.register(new DataFlowEntity()); entityRegistry.register(new DataJobEntity()); + entityRegistry.register(new GlossaryTermEntity()); return entityRegistry; }