Skip to content

Commit ba28982

Browse files
committed
feat(ingestion/grafana) add option to pass grafana user email as dashboard owner
Also remove the unique id of the dashboard as a owner.
1 parent 0a14181 commit ba28982

File tree

6 files changed

+32
-45
lines changed

6 files changed

+32
-45
lines changed

metadata-ingestion/docs/sources/grafana/grafana_pre.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
| Grafana Dashboard | [Container](../../metamodel/entities/container.md) | Subtype `Dashboard` |
88
| Grafana Panel/Visualization | [Chart](../../metamodel/entities/chart.md) | Various types mapped based on panel type (e.g., graph → LINE, pie → PIE) |
99
| Grafana Data Source | [Dataset](../../metamodel/entities/dataset.md) | Created for each panel's data source |
10-
| Dashboard Owner | [Corp User](../../metamodel/entities/corpuser.md) | Derived from dashboard UID and creator |
10+
| Dashboard Owner | [Corp User](../../metamodel/entities/corpuser.md) | Derived from dashboard creator |
1111
| Dashboard Tags | [Tag](../../metamodel/entities/tag.md) | Supports both simple tags and key:value tags |
1212

1313
### Compatibility

metadata-ingestion/src/datahub/ingestion/source/grafana/entity_mcp_builder.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ def build_dashboard_mcps(
167167
chart_urns: List[str],
168168
base_url: str,
169169
ingest_owners: bool,
170+
strip_user_ids_from_email: bool,
170171
ingest_tags: bool,
171172
) -> Tuple[str, List[MetadataChangeProposalWrapper]]:
172173
"""Build dashboard metadata change proposals"""
@@ -206,7 +207,7 @@ def build_dashboard_mcps(
206207

207208
# Ownership aspect
208209
if dashboard.uid and ingest_owners:
209-
owner = _build_ownership(dashboard)
210+
owner = _build_ownership(dashboard, strip_user_ids_from_email)
210211
if owner:
211212
mcps.append(
212213
MetadataChangeProposalWrapper(
@@ -378,24 +379,22 @@ def _build_dashboard_properties(dashboard: Dashboard) -> Dict[str, str]:
378379
return props
379380

380381

381-
def _build_ownership(dashboard: Dashboard) -> Optional[OwnershipClass]:
382+
def _build_ownership(
383+
dashboard: Dashboard, strip_user_ids_from_email: bool
384+
) -> Optional[OwnershipClass]:
382385
"""Build ownership information"""
383386
owners = []
384387

385-
if dashboard.uid:
386-
owners.append(
387-
OwnerClass(
388-
owner=make_user_urn(dashboard.uid),
389-
type=OwnershipTypeClass.TECHNICAL_OWNER,
390-
)
391-
)
392-
393388
if dashboard.created_by:
394-
owner_id = dashboard.created_by.split("@")[0]
389+
if strip_user_ids_from_email:
390+
owner_id = dashboard.created_by.split("@")[0]
391+
else:
392+
owner_id = dashboard.created_by
393+
395394
owners.append(
396395
OwnerClass(
397396
owner=make_user_urn(owner_id),
398-
type=OwnershipTypeClass.DATAOWNER,
397+
type=OwnershipTypeClass.TECHNICAL_OWNER,
399398
)
400399
)
401400

metadata-ingestion/src/datahub/ingestion/source/grafana/grafana_config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ class GrafanaSourceConfig(
8383
ingest_owners: bool = Field(
8484
default=True, description="Whether to ingest dashboard ownership information"
8585
)
86+
strip_user_ids_from_email: bool = Field(
87+
True,
88+
description="When enabled, converts Grafana user emails of the form [email protected] to urn:li:corpuser:name "
89+
"when assigning ownership",
90+
)
8691
skip_text_panels: bool = Field(
8792
default=False,
8893
description="Whether to skip text panels during ingestion. "

metadata-ingestion/src/datahub/ingestion/source/grafana/grafana_source.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,6 @@ class GrafanaSource(StatefulIngestionSourceBase):
110110
- Dashboard and chart tags
111111
- Ownership information derived from:
112112
- Dashboard creators
113-
- Technical owners based on dashboard UIDs
114-
- Custom ownership assignments
115113
116114
The source supports the following capabilities:
117115
- Platform instance support for multi-Grafana deployments
@@ -392,6 +390,7 @@ def _process_dashboard(self, dashboard: Dashboard) -> Iterable[MetadataWorkUnit]
392390
chart_urns=chart_urns,
393391
base_url=self.config.url,
394392
ingest_owners=self.config.ingest_owners,
393+
strip_user_ids_from_email=self.config.strip_user_ids_from_email,
395394
ingest_tags=self.config.ingest_tags,
396395
)
397396

metadata-ingestion/tests/integration/grafana/grafana_mcps_golden.json

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2218,32 +2218,6 @@
22182218
"lastRunId": "no-run-id-provided"
22192219
}
22202220
},
2221-
{
2222-
"entityType": "dashboard",
2223-
"entityUrn": "urn:li:dashboard:(grafana,local-grafana.default)",
2224-
"changeType": "UPSERT",
2225-
"aspectName": "ownership",
2226-
"aspect": {
2227-
"json": {
2228-
"owners": [
2229-
{
2230-
"owner": "urn:li:corpuser:default",
2231-
"type": "TECHNICAL_OWNER"
2232-
}
2233-
],
2234-
"ownerTypes": {},
2235-
"lastModified": {
2236-
"time": 0,
2237-
"actor": "urn:li:corpuser:unknown"
2238-
}
2239-
}
2240-
},
2241-
"systemMetadata": {
2242-
"lastObserved": 1720785600000,
2243-
"runId": "grafana-test",
2244-
"lastRunId": "no-run-id-provided"
2245-
}
2246-
},
22472221
{
22482222
"entityType": "dashboard",
22492223
"entityUrn": "urn:li:dashboard:(grafana,local-grafana.default)",

metadata-ingestion/tests/unit/grafana/test_grafana_entity_mcp_builder.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,23 @@ def test_build_dashboard_properties(mock_dashboard):
7272

7373

7474
def test_build_ownership(mock_dashboard):
75-
ownership = _build_ownership(mock_dashboard)
75+
ownership = _build_ownership(mock_dashboard, True)
7676
assert isinstance(ownership, OwnershipClass)
77-
assert len(ownership.owners) == 2
77+
assert len(ownership.owners) == 1
7878
assert {owner.owner for owner in ownership.owners} == {
79-
"urn:li:corpuser:dash1",
8079
"urn:li:corpuser:test",
8180
}
8281

8382

83+
def test_build_ownership_full_email(mock_dashboard):
84+
ownership = _build_ownership(mock_dashboard, False)
85+
assert isinstance(ownership, OwnershipClass)
86+
assert len(ownership.owners) == 1
87+
assert {owner.owner for owner in ownership.owners} == {
88+
"urn:li:corpuser:[email protected]",
89+
}
90+
91+
8492
def test_build_chart_mcps(mock_panel, mock_dashboard):
8593
dataset_urn, chart_urn, chart_mcps = build_chart_mcps(
8694
panel=mock_panel,
@@ -147,6 +155,7 @@ def test_build_dashboard_mcps(mock_dashboard):
147155
chart_urns=chart_urns,
148156
base_url="http://grafana.test",
149157
ingest_owners=True,
158+
strip_user_ids_from_email=True,
150159
ingest_tags=True,
151160
)
152161

@@ -193,7 +202,7 @@ def test_build_dashboard_mcps(mock_dashboard):
193202
)
194203
assert ownership_mcp is not None
195204
assert isinstance(ownership_mcp.aspect, OwnershipClass) # type safety
196-
assert len(ownership_mcp.aspect.owners) == 2
205+
assert len(ownership_mcp.aspect.owners) == 1
197206

198207

199208
def test_build_chart_mcps_no_tags(mock_panel, mock_dashboard):
@@ -222,6 +231,7 @@ def test_build_dashboard_mcps_no_owners(mock_dashboard):
222231
chart_urns=[],
223232
base_url="http://grafana.test",
224233
ingest_owners=True,
234+
strip_user_ids_from_email=True,
225235
ingest_tags=True,
226236
)
227237

0 commit comments

Comments
 (0)