1- use crate :: api :: { ResourceInfoRequest , ResourceInfoRequestResource } ;
1+ use std :: collections :: BTreeMap ;
22
3+ use crate :: api:: { ResourceInfoRequest , ResourceInfoRequestResource } ;
34
45pub fn urn_for_request ( request : & ResourceInfoRequest , env : & str ) -> String {
56 let stacklet = & request. stacklet ;
@@ -13,5 +14,74 @@ pub fn urn_for_request(request: &ResourceInfoRequest, env: &str) -> String {
1314 "urn:li:dataset:(urn:li:dataPlatform:{stacklet},{catalog}.{schema}.{table},{env})"
1415 )
1516 }
17+ // Trino catalogs and schemas are modelled as DataHub `Container`s (subtypes `Database` and
18+ // `Schema` respectively). Unlike datasets, their URN is not human-readable but a GUID derived
19+ // from the container key - see `container_urn`.
20+ ResourceInfoRequestResource :: TrinoCatalog { catalog } => {
21+ container_urn ( & BTreeMap :: from ( [
22+ ( "platform" , stacklet. as_str ( ) ) ,
23+ ( "instance" , env) ,
24+ ( "database" , catalog. as_str ( ) ) ,
25+ ] ) )
26+ }
27+ ResourceInfoRequestResource :: TrinoSchema { catalog, schema } => {
28+ container_urn ( & BTreeMap :: from ( [
29+ ( "platform" , stacklet. as_str ( ) ) ,
30+ ( "instance" , env) ,
31+ ( "database" , catalog. as_str ( ) ) ,
32+ ( "schema" , schema. as_str ( ) ) ,
33+ ] ) )
34+ }
35+ }
36+ }
37+
38+ /// Reproduces DataHub's `datahub_guid`: the container key is serialized to compact, key-sorted JSON
39+ /// and MD5-hashed. A [`BTreeMap`] yields sorted keys and `serde_json` emits no whitespace, which
40+ /// matches Python's `json.dumps(key, sort_keys=True, separators=(",", ":"))`.
41+ ///
42+ /// Note that the SQL ingestion source sets `backcompat_env_as_instance`, so the configured `env`
43+ /// (e.g. `PROD`) ends up in the `instance` field and no `env` field is present in the key. The
44+ /// `platform` is the bare platform name (e.g. `trino`), *not* the `urn:li:dataPlatform:` form.
45+ fn container_urn ( container_key : & BTreeMap < & str , & str > ) -> String {
46+ let key_json =
47+ serde_json:: to_string ( container_key) . expect ( "serializing a BTreeMap<&str, &str> cannot fail" ) ;
48+ format ! ( "urn:li:container:{:x}" , md5:: compute( key_json. as_bytes( ) ) )
49+ }
50+
51+ #[ cfg( test) ]
52+ mod tests {
53+ use super :: * ;
54+
55+ /// Verified against a live DataHub: the schema `customer_analytics` in catalog `lakehouse`.
56+ #[ test]
57+ fn trino_schema_urn_matches_data_hub ( ) {
58+ let request = ResourceInfoRequest {
59+ stacklet : "trino" . to_owned ( ) ,
60+ resource : ResourceInfoRequestResource :: TrinoSchema {
61+ catalog : "lakehouse" . to_owned ( ) ,
62+ schema : "customer_analytics" . to_owned ( ) ,
63+ } ,
64+ } ;
65+
66+ assert_eq ! (
67+ urn_for_request( & request, "PROD" ) ,
68+ "urn:li:container:c8531e5a52cacf56768d0bf77ca8787c"
69+ ) ;
70+ }
71+
72+ /// Verified against a live DataHub: the catalog `lakehouse`.
73+ #[ test]
74+ fn trino_catalog_urn_matches_data_hub ( ) {
75+ let request = ResourceInfoRequest {
76+ stacklet : "trino" . to_owned ( ) ,
77+ resource : ResourceInfoRequestResource :: TrinoCatalog {
78+ catalog : "lakehouse" . to_owned ( ) ,
79+ } ,
80+ } ;
81+
82+ assert_eq ! (
83+ urn_for_request( & request, "PROD" ) ,
84+ "urn:li:container:39967cd09b38e2d4736d1eb604cd5247"
85+ ) ;
1686 }
1787}
0 commit comments