feat(ui): Edit glossary term descriptions via UI (#4156)

This commit is contained in:
John Joyce 2022-02-16 15:01:22 -08:00 committed by GitHub
parent e3599c521c
commit da74943485
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 107 additions and 55 deletions

View File

@ -46,6 +46,7 @@ public class GlossaryTermType implements SearchableEntityType<GlossaryTerm>, Bro
GLOSSARY_TERM_KEY_ASPECT_NAME,
GLOSSARY_TERM_INFO_ASPECT_NAME,
GLOSSARY_RELATED_TERM_ASPECT_NAME,
INSTITUTIONAL_MEMORY_ASPECT_NAME,
OWNERSHIP_ASPECT_NAME,
STATUS_ASPECT_NAME,
BROWSE_PATHS_ASPECT_NAME,
@ -76,7 +77,7 @@ public class GlossaryTermType implements SearchableEntityType<GlossaryTerm>, Bro
try {
final Map<Urn, EntityResponse> glossaryTermMap = _entityClient.batchGetV2(GLOSSARY_TERM_ENTITY_NAME,
new HashSet<>(glossaryTermUrns), null, context.getAuthentication());
new HashSet<>(glossaryTermUrns), ASPECTS_TO_RESOLVE, context.getAuthentication());
final List<EntityResponse> gmsResults = new ArrayList<>();
for (Urn urn : glossaryTermUrns) {

View File

@ -1,12 +1,14 @@
package com.linkedin.datahub.graphql.types.glossary.mappers;
import com.linkedin.common.Deprecation;
import com.linkedin.common.InstitutionalMemory;
import com.linkedin.common.Ownership;
import com.linkedin.data.DataMap;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.datahub.graphql.generated.EntityType;
import com.linkedin.datahub.graphql.generated.GlossaryTerm;
import com.linkedin.datahub.graphql.types.common.mappers.DeprecationMapper;
import com.linkedin.datahub.graphql.types.common.mappers.InstitutionalMemoryMapper;
import com.linkedin.datahub.graphql.types.common.mappers.OwnershipMapper;
import com.linkedin.datahub.graphql.types.common.mappers.util.MappingHelper;
import com.linkedin.datahub.graphql.types.glossary.GlossaryTermUtils;
@ -30,31 +32,43 @@ public class GlossaryTermMapper implements ModelMapper<EntityResponse, GlossaryT
public static final GlossaryTermMapper INSTANCE = new GlossaryTermMapper();
public static GlossaryTerm map(@Nonnull final EntityResponse entityResponse) {
return INSTANCE.apply(entityResponse);
return INSTANCE.apply(entityResponse);
}
@Override
public GlossaryTerm apply(@Nonnull final EntityResponse entityResponse) {
GlossaryTerm result = new GlossaryTerm();
result.setUrn(entityResponse.getUrn().toString());
result.setType(EntityType.GLOSSARY_TERM);
GlossaryTerm result = new GlossaryTerm();
result.setUrn(entityResponse.getUrn().toString());
result.setType(EntityType.GLOSSARY_TERM);
final String legacyName = GlossaryTermUtils.getGlossaryTermName(entityResponse.getUrn().getId());
EnvelopedAspectMap aspectMap = entityResponse.getAspects();
MappingHelper<GlossaryTerm> mappingHelper = new MappingHelper<>(aspectMap, result);
mappingHelper.mapToResult(GLOSSARY_TERM_KEY_ASPECT_NAME, this::mapGlossaryTermKey);
mappingHelper.mapToResult(GLOSSARY_TERM_INFO_ASPECT_NAME, (glossaryTerm, dataMap) ->
glossaryTerm.setGlossaryTermInfo(GlossaryTermInfoMapper.map(new GlossaryTermInfo(dataMap))));
mappingHelper.mapToResult(OWNERSHIP_ASPECT_NAME, (glossaryTerm, dataMap) ->
glossaryTerm.setOwnership(OwnershipMapper.map(new Ownership(dataMap))));
EnvelopedAspectMap aspectMap = entityResponse.getAspects();
MappingHelper<GlossaryTerm> mappingHelper = new MappingHelper<>(aspectMap, result);
mappingHelper.mapToResult(GLOSSARY_TERM_KEY_ASPECT_NAME, this::mapGlossaryTermKey);
mappingHelper.mapToResult(GLOSSARY_TERM_INFO_ASPECT_NAME, (glossaryTerm, dataMap) ->
glossaryTerm.setGlossaryTermInfo(GlossaryTermInfoMapper.map(new GlossaryTermInfo(dataMap))));
mappingHelper.mapToResult(GLOSSARY_TERM_INFO_ASPECT_NAME, (glossaryTerm, dataMap) ->
glossaryTerm.setProperties(GlossaryTermPropertiesMapper.map(new GlossaryTermInfo(dataMap))));
mappingHelper.mapToResult(OWNERSHIP_ASPECT_NAME, (glossaryTerm, dataMap) ->
glossaryTerm.setOwnership(OwnershipMapper.map(new Ownership(dataMap))));
mappingHelper.mapToResult(DEPRECATION_ASPECT_NAME, (glossaryTerm, dataMap) ->
glossaryTerm.setDeprecation(DeprecationMapper.map(new Deprecation(dataMap))));
glossaryTerm.setDeprecation(DeprecationMapper.map(new Deprecation(dataMap))));
mappingHelper.mapToResult(INSTITUTIONAL_MEMORY_ASPECT_NAME, (dataset, dataMap) ->
dataset.setInstitutionalMemory(InstitutionalMemoryMapper.map(new InstitutionalMemory(dataMap))));
return mappingHelper.getResult();
// If there's no name property, resort to the legacy name computation.
if (result.getGlossaryTermInfo() != null && result.getGlossaryTermInfo().getName() == null) {
result.getGlossaryTermInfo().setName(legacyName);
}
if (result.getProperties() != null && result.getProperties().getName() == null) {
result.getProperties().setName(legacyName);
}
return mappingHelper.getResult();
}
private void mapGlossaryTermKey(@Nonnull GlossaryTerm glossaryTerm, @Nonnull DataMap dataMap) {
GlossaryTermKey glossaryTermKey = new GlossaryTermKey(dataMap);
glossaryTerm.setName(GlossaryTermUtils.getGlossaryTermName(glossaryTermKey.getName()));
glossaryTerm.setHierarchicalName(glossaryTermKey.getName());
GlossaryTermKey glossaryTermKey = new GlossaryTermKey(dataMap);
glossaryTerm.setName(GlossaryTermUtils.getGlossaryTermName(glossaryTermKey.getName()));
glossaryTerm.setHierarchicalName(glossaryTermKey.getName());
}
}

View File

@ -36,14 +36,21 @@ public class TagMapper implements ModelMapper<EntityResponse, Tag> {
final Tag result = new Tag();
result.setUrn(entityResponse.getUrn().toString());
result.setType(EntityType.TAG);
final String legacyName = entityResponse.getUrn().getId();
result.setName(legacyName);
EnvelopedAspectMap aspectMap = entityResponse.getAspects();
MappingHelper<Tag> mappingHelper = new MappingHelper<>(aspectMap, result);
mappingHelper.mapToResult(TAG_KEY_ASPECT_NAME, this::mapTagKey);
mappingHelper.mapToResult(TAG_PROPERTIES_ASPECT_NAME, (tag, dataMap) ->
tag.setDescription(new TagProperties(dataMap).getDescription()));
mappingHelper.mapToResult(TAG_PROPERTIES_ASPECT_NAME, this::mapTagProperties);
mappingHelper.mapToResult(OWNERSHIP_ASPECT_NAME, (tag, dataMap) ->
tag.setOwnership(OwnershipMapper.map(new Ownership(dataMap))));
if (result.getProperties() != null && result.getProperties().getName() == null) {
result.getProperties().setName(legacyName);
}
return mappingHelper.getResult();
}

View File

@ -802,6 +802,11 @@ type GlossaryTerm implements Entity {
"""
ownership: Ownership
"""
References to internal resources related to the Glossary Term
"""
institutionalMemory: InstitutionalMemory
"""
A standard Entity Type
"""
@ -818,7 +823,7 @@ type GlossaryTerm implements Entity {
hierarchicalName: String!
"""
Additional read only properties associated with the Glossary Term
Additional properties associated with the Glossary Term
"""
properties: GlossaryTermProperties
@ -858,7 +863,7 @@ type GlossaryTermInfo {
"""
Description of the glossary term
"""
description: String!
description: String
"""
Definition of the glossary term. Deprecated - Use 'description' instead.
@ -898,12 +903,12 @@ type GlossaryTermProperties {
"""
The name of the Glossary Term
"""
name: String
name: String!
"""
Description of the glossary term
"""
description: String!
description: String
"""
Definition of the glossary term. Deprecated - Use 'description' instead.
@ -2579,7 +2584,7 @@ type TagProperties {
"""
A display name for the Tag
"""
name: String
name: String!
"""
A description of the Tag

View File

@ -839,12 +839,16 @@ const glossaryTerm3 = {
hasRelatedTerms: [
{
urn: 'urn:li:glossaryTerm:example.glossaryterm3',
name: 'glossaryterm3',
properties: {
name: 'glossaryterm3',
},
__typename: 'GlossaryTerm',
},
{
urn: 'urn:li:glossaryTerm:example.glossaryterm4',
name: 'glossaryterm4',
properties: {
name: 'glossaryterm4',
},
__typename: 'GlossaryTerm',
},
],

View File

@ -121,8 +121,8 @@ export const sampleSchemaWithTags: Schema = {
{
term: {
type: EntityType.GlossaryTerm,
urn: 'urn:li:glossaryTerm:sample-glossary-term',
name: 'sample-glossary-term',
urn: 'urn:li:glossaryTerm:sample-glossary-term',
hierarchicalName: 'example.sample-glossary-term',
properties: {
name: 'sample-glossary-term',

View File

@ -11,8 +11,9 @@ import { SchemaTab } from '../shared/tabs/Dataset/Schema/SchemaTab';
import GlossaryRelatedEntity from './profile/GlossaryRelatedEntity';
import GlossayRelatedTerms from './profile/GlossaryRelatedTerms';
import { SidebarOwnerSection } from '../shared/containers/profile/sidebar/Ownership/SidebarOwnerSection';
import GlossarySidebarAboutSection from './profile/GlossarySidebarAboutSection';
import { PropertiesTab } from '../shared/tabs/Properties/PropertiesTab';
import { DocumentationTab } from '../shared/tabs/Documentation/DocumentationTab';
import { SidebarAboutSection } from '../shared/containers/profile/sidebar/SidebarAboutSection';
/**
* Definition of the DataHub Dataset entity.
@ -64,6 +65,10 @@ export class GlossaryTermEntity implements Entity<GlossaryTerm> {
name: 'Related Entities',
component: GlossaryRelatedEntity,
},
{
name: 'Documentation',
component: DocumentationTab,
},
{
name: 'Schema',
component: SchemaTab,
@ -88,7 +93,7 @@ export class GlossaryTermEntity implements Entity<GlossaryTerm> {
]}
sidebarSections={[
{
component: GlossarySidebarAboutSection,
component: SidebarAboutSection,
},
{
component: SidebarOwnerSection,
@ -102,7 +107,7 @@ export class GlossaryTermEntity implements Entity<GlossaryTerm> {
getOverridePropertiesFromEntity = (glossaryTerm?: GlossaryTerm | null): GenericEntityProperties => {
// if dataset has subTypes filled out, pick the most specific subtype and return it
return {
customProperties: glossaryTerm?.glossaryTermInfo?.customProperties,
customProperties: glossaryTerm?.properties?.customProperties,
};
};
@ -114,15 +119,15 @@ export class GlossaryTermEntity implements Entity<GlossaryTerm> {
return (
<Preview
urn={data?.urn}
name={data?.name}
definition={data?.glossaryTermInfo?.definition}
name={this.displayName(data)}
description={data?.properties?.description || ''}
owners={data?.ownership?.owners}
/>
);
};
displayName = (data: GlossaryTerm) => {
return data.name;
return data.properties?.name || data.name || data.urn;
};
platformLogoUrl = (_: GlossaryTerm) => {

View File

@ -7,12 +7,12 @@ import { useEntityRegistry } from '../../../useEntityRegistry';
export const Preview = ({
urn,
name,
definition,
description,
owners,
}: {
urn: string;
name: string;
definition?: string | null;
description?: string | null;
owners?: Array<Owner> | null;
}): JSX.Element => {
const entityRegistry = useEntityRegistry();
@ -20,7 +20,7 @@ export const Preview = ({
<DefaultPreviewCard
url={entityRegistry.getEntityUrl(EntityType.GlossaryTerm, urn)}
name={name || ''}
description={definition || ''}
description={description || ''}
owners={owners}
logoComponent={<BookOutlined style={{ fontSize: '20px' }} />}
type="Glossary Term"

View File

@ -13,7 +13,7 @@ describe('Preview', () => {
<Preview
urn="urn:li:glossaryTerm:instruments.FinancialInstrument_v1"
name="name"
definition="definition"
description="definition"
owners={null}
/>
</TestPageContainer>

View File

@ -68,7 +68,7 @@ export class TagEntity implements Entity<Tag> {
};
displayName = (data: Tag) => {
return data.name;
return data.properties?.name || data.name || data.urn;
};
getGenericEntityProperties = (tag: Tag) => {

View File

@ -160,7 +160,7 @@ export default function TagTermGroup({
<TagLink to={entityRegistry.getEntityUrl(EntityType.GlossaryTerm, term.term.urn)} key={term.term.urn}>
<Tag closable={false}>
<BookOutlined style={{ marginRight: '3%' }} />
{term.term.name}
{entityRegistry.getDisplayName(EntityType.GlossaryTerm, term.term)}
</Tag>
</TagLink>
))}
@ -174,7 +174,7 @@ export default function TagTermGroup({
}}
>
<BookOutlined style={{ marginRight: '3%' }} />
{term.term.name}
{entityRegistry.getDisplayName(EntityType.GlossaryTerm, term.term)}
</Tag>
</TagLink>
))}
@ -185,7 +185,7 @@ export default function TagTermGroup({
return (
<TagLink to={entityRegistry.getEntityUrl(EntityType.Tag, tag.tag.urn)} key={tag.tag.urn}>
<StyledTag $colorHash={tag.tag.urn} closable={false}>
{tag.tag.name}
{entityRegistry.getDisplayName(EntityType.Tag, tag.tag)}
</StyledTag>
</TagLink>
);

View File

@ -71,11 +71,12 @@ query getBrowseResults($input: BrowseInput!) {
}
}
... on GlossaryTerm {
name
ownership {
...ownershipFields
}
glossaryTermInfo {
properties {
name
description
definition
termSource
sourceRef

View File

@ -32,5 +32,8 @@ query getContainer($urn: String!) {
container {
...entityContainer
}
domain {
...entityDomain
}
}
}

View File

@ -13,6 +13,9 @@ fragment glossaryTerms on GlossaryTerms {
term {
urn
name
properties {
name
}
}
}
}

View File

@ -31,8 +31,12 @@ query getGlossaryTerm($urn: String!, $start: Int, $count: Int) {
ownership {
...ownershipFields
}
glossaryTermInfo {
definition
institutionalMemory {
...institutionalMemoryFields
}
properties {
name
description
termSource
sourceRef
sourceUrl
@ -42,10 +46,8 @@ query getGlossaryTerm($urn: String!, $start: Int, $count: Int) {
value
}
}
schemaMetadata(version: 0) {
schemaMetadata(version: 0) {
...schemaMetadataFields
}
}
}

View File

@ -186,11 +186,13 @@ fragment entityPreview on Entity {
... on GlossaryTerm {
name
hierarchicalName
glossaryTermInfo {
definition
properties {
name
description
termSource
sourceRef
sourceUrl
rawSchema
customProperties {
key
value

View File

@ -219,11 +219,13 @@ fragment searchResults on SearchResults {
... on GlossaryTerm {
name
hierarchicalName
glossaryTermInfo {
definition
properties {
name
description
termSource
sourceRef
sourceUrl
rawSchema
customProperties {
key
value
@ -345,7 +347,9 @@ fragment searchResults on SearchResults {
description
}
... on GlossaryTerm {
name
properties {
name
}
}
... on DataPlatform {
...platformFields

View File

@ -76,6 +76,7 @@ entities:
keyAspect: glossaryTermKey
aspects:
- glossaryTermInfo
- institutionalMemory
- schemaMetadata
- ownership
- deprecation

View File

@ -32,5 +32,5 @@ dependencies {
because("previous versions are vulnerable to CVE-2021-45105")
}
}
}

View File

@ -134,7 +134,7 @@ public class Constants {
// Policy
public static final String DATAHUB_POLICY_INFO_ASPECT_NAME = "dataHubPolicyInfo";
//Tag
// Tag
public static final String TAG_KEY_ASPECT_NAME = "tagKey";
public static final String TAG_PROPERTIES_ASPECT_NAME = "tagProperties";