metadata-integration/java/docs/sdk-v2/dashboard-entity.md
The Dashboard entity represents collections of visualizations and reports in BI tools (e.g., Looker, Tableau, PowerBI). This guide covers comprehensive dashboard operations in SDK V2.
Only tool and id are required:
Dashboard dashboard = Dashboard.builder()
.tool("looker")
.id("my_sales_dashboard")
.build();
Add title and description at construction:
Dashboard dashboard = Dashboard.builder()
.tool("tableau")
.id("executive_dashboard")
.title("Executive KPI Dashboard")
.description("Real-time executive dashboard showing key business metrics")
.build();
Include custom properties in builder:
Map<String, String> props = new HashMap<>();
props.put("team", "business-intelligence");
props.put("refresh_schedule", "hourly");
Dashboard dashboard = Dashboard.builder()
.tool("powerbi")
.id("sales_dashboard")
.title("Sales Performance")
.customProperties(props)
.build();
Dashboard URNs follow the pattern:
urn:li:dashboard:({tool},{id})
Automatic URN creation:
Dashboard dashboard = Dashboard.builder()
.tool("looker")
.id("regional_sales")
.build();
DashboardUrn urn = dashboard.getDashboardUrn();
// urn:li:dashboard:(looker,regional_sales)
Common tool identifiers:
looker - Lookertableau - Tableaupowerbi - Power BIsuperset - Apache Supersetmetabase - Metabaseredash - Redashmode - Mode Analyticsquicksight - Amazon QuickSightthoughtspot - ThoughtSpot// Simple tag name (auto-prefixed)
dashboard.addTag("executive");
// Creates: urn:li:tag:executive
// Full tag URN
dashboard.addTag("urn:li:tag:production");
dashboard.removeTag("executive");
dashboard.removeTag("urn:li:tag:production");
dashboard.addTag("executive")
.addTag("real-time")
.addTag("kpi");
import com.linkedin.common.OwnershipType;
// Business owner
dashboard.addOwner(
"urn:li:corpuser:john_doe",
OwnershipType.BUSINESS_OWNER
);
// Technical owner
dashboard.addOwner(
"urn:li:corpuser:bi_team",
OwnershipType.TECHNICAL_OWNER
);
// Data steward
dashboard.addOwner(
"urn:li:corpuser:governance",
OwnershipType.DATA_STEWARD
);
dashboard.removeOwner("urn:li:corpuser:john_doe");
Available ownership types:
BUSINESS_OWNER - Business stakeholderTECHNICAL_OWNER - Maintains the technical implementationDATA_STEWARD - Manages data quality and complianceDATAOWNER - Generic data ownerDEVELOPER - Software developerPRODUCER - Dashboard producer/creatorCONSUMER - Dashboard consumerSTAKEHOLDER - Other stakeholderdashboard.addTerm("urn:li:glossaryTerm:ExecutiveMetrics");
dashboard.addTerm("urn:li:glossaryTerm:BusinessIntelligence");
dashboard.removeTerm("urn:li:glossaryTerm:ExecutiveMetrics");
dashboard.addTerm("urn:li:glossaryTerm:KeyPerformanceIndicator")
.addTerm("urn:li:glossaryTerm:SalesMetrics")
.addTerm("urn:li:glossaryTerm:RealTimeData");
dashboard.setDomain("urn:li:domain:Sales");
// Remove a specific domain
dashboard.removeDomain("urn:li:domain:Sales");
// Or clear all domains
dashboard.clearDomains();
dashboard.addCustomProperty("team", "sales-operations");
dashboard.addCustomProperty("refresh_schedule", "hourly");
dashboard.addCustomProperty("data_source", "snowflake");
Replace all custom properties:
Map<String, String> properties = new HashMap<>();
properties.put("team", "business-intelligence");
properties.put("refresh_schedule", "real-time");
properties.put("access_level", "executive");
dashboard.setCustomProperties(properties);
dashboard.removeCustomProperty("refresh_schedule");
String title = dashboard.getTitle();
String description = dashboard.getDescription();
Dashboard lineage represents the data sources (datasets) that feed into the dashboard. This creates upstream lineage relationships from the dashboard to its source datasets.
Add datasets one at a time:
// Using DatasetUrn
DatasetUrn dataset = DatasetUrn.createFromString(
"urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.orders,PROD)"
);
dashboard.addInputDataset(dataset);
// Using string URN
dashboard.addInputDataset(
"urn:li:dataset:(urn:li:dataPlatform:bigquery,marketing.campaigns,PROD)"
);
Replace all input datasets at once:
List<DatasetUrn> datasets = Arrays.asList(
DatasetUrn.createFromString(
"urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.orders,PROD)"
),
DatasetUrn.createFromString(
"urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.customers,PROD)"
)
);
dashboard.setInputDatasets(datasets);
// Using DatasetUrn
DatasetUrn dataset = DatasetUrn.createFromString(
"urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.orders,PROD)"
);
dashboard.removeInputDataset(dataset);
// Using string URN
dashboard.removeInputDataset(
"urn:li:dataset:(urn:li:dataPlatform:bigquery,marketing.campaigns,PROD)"
);
Retrieve all input datasets:
List<DatasetUrn> inputDatasets = dashboard.getInputDatasets();
for (DatasetUrn dataset : inputDatasets) {
System.out.println("Dataset: " + dataset);
}
dashboard.addInputDataset("urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.orders,PROD)")
.addInputDataset("urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.customers,PROD)")
.addInputDataset("urn:li:dataset:(urn:li:dataPlatform:salesforce,Account,PROD)");
Chart relationships represent the visualizations embedded in a dashboard. This creates "Contains" relationships between the dashboard and its charts.
Add charts one at a time:
// Using ChartUrn
ChartUrn chart = new ChartUrn("tableau", "revenue_chart");
dashboard.addChart(chart);
// Using string URN
dashboard.addChart("urn:li:chart:(looker,sales_performance_chart)");
Replace all charts at once:
List<ChartUrn> charts = Arrays.asList(
new ChartUrn("tableau", "revenue_chart"),
new ChartUrn("tableau", "customer_satisfaction_chart"),
new ChartUrn("tableau", "regional_breakdown_chart")
);
dashboard.setCharts(charts);
// Using ChartUrn
ChartUrn chart = new ChartUrn("tableau", "revenue_chart");
dashboard.removeChart(chart);
// Using string URN
dashboard.removeChart("urn:li:chart:(looker,sales_performance_chart)");
Retrieve all charts:
List<ChartUrn> charts = dashboard.getCharts();
for (ChartUrn chart : charts) {
System.out.println("Chart: " + chart);
}
dashboard.addChart(new ChartUrn("looker", "revenue_chart"))
.addChart(new ChartUrn("looker", "customer_chart"))
.addChart(new ChartUrn("looker", "product_chart"));
Set a direct link to the dashboard in its native BI tool:
// Set dashboard URL
dashboard.setDashboardUrl("https://tableau.company.com/views/sales-dashboard");
// Get dashboard URL
String url = dashboard.getDashboardUrl();
Track when the dashboard data was last updated:
// Set last refreshed timestamp (milliseconds since epoch)
long currentTime = System.currentTimeMillis();
dashboard.setLastRefreshed(currentTime);
// Get last refreshed timestamp
Long lastRefreshed = dashboard.getLastRefreshed();
if (lastRefreshed != null) {
System.out.println("Last refreshed at: " + new Date(lastRefreshed));
}
dashboard.setDashboardUrl("https://looker.company.com/dashboards/executive")
.setLastRefreshed(System.currentTimeMillis());
import datahub.client.v2.DataHubClientV2;
import datahub.client.v2.entity.Dashboard;
import com.linkedin.common.OwnershipType;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
public class DashboardExample {
public static void main(String[] args) {
// Create client
DataHubClientV2 client = DataHubClientV2.builder()
.server("http://localhost:8080")
.build();
try {
// Build dashboard with all metadata
Dashboard dashboard = Dashboard.builder()
.tool("looker")
.id("sales_performance_dashboard")
.title("Sales Performance Dashboard")
.description("Executive dashboard showing key sales metrics and regional performance")
.build();
// Add tags
dashboard.addTag("executive")
.addTag("sales")
.addTag("production");
// Add owners
dashboard.addOwner("urn:li:corpuser:sales_team", OwnershipType.BUSINESS_OWNER)
.addOwner("urn:li:corpuser:bi_team", OwnershipType.TECHNICAL_OWNER);
// Add glossary terms
dashboard.addTerm("urn:li:glossaryTerm:SalesMetrics")
.addTerm("urn:li:glossaryTerm:ExecutiveDashboard");
// Set domain
dashboard.setDomain("urn:li:domain:Sales");
// Add custom properties
dashboard.addCustomProperty("team", "sales-operations")
.addCustomProperty("refresh_schedule", "hourly")
.addCustomProperty("data_source", "snowflake");
// Upsert to DataHub
client.entities().upsert(dashboard);
System.out.println("Successfully created dashboard: " + dashboard.getUrn());
} catch (IOException | ExecutionException | InterruptedException e) {
e.printStackTrace();
} finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// Load existing dashboard
DashboardUrn urn = new DashboardUrn("looker", "my_dashboard");
Dashboard dashboard = client.entities().get(urn);
// Add new metadata (creates patches)
dashboard.addTag("new-tag")
.addOwner("urn:li:corpuser:new_owner", OwnershipType.TECHNICAL_OWNER);
// Apply patches
client.entities().update(dashboard);
// Just add what you need
dashboard.addTag("real-time");
client.entities().update(dashboard);
// Later, add more
dashboard.addCustomProperty("updated_at", String.valueOf(System.currentTimeMillis()));
client.entities().update(dashboard);
| Method | Required | Description |
|---|---|---|
tool(String) | ✅ Yes | BI tool identifier (e.g., "looker", "tableau") |
id(String) | ✅ Yes | Dashboard identifier within the tool |
title(String) | No | Dashboard title |
description(String) | No | Dashboard description |
customProperties(Map) | No | Map of custom key-value properties |
Dashboard uses patch-based updates for metadata operations. All methods like addTag(), addOwner(), etc. create patches that are accumulated until upsert() or update() is called.
Benefits:
Example:
Dashboard dashboard = Dashboard.builder()
.tool("tableau")
.id("sales_dashboard")
.build();
// These create patches (no API calls yet)
dashboard.addTag("production");
dashboard.addOwner("urn:li:corpuser:owner", OwnershipType.BUSINESS_OWNER);
dashboard.setDomain("urn:li:domain:Sales");
// Single API call emits all patches
client.entities().upsert(dashboard);
List<String> dashboardIds = Arrays.asList("dashboard1", "dashboard2", "dashboard3");
for (String dashboardId : dashboardIds) {
Dashboard dashboard = Dashboard.builder()
.tool("looker")
.id(dashboardId)
.title("Dashboard " + dashboardId)
.build();
dashboard.addTag("auto-generated")
.addCustomProperty("created_by", "sync_job");
client.entities().upsert(dashboard);
}
Dashboard dashboard = Dashboard.builder()
.tool("tableau")
.id("executive_dashboard")
.build();
List<String> tags = Arrays.asList("executive", "kpi", "real-time", "production");
tags.forEach(dashboard::addTag);
client.entities().upsert(dashboard); // Emits all tags in one call
if (isExecutiveDashboard(dashboard)) {
dashboard.addTag("executive")
.addTerm("urn:li:glossaryTerm:ExecutiveMetrics");
}
if (requiresGovernance(dashboard)) {
dashboard.addOwner("urn:li:corpuser:governance_team", OwnershipType.DATA_STEWARD);
}
// Create dashboard with rich metadata
Dashboard dashboard = Dashboard.builder()
.tool("looker")
.id("customer_360_dashboard")
.title("Customer 360 Dashboard")
.description("Comprehensive customer analytics dashboard")
.build();
// Add business context
dashboard.addTerm("urn:li:glossaryTerm:CustomerAnalytics")
.addTerm("urn:li:glossaryTerm:BusinessIntelligence")
.setDomain("urn:li:domain:Customer");
// Add input datasets for lineage
dashboard.addInputDataset("urn:li:dataset:(urn:li:dataPlatform:snowflake,customer.profile,PROD)")
.addInputDataset("urn:li:dataset:(urn:li:dataPlatform:salesforce,Account,PROD)")
.addInputDataset("urn:li:dataset:(urn:li:dataPlatform:zendesk,Tickets,PROD)");
// Add embedded charts
dashboard.addChart(new ChartUrn("looker", "customer_segmentation_chart"))
.addChart(new ChartUrn("looker", "lifetime_value_chart"))
.addChart(new ChartUrn("looker", "support_tickets_chart"));
// Add operational metadata
dashboard.addCustomProperty("data_sources", "snowflake,salesforce,zendesk")
.addCustomProperty("refresh_schedule", "every_15_minutes")
.addCustomProperty("sla_tier", "tier1")
.addCustomProperty("business_criticality", "high");
// Set dashboard properties
dashboard.setDashboardUrl("https://looker.company.com/dashboards/customer-360")
.setLastRefreshed(System.currentTimeMillis());
// Add ownership and governance
dashboard.addOwner("urn:li:corpuser:product_team", OwnershipType.BUSINESS_OWNER)
.addOwner("urn:li:corpuser:bi_team", OwnershipType.TECHNICAL_OWNER)
.addTag("production")
.addTag("customer-facing");
client.entities().upsert(dashboard);
Dashboard and Chart are similar but serve different purposes:
| Feature | Dashboard | Chart |
|---|---|---|
| Purpose | Collection of visualizations | Single visualization |
| URN Pattern | (tool,id) | (tool,id) |
| Patch Operations | ✅ Full support | ✅ Full support |
| Common Use Cases | Executive dashboards, reports | Individual graphs, charts |
{{ inline /metadata-integration/java/examples/src/main/java/io/datahubproject/examples/v2/DashboardCreateExample.java show_path_as_comment }}
{{ inline /metadata-integration/java/examples/src/main/java/io/datahubproject/examples/v2/DashboardFullExample.java show_path_as_comment }}
{{ inline /metadata-integration/java/examples/src/main/java/io/datahubproject/examples/v2/DashboardLineageExample.java show_path_as_comment }}