Skip to content

Commit 5252321

Browse files
jjoyce0510ljubon
authored andcommitted
feat(ui): Partial support for Chart usage (datahub-project#5473)
1 parent 935b423 commit 5252321

File tree

14 files changed

+308
-10
lines changed

14 files changed

+308
-10
lines changed

.github/pull.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
version: "1"
2+
rules:
3+
- base: master
4+
upstream: datahub-project:master
5+
mergeMethod: hardreset
6+
mergeUnstable: false
7+
label: ":arrow_heading_down: pull"
8+
conflictLabel: "merge-conflict"

datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import com.linkedin.datahub.graphql.resolvers.auth.RevokeAccessTokenResolver;
8383
import com.linkedin.datahub.graphql.resolvers.browse.BrowsePathsResolver;
8484
import com.linkedin.datahub.graphql.resolvers.browse.BrowseResolver;
85+
import com.linkedin.datahub.graphql.resolvers.chart.ChartStatsSummaryResolver;
8586
import com.linkedin.datahub.graphql.resolvers.config.AppConfigResolver;
8687
import com.linkedin.datahub.graphql.resolvers.container.ContainerEntitiesResolver;
8788
import com.linkedin.datahub.graphql.resolvers.container.ParentContainersResolver;
@@ -1080,6 +1081,7 @@ private void configureChartResolvers(final RuntimeWiring.Builder builder) {
10801081
})
10811082
)
10821083
.dataFetcher("parentContainers", new ParentContainersResolver(entityClient))
1084+
.dataFetcher("statsSummary", new ChartStatsSummaryResolver(this.timeseriesAspectService))
10831085
);
10841086
builder.type("ChartInfo", typeWiring -> typeWiring
10851087
.dataFetcher("inputs", new LoadableTypeBatchResolver<>(datasetType,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.linkedin.datahub.graphql.resolvers.chart;
2+
3+
import com.google.common.cache.Cache;
4+
import com.google.common.cache.CacheBuilder;
5+
import com.linkedin.common.urn.Urn;
6+
import com.linkedin.datahub.graphql.generated.ChartStatsSummary;
7+
import com.linkedin.metadata.timeseries.TimeseriesAspectService;
8+
import graphql.schema.DataFetcher;
9+
import graphql.schema.DataFetchingEnvironment;
10+
import java.util.concurrent.CompletableFuture;
11+
import java.util.concurrent.TimeUnit;
12+
import lombok.extern.slf4j.Slf4j;
13+
14+
15+
@Slf4j
16+
public class ChartStatsSummaryResolver implements DataFetcher<CompletableFuture<ChartStatsSummary>> {
17+
18+
private final TimeseriesAspectService timeseriesAspectService;
19+
private final Cache<Urn, ChartStatsSummary> summaryCache;
20+
21+
public ChartStatsSummaryResolver(final TimeseriesAspectService timeseriesAspectService) {
22+
this.timeseriesAspectService = timeseriesAspectService;
23+
this.summaryCache = CacheBuilder.newBuilder()
24+
.maximumSize(10000)
25+
.expireAfterWrite(6, TimeUnit.HOURS)
26+
.build();
27+
}
28+
29+
@Override
30+
public CompletableFuture<ChartStatsSummary> get(DataFetchingEnvironment environment) throws Exception {
31+
// Not yet implemented
32+
return CompletableFuture.completedFuture(null);
33+
}
34+
}

datahub-graphql-core/src/main/resources/entity.graphql

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4139,7 +4139,7 @@ type Dashboard implements EntityWithRelationships & Entity {
41394139
usageStats(startTimeMillis: Long, endTimeMillis: Long, limit: Int): DashboardUsageQueryResult
41404140

41414141
"""
4142-
Experimental - Summary operational & usage statistics about a Dataset
4142+
Experimental - Summary operational & usage statistics about a Dashboard
41434143
"""
41444144
statsSummary: DashboardStatsSummary
41454145

@@ -4396,6 +4396,13 @@ type Chart implements EntityWithRelationships & Entity {
43964396
"""
43974397
dataPlatformInstance: DataPlatformInstance
43984398

4399+
"""
4400+
Not yet implemented.
4401+
4402+
Experimental - Summary operational & usage statistics about a Chart
4403+
"""
4404+
statsSummary: ChartStatsSummary
4405+
43994406
"""
44004407
Granular API for querying edges extending from this entity
44014408
"""
@@ -5467,6 +5474,32 @@ type DashboardStatsSummary {
54675474
}
54685475

54695476

5477+
"""
5478+
Experimental - subject to change. A summary of usage metrics about a Chart.
5479+
"""
5480+
type ChartStatsSummary {
5481+
"""
5482+
The total view count for the chart
5483+
"""
5484+
viewCount: Int
5485+
5486+
"""
5487+
The view count in the last 30 days
5488+
"""
5489+
viewCountLast30Days: Int
5490+
5491+
"""
5492+
The unique user count in the past 30 days
5493+
"""
5494+
uniqueUserCountLast30Days: Int
5495+
5496+
"""
5497+
The top users in the past 30 days
5498+
"""
5499+
topUsersLast30Days: [CorpUser!]
5500+
}
5501+
5502+
54705503
"""
54715504
The duration of a fixed window of time
54725505
"""

datahub-web-react/src/app/entity/chart/ChartEntity.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { getDataForEntityType } from '../shared/containers/profile/utils';
1717
import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection';
1818
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
1919
import { LineageTab } from '../shared/tabs/Lineage/LineageTab';
20+
import { ChartStatsSummarySubHeader } from './profile/stats/ChartStatsSummarySubHeader';
2021

2122
/**
2223
* Definition of the DataHub Chart entity.
@@ -71,6 +72,9 @@ export class ChartEntity implements Entity<Chart> {
7172
useUpdateQuery={useUpdateChartMutation}
7273
getOverrideProperties={this.getOverridePropertiesFromEntity}
7374
headerDropdownItems={new Set([EntityMenuItems.COPY_URL, EntityMenuItems.UPDATE_DEPRECATION])}
75+
subHeader={{
76+
component: ChartStatsSummarySubHeader,
77+
}}
7478
tabs={[
7579
{
7680
name: 'Documentation',
@@ -176,6 +180,10 @@ export class ChartEntity implements Entity<Chart> {
176180
logoUrl={data?.platform?.properties?.logoUrl || ''}
177181
domain={data.domain?.domain}
178182
deprecation={data.deprecation}
183+
statsSummary={data.statsSummary}
184+
lastUpdatedMs={data.properties?.lastModified?.time}
185+
createdMs={data.properties?.created?.time}
186+
externalUrl={data.properties?.externalUrl}
179187
/>
180188
);
181189
};

datahub-web-react/src/app/entity/chart/preview/ChartPreview.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import {
1010
SearchInsight,
1111
ParentContainersResult,
1212
Deprecation,
13+
ChartStatsSummary,
1314
} from '../../../../types.generated';
1415
import DefaultPreviewCard from '../../../preview/DefaultPreviewCard';
1516
import { useEntityRegistry } from '../../../useEntityRegistry';
1617
import { capitalizeFirstLetter } from '../../../shared/textUtil';
1718
import { IconStyleType } from '../../Entity';
19+
import { ChartStatsSummary as ChartStatsSummaryView } from '../shared/ChartStatsSummary';
1820

1921
export const ChartPreview = ({
2022
urn,
@@ -31,6 +33,10 @@ export const ChartPreview = ({
3133
insights,
3234
logoUrl,
3335
deprecation,
36+
statsSummary,
37+
lastUpdatedMs,
38+
createdMs,
39+
externalUrl,
3440
parentContainers,
3541
}: {
3642
urn: string;
@@ -47,6 +53,10 @@ export const ChartPreview = ({
4753
insights?: Array<SearchInsight> | null;
4854
logoUrl?: string | null;
4955
deprecation?: Deprecation | null;
56+
statsSummary?: ChartStatsSummary | null;
57+
lastUpdatedMs?: number | null;
58+
createdMs?: number | null;
59+
externalUrl?: string | null;
5060
parentContainers?: ParentContainersResult | null;
5161
}): JSX.Element => {
5262
const entityRegistry = useEntityRegistry();
@@ -71,6 +81,15 @@ export const ChartPreview = ({
7181
insights={insights}
7282
parentContainers={parentContainers}
7383
deprecation={deprecation}
84+
externalUrl={externalUrl}
85+
subHeader={
86+
<ChartStatsSummaryView
87+
viewCount={statsSummary?.viewCount}
88+
uniqueUserCountLast30Days={statsSummary?.uniqueUserCountLast30Days}
89+
lastUpdatedMs={lastUpdatedMs}
90+
createdMs={createdMs}
91+
/>
92+
}
7493
/>
7594
);
7695
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { ChartStatsSummary as ChartStatsSummaryObj } from '../../../../../types.generated';
3+
import { useBaseEntity } from '../../../shared/EntityContext';
4+
import { GetChartQuery } from '../../../../../graphql/chart.generated';
5+
import { ChartStatsSummary } from '../../shared/ChartStatsSummary';
6+
7+
export const ChartStatsSummarySubHeader = () => {
8+
const result = useBaseEntity<GetChartQuery>();
9+
const chart = result?.chart;
10+
const maybeStatsSummary = chart?.statsSummary as ChartStatsSummaryObj;
11+
const viewCount = maybeStatsSummary?.viewCount;
12+
const uniqueUserCountLast30Days = maybeStatsSummary?.uniqueUserCountLast30Days;
13+
const lastUpdatedMs = chart?.properties?.lastModified?.time;
14+
const createdMs = chart?.properties?.created?.time;
15+
16+
return (
17+
<ChartStatsSummary
18+
viewCount={viewCount}
19+
uniqueUserCountLast30Days={uniqueUserCountLast30Days}
20+
lastUpdatedMs={lastUpdatedMs}
21+
createdMs={createdMs}
22+
/>
23+
);
24+
};
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import { Popover, Tooltip } from 'antd';
4+
import { ClockCircleOutlined, EyeOutlined, TeamOutlined, QuestionCircleOutlined } from '@ant-design/icons';
5+
import { formatNumberWithoutAbbreviation } from '../../../shared/formatNumber';
6+
import { ANTD_GRAY } from '../../shared/constants';
7+
import { toLocalDateTimeString, toRelativeTimeString } from '../../../shared/time/timeUtils';
8+
import { StatsSummary } from '../../shared/components/styled/StatsSummary';
9+
10+
const StatText = styled.span`
11+
color: ${ANTD_GRAY[8]};
12+
`;
13+
14+
const HelpIcon = styled(QuestionCircleOutlined)`
15+
color: ${ANTD_GRAY[7]};
16+
padding-left: 4px;
17+
`;
18+
19+
type Props = {
20+
chartCount?: number | null;
21+
viewCount?: number | null;
22+
uniqueUserCountLast30Days?: number | null;
23+
lastUpdatedMs?: number | null;
24+
createdMs?: number | null;
25+
};
26+
27+
export const ChartStatsSummary = ({
28+
chartCount,
29+
viewCount,
30+
uniqueUserCountLast30Days,
31+
lastUpdatedMs,
32+
createdMs,
33+
}: Props) => {
34+
const statsViews = [
35+
(!!chartCount && (
36+
<StatText>
37+
<b>{chartCount}</b> charts
38+
</StatText>
39+
)) ||
40+
undefined,
41+
(!!viewCount && (
42+
<StatText>
43+
<EyeOutlined style={{ marginRight: 8, color: ANTD_GRAY[7] }} />
44+
<b>{formatNumberWithoutAbbreviation(viewCount)}</b> views
45+
</StatText>
46+
)) ||
47+
undefined,
48+
(!!uniqueUserCountLast30Days && (
49+
<StatText>
50+
<TeamOutlined style={{ marginRight: 8, color: ANTD_GRAY[7] }} />
51+
<b>{formatNumberWithoutAbbreviation(uniqueUserCountLast30Days)}</b> unique users
52+
</StatText>
53+
)) ||
54+
undefined,
55+
(!!lastUpdatedMs && (
56+
<Popover
57+
content={
58+
<>
59+
{createdMs && <div>Created on {toLocalDateTimeString(createdMs)}.</div>}
60+
<div>
61+
Changed on {toLocalDateTimeString(lastUpdatedMs)}.{' '}
62+
<Tooltip title="The time at which the chart was last changed in the source platform">
63+
<HelpIcon />
64+
</Tooltip>
65+
</div>
66+
</>
67+
}
68+
>
69+
<StatText>
70+
<ClockCircleOutlined style={{ marginRight: 8, color: ANTD_GRAY[7] }} />
71+
Changed {toRelativeTimeString(lastUpdatedMs)}
72+
</StatText>
73+
</Popover>
74+
)) ||
75+
undefined,
76+
].filter((stat) => stat !== undefined);
77+
78+
return <>{statsViews.length > 0 && <StatsSummary stats={statsViews} />}</>;
79+
};

datahub-web-react/src/graphql/chart.graphql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@ query getChart($urn: String!) {
7171
dataPlatformInstance {
7272
...dataPlatformInstanceFields
7373
}
74+
statsSummary {
75+
viewCount
76+
uniqueUserCountLast30Days
77+
topUsersLast30Days {
78+
urn
79+
type
80+
username
81+
properties {
82+
displayName
83+
firstName
84+
lastName
85+
fullName
86+
}
87+
editableProperties {
88+
displayName
89+
pictureLink
90+
}
91+
}
92+
}
7493
}
7594
}
7695

datahub-web-react/src/graphql/search.graphql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ fragment searchResultFields on Entity {
385385
lastModified {
386386
time
387387
}
388+
created {
389+
time
390+
}
388391
}
389392
ownership {
390393
...ownershipFields
@@ -413,6 +416,25 @@ fragment searchResultFields on Entity {
413416
parentContainers {
414417
...parentContainersFields
415418
}
419+
statsSummary {
420+
viewCount
421+
uniqueUserCountLast30Days
422+
topUsersLast30Days {
423+
urn
424+
type
425+
username
426+
properties {
427+
displayName
428+
firstName
429+
lastName
430+
fullName
431+
}
432+
editableProperties {
433+
displayName
434+
pictureLink
435+
}
436+
}
437+
}
416438
}
417439
... on DataFlow {
418440
flowId

0 commit comments

Comments
 (0)