Skip to content

Commit

Permalink
feat(previews): add previews for glossary terms, tags, and domains (#…
Browse files Browse the repository at this point in the history
…5784)

* add graphql for chart glossary terms

* add placeholder tab component

* continued progress

* finished rendering of other tooltips

* fix tests

* add null check

* force more clicks

* fix analytics test
  • Loading branch information
gabe-lyons authored Sep 1, 2022
1 parent ca29f8b commit 5bf5fc2
Show file tree
Hide file tree
Showing 32 changed files with 591 additions and 221 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ public class ChartType implements SearchableEntityType<Chart, String>, Browsable
CONTAINER_ASPECT_NAME,
DOMAINS_ASPECT_NAME,
DEPRECATION_ASPECT_NAME,
DATA_PLATFORM_INSTANCE_ASPECT_NAME
DATA_PLATFORM_INSTANCE_ASPECT_NAME,
INPUT_FIELDS_ASPECT_NAME
);
private static final Set<String> FACET_FIELDS = ImmutableSet.of("access", "queryType", "tool", "type");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.linkedin.common.Deprecation;
import com.linkedin.common.GlobalTags;
import com.linkedin.common.GlossaryTerms;
import com.linkedin.common.InputFields;
import com.linkedin.common.InstitutionalMemory;
import com.linkedin.common.Ownership;
import com.linkedin.common.Status;
Expand Down Expand Up @@ -86,6 +87,8 @@ public Chart apply(@Nonnull final EntityResponse entityResponse) {
chart.setDeprecation(DeprecationMapper.map(new Deprecation(dataMap))));
mappingHelper.mapToResult(DATA_PLATFORM_INSTANCE_ASPECT_NAME, (dataset, dataMap) ->
dataset.setDataPlatformInstance(DataPlatformInstanceAspectMapper.map(new DataPlatformInstance(dataMap))));
mappingHelper.mapToResult(INPUT_FIELDS_ASPECT_NAME, (chart, dataMap) ->
chart.setInputFields(InputFieldsMapper.map(new InputFields(dataMap), entityUrn)));

return mappingHelper.getResult();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.linkedin.datahub.graphql.types.chart.mappers;

import com.linkedin.common.InputFields;
import com.linkedin.common.urn.Urn;
import com.linkedin.datahub.graphql.generated.InputField;
import com.linkedin.datahub.graphql.types.dataset.mappers.SchemaFieldMapper;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;


public class InputFieldsMapper {

public static final InputFieldsMapper INSTANCE = new InputFieldsMapper();

public static com.linkedin.datahub.graphql.generated.InputFields map(@Nonnull final InputFields metadata, @Nonnull final Urn entityUrn) {
return INSTANCE.apply(metadata, entityUrn);
}

public com.linkedin.datahub.graphql.generated.InputFields apply(@Nonnull final InputFields input, @Nonnull final Urn entityUrn) {
final com.linkedin.datahub.graphql.generated.InputFields result = new com.linkedin.datahub.graphql.generated.InputFields();
result.setFields(input.getFields().stream().map(field -> {
InputField fieldResult = new InputField();

if (field.hasSchemaField()) {
fieldResult.setSchemaField(SchemaFieldMapper.map(field.getSchemaField(), entityUrn));
}
return fieldResult;
}).collect(Collectors.toList()));

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ public class DashboardType implements SearchableEntityType<Dashboard, String>, B
CONTAINER_ASPECT_NAME,
DOMAINS_ASPECT_NAME,
DEPRECATION_ASPECT_NAME,
DATA_PLATFORM_INSTANCE_ASPECT_NAME
DATA_PLATFORM_INSTANCE_ASPECT_NAME,
INPUT_FIELDS_ASPECT_NAME
);
private static final Set<String> FACET_FIELDS = ImmutableSet.of("access", "tool");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.linkedin.common.Deprecation;
import com.linkedin.common.GlobalTags;
import com.linkedin.common.GlossaryTerms;
import com.linkedin.common.InputFields;
import com.linkedin.common.InstitutionalMemory;
import com.linkedin.common.Ownership;
import com.linkedin.common.Status;
Expand All @@ -19,6 +20,7 @@
import com.linkedin.datahub.graphql.generated.DashboardProperties;
import com.linkedin.datahub.graphql.generated.DataPlatform;
import com.linkedin.datahub.graphql.generated.EntityType;
import com.linkedin.datahub.graphql.types.chart.mappers.InputFieldsMapper;
import com.linkedin.datahub.graphql.types.common.mappers.AuditStampMapper;
import com.linkedin.datahub.graphql.types.common.mappers.DataPlatformInstanceAspectMapper;
import com.linkedin.datahub.graphql.types.common.mappers.DeprecationMapper;
Expand Down Expand Up @@ -82,6 +84,8 @@ public Dashboard apply(@Nonnull final EntityResponse entityResponse) {
mappingHelper.mapToResult(GLOBAL_TAGS_ASPECT_NAME, (dataset, dataMap) -> this.mapGlobalTags(dataset, dataMap, entityUrn));
mappingHelper.mapToResult(DATA_PLATFORM_INSTANCE_ASPECT_NAME, (dataset, dataMap) ->
dataset.setDataPlatformInstance(DataPlatformInstanceAspectMapper.map(new DataPlatformInstance(dataMap))));
mappingHelper.mapToResult(INPUT_FIELDS_ASPECT_NAME, (dashboard, dataMap) ->
dashboard.setInputFields(InputFieldsMapper.map(new InputFields(dataMap), entityUrn)));

return mappingHelper.getResult();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public SchemaField apply(@Nonnull final com.linkedin.schema.SchemaField input, @
result.setNullable(input.isNullable());
result.setNativeDataType(input.getNativeDataType());
result.setType(mapSchemaFieldDataType(input.getType()));
result.setLabel(input.getLabel());
if (input.hasGlobalTags()) {
result.setGlobalTags(GlobalTagsMapper.map(input.getGlobalTags(), entityUrn));
result.setTags(GlobalTagsMapper.map(input.getGlobalTags(), entityUrn));
Expand Down
30 changes: 30 additions & 0 deletions datahub-graphql-core/src/main/resources/entity.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2319,6 +2319,11 @@ type SchemaField {
"""
jsonPath: String

"""
Human readable label for the field. Not supplied by all data sources
"""
label: String

"""
Indicates if this field is optional or nullable
"""
Expand Down Expand Up @@ -4332,6 +4337,11 @@ type Dashboard implements EntityWithRelationships & Entity & BrowsableEntity {
Standardized platform urn where the dashboard is defined
"""
platform: DataPlatform!

"""
Input fields that power all the charts in the dashboard
"""
inputFields: InputFields
}

"""
Expand Down Expand Up @@ -4612,6 +4622,11 @@ type Chart implements EntityWithRelationships & Entity & BrowsableEntity {
Standardized platform urn where the chart is defined
"""
platform: DataPlatform!

"""
Input fields to power the chart
"""
inputFields: InputFields
}

"""
Expand Down Expand Up @@ -9188,6 +9203,21 @@ input BatchUpdateSoftDeletedInput {
deleted: Boolean!
}

"""
Input fields of the chart
"""
type InputFields {
fields: [InputField]
}

"""
Input field of the chart
"""
type InputField {
schemaFieldUrn: String
schemaField: SchemaField
}

enum UserSetting {
"""
Show simplified homepage
Expand Down
9 changes: 9 additions & 0 deletions datahub-web-react/src/Mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,10 @@ export const dataset3 = {
description: 'sample definition',
definition: 'sample definition',
termSource: 'sample term source',
customProperties: null,
},
ownership: null,
parentNodes: null,
},
associatedUrn: 'urn:li:dataset:3',
},
Expand Down Expand Up @@ -439,6 +442,7 @@ export const dataset3 = {
createdAt: 0,
fields: [
{
__typename: 'SchemaField',
nullable: false,
recursive: false,
fieldPath: 'user_id',
Expand All @@ -449,8 +453,10 @@ export const dataset3 = {
jsonPath: null,
globalTags: null,
glossaryTerms: null,
label: 'hi',
},
{
__typename: 'SchemaField',
nullable: false,
recursive: false,
fieldPath: 'user_name',
Expand All @@ -461,6 +467,7 @@ export const dataset3 = {
jsonPath: null,
globalTags: null,
glossaryTerms: null,
label: 'hi',
},
],
hash: '',
Expand Down Expand Up @@ -862,6 +869,7 @@ const glossaryTerm1 = {
sourceRef: 'sourceRef',
sourceURI: 'sourceURI',
},
parentNodes: null,
deprecation: null,
} as GlossaryTerm;

Expand Down Expand Up @@ -934,6 +942,7 @@ const glossaryTerm2 = {
],
__typename: 'EntityRelationshipsResult',
},
parentNodes: null,
__typename: 'GlossaryTerm',
};

Expand Down
4 changes: 4 additions & 0 deletions datahub-web-react/src/app/entity/Entity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export enum PreviewType {
* A tiny search preview for text-box search.
*/
MINI_SEARCH,
/**
* Previews rendered when hovering over the entity in a compact list
*/
HOVER_CARD,
}

export enum IconStyleType {
Expand Down
23 changes: 11 additions & 12 deletions datahub-web-react/src/app/entity/chart/ChartEntity.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { LineChartOutlined } from '@ant-design/icons';
import * as React from 'react';
import { Typography } from 'antd';

import { Chart, EntityType, SearchResult } from '../../../types.generated';
import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '../Entity';
Expand All @@ -20,8 +19,8 @@ import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domai
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
import { LineageTab } from '../shared/tabs/Lineage/LineageTab';
import { ChartStatsSummarySubHeader } from './profile/stats/ChartStatsSummarySubHeader';
import { getMatchPrioritizingPrimary } from '../shared/utils';
import { FIELDS_TO_HIGHLIGHT } from '../dataset/search/highlights';
import { InputFieldsTab } from '../shared/tabs/Entity/InputFieldsTab';
import { ChartSnippet } from './ChartSnippet';

/**
* Definition of the DataHub Chart entity.
Expand Down Expand Up @@ -84,6 +83,14 @@ export class ChartEntity implements Entity<Chart> {
name: 'Documentation',
component: DocumentationTab,
},
{
name: 'Fields',
component: InputFieldsTab,
display: {
visible: (_, chart: GetChartQuery) => (chart?.chart?.inputFields?.fields?.length || 0) > 0,
enabled: (_, chart: GetChartQuery) => (chart?.chart?.inputFields?.fields?.length || 0) > 0,
},
},
{
name: 'Properties',
component: PropertiesTab,
Expand Down Expand Up @@ -169,8 +176,6 @@ export class ChartEntity implements Entity<Chart> {

renderSearch = (result: SearchResult) => {
const data = result.entity as Chart;
const matchedField = getMatchPrioritizingPrimary(result.matchedFields, 'fieldLabels');

return (
<ChartPreview
urn={data.urn}
Expand All @@ -190,13 +195,7 @@ export class ChartEntity implements Entity<Chart> {
lastUpdatedMs={data.properties?.lastModified?.time}
createdMs={data.properties?.created?.time}
externalUrl={data.properties?.externalUrl}
snippet={
matchedField && (
<Typography.Text>
Matches {FIELDS_TO_HIGHLIGHT.get(matchedField.name)} <b>{matchedField.value}</b>
</Typography.Text>
)
}
snippet={<ChartSnippet matchedFields={result.matchedFields} inputFields={data.inputFields} />}
/>
);
};
Expand Down
50 changes: 50 additions & 0 deletions datahub-web-react/src/app/entity/chart/ChartSnippet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';

import { Typography } from 'antd';
import { InputFields, MatchedField, Maybe } from '../../../types.generated';
import TagTermGroup from '../../shared/tags/TagTermGroup';
import { FIELDS_TO_HIGHLIGHT } from '../dataset/search/highlights';
import { getMatchPrioritizingPrimary } from '../shared/utils';

type Props = {
matchedFields: MatchedField[];
inputFields: Maybe<InputFields> | undefined;
};

const LABEL_INDEX_NAME = 'fieldLabels';
const TYPE_PROPERTY_KEY_NAME = 'type';

export const ChartSnippet = ({ matchedFields, inputFields }: Props) => {
const matchedField = getMatchPrioritizingPrimary(matchedFields, 'fieldLabels');

if (matchedField?.name === LABEL_INDEX_NAME) {
const matchedSchemaField = inputFields?.fields?.find(
(field) => field?.schemaField?.label === matchedField.value,
);
const matchedGlossaryTerm = matchedSchemaField?.schemaField?.glossaryTerms?.terms?.find(
(term) => term?.term?.name === matchedField.value,
);

if (matchedGlossaryTerm) {
let termType = 'term';
const typeProperty = matchedGlossaryTerm.term.properties?.customProperties?.find(
(property) => property.key === TYPE_PROPERTY_KEY_NAME,
);
if (typeProperty) {
termType = typeProperty.value || termType;
}

return (
<Typography.Text>
Matches {termType} <TagTermGroup uneditableGlossaryTerms={{ terms: [matchedGlossaryTerm] }} />
</Typography.Text>
);
}
}

return matchedField ? (
<Typography.Text>
Matches {FIELDS_TO_HIGHLIGHT.get(matchedField.name)} <b>{matchedField.value}</b>
</Typography.Text>
) : null;
};
24 changes: 13 additions & 11 deletions datahub-web-react/src/app/entity/dashboard/DashboardEntity.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { DashboardFilled, DashboardOutlined } from '@ant-design/icons';
import * as React from 'react';
import { Typography } from 'antd';

import {
GetDashboardQuery,
Expand All @@ -24,8 +23,8 @@ import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domai
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
import { LineageTab } from '../shared/tabs/Lineage/LineageTab';
import { DashboardStatsSummarySubHeader } from './profile/DashboardStatsSummarySubHeader';
import { FIELDS_TO_HIGHLIGHT } from '../dataset/search/highlights';
import { getMatchPrioritizingPrimary } from '../shared/utils';
import { InputFieldsTab } from '../shared/tabs/Entity/InputFieldsTab';
import { ChartSnippet } from '../chart/ChartSnippet';

/**
* Definition of the DataHub Dashboard entity.
Expand Down Expand Up @@ -88,6 +87,16 @@ export class DashboardEntity implements Entity<Dashboard> {
name: 'Documentation',
component: DocumentationTab,
},
{
name: 'Fields',
component: InputFieldsTab,
display: {
visible: (_, dashboard: GetDashboardQuery) =>
(dashboard?.dashboard?.inputFields?.fields?.length || 0) > 0,
enabled: (_, dashboard: GetDashboardQuery) =>
(dashboard?.dashboard?.inputFields?.fields?.length || 0) > 0,
},
},
{
name: 'Properties',
component: PropertiesTab,
Expand Down Expand Up @@ -184,7 +193,6 @@ export class DashboardEntity implements Entity<Dashboard> {

renderSearch = (result: SearchResult) => {
const data = result.entity as Dashboard;
const matchedField = getMatchPrioritizingPrimary(result.matchedFields, 'fieldLabels');

return (
<DashboardPreview
Expand All @@ -207,13 +215,7 @@ export class DashboardEntity implements Entity<Dashboard> {
statsSummary={data.statsSummary}
lastUpdatedMs={data.properties?.lastModified?.time}
createdMs={data.properties?.created?.time}
snippet={
matchedField && (
<Typography.Text>
Matches {FIELDS_TO_HIGHLIGHT.get(matchedField.name)} <b>{matchedField.value}</b>
</Typography.Text>
)
}
snippet={<ChartSnippet matchedFields={result.matchedFields} inputFields={data.inputFields} />}
/>
);
};
Expand Down
Loading

0 comments on commit 5bf5fc2

Please sign in to comment.