@@ -8,6 +8,11 @@ import (
88 "time"
99)
1010
11+ // ErrUnsupportedEntityType is returned when an entity type does not support description updates.
12+ // Entity types intentionally excluded (no editable description aspect in DataHub):
13+ // tag, corpuser, corpGroup, mlModel, mlModelGroup, notebook.
14+ var ErrUnsupportedEntityType = errors .New ("unsupported entity type for description update" )
15+
1116// entityTypeFromURN derives the DataHub entity type string from a parsed URN.
1217// Maps URN entity types to the REST API entity type names.
1318func entityTypeFromURN (urn string ) (string , error ) {
@@ -18,6 +23,39 @@ func entityTypeFromURN(urn string) (string, error) {
1823 return parsed .EntityType , nil
1924}
2025
26+ // descriptionAspectInfo holds the aspect name and field name for updating
27+ // an entity's description. The field is "description" for most entity types,
28+ // but "definition" for glossary entities.
29+ type descriptionAspectInfo struct {
30+ AspectName string
31+ FieldName string
32+ }
33+
34+ // descriptionAspectMap maps DataHub entity types to their description aspect.
35+ // glossaryTerm and glossaryNode use "definition" instead of "description".
36+ // dataProduct and domain use non-editable property aspects.
37+ var descriptionAspectMap = map [string ]descriptionAspectInfo {
38+ "dataset" : {AspectName : "editableDatasetProperties" , FieldName : "description" },
39+ "dashboard" : {AspectName : "editableDashboardProperties" , FieldName : "description" },
40+ "chart" : {AspectName : "editableChartProperties" , FieldName : "description" },
41+ "dataFlow" : {AspectName : "editableDataFlowProperties" , FieldName : "description" },
42+ "dataJob" : {AspectName : "editableDataJobProperties" , FieldName : "description" },
43+ "container" : {AspectName : "editableContainerProperties" , FieldName : "description" },
44+ "dataProduct" : {AspectName : "dataProductProperties" , FieldName : "description" },
45+ "domain" : {AspectName : "domainProperties" , FieldName : "description" },
46+ "glossaryTerm" : {AspectName : "glossaryTermInfo" , FieldName : "definition" },
47+ "glossaryNode" : {AspectName : "glossaryNodeInfo" , FieldName : "definition" },
48+ }
49+
50+ // lookupDescriptionAspect returns the aspect info for updating the description of the given entity type.
51+ func lookupDescriptionAspect (entityType string ) (descriptionAspectInfo , error ) {
52+ info , ok := descriptionAspectMap [entityType ]
53+ if ! ok {
54+ return descriptionAspectInfo {}, fmt .Errorf ("%w: %s" , ErrUnsupportedEntityType , entityType )
55+ }
56+ return info , nil
57+ }
58+
2159// editableSchemaAspect is the REST API representation of editableSchemaMetadata.
2260type editableSchemaAspect struct {
2361 EditableSchemaFieldInfo []editableFieldInfo `json:"editableSchemaFieldInfo"`
@@ -32,49 +70,77 @@ type editableFieldInfo struct {
3270 GlossaryTerms json.RawMessage `json:"glossaryTerms,omitempty"`
3371}
3472
35- // editablePropertiesAspect represents the editableDatasetProperties aspect.
36- type editablePropertiesAspect struct {
37- Description string `json:"description"`
38- Created * auditStampRaw `json:"created,omitempty"`
39- LastModified * auditStampRaw `json:"lastModified,omitempty"`
73+ // descriptionAspect represents a generic properties aspect for description updates.
74+ // Uses a map to preserve all existing fields during read-modify-write regardless of
75+ // which aspect is being updated — different aspects have different schemas.
76+ type descriptionAspect struct {
77+ fields map [string ]json.RawMessage
78+ }
79+
80+ // MarshalJSON serializes the aspect as a flat JSON object.
81+ func (a * descriptionAspect ) MarshalJSON () ([]byte , error ) {
82+ return json .Marshal (a .fields )
83+ }
84+
85+ // UnmarshalJSON deserializes a flat JSON object into the aspect's field map.
86+ func (a * descriptionAspect ) UnmarshalJSON (data []byte ) error {
87+ return json .Unmarshal (data , & a .fields )
88+ }
89+
90+ // setDescription sets the description value in the aspect under the given field name.
91+ func (a * descriptionAspect ) setDescription (fieldName , value string ) error {
92+ encoded , err := json .Marshal (value )
93+ if err != nil {
94+ return fmt .Errorf ("encoding description: %w" , err )
95+ }
96+ a .fields [fieldName ] = encoded
97+ return nil
4098}
4199
42100// UpdateDescription sets the editable description for any entity using read-modify-write.
101+ // Resolves the correct aspect name and field name based on the entity type in the URN.
43102func (c * Client ) UpdateDescription (ctx context.Context , urn , description string ) error {
44103 entityType , err := entityTypeFromURN (urn )
45104 if err != nil {
46105 return fmt .Errorf ("UpdateDescription: %w" , err )
47106 }
48107
49- props , err := c . readEditableProperties ( ctx , urn )
108+ aspectInfo , err := lookupDescriptionAspect ( entityType )
50109 if err != nil {
51110 return fmt .Errorf ("UpdateDescription: %w" , err )
52111 }
53112
54- props .Description = description
113+ props , err := c .readEditableProperties (ctx , urn , aspectInfo .AspectName )
114+ if err != nil {
115+ return fmt .Errorf ("UpdateDescription: %w" , err )
116+ }
117+
118+ if err := props .setDescription (aspectInfo .FieldName , description ); err != nil {
119+ return fmt .Errorf ("UpdateDescription: %w" , err )
120+ }
55121
56122 return c .postIngestProposal (ctx , ingestProposal {
57123 EntityType : entityType ,
58124 EntityURN : urn ,
59- AspectName : "editableDatasetProperties" ,
125+ AspectName : aspectInfo . AspectName ,
60126 Aspect : props ,
61127 })
62128}
63129
64- // readEditableProperties reads the current editableDatasetProperties aspect.
130+ // readEditableProperties reads the current properties aspect for an entity .
65131// Returns an empty aspect if none exists (not an error).
66- func (c * Client ) readEditableProperties (ctx context.Context , urn string ) (* editablePropertiesAspect , error ) {
67- raw , err := c .getAspect (ctx , urn , "editableDatasetProperties" )
132+ func (c * Client ) readEditableProperties (ctx context.Context , urn , aspectName string ) (* descriptionAspect , error ) {
133+ raw , err := c .getAspect (ctx , urn , aspectName )
68134 if err != nil {
69135 if errors .Is (err , ErrNotFound ) {
70- return & editablePropertiesAspect { }, nil
136+ return & descriptionAspect { fields : map [ string ]json. RawMessage {} }, nil
71137 }
72- return nil , fmt .Errorf ("reading editableDatasetProperties : %w" , err )
138+ return nil , fmt .Errorf ("reading %s : %w" , aspectName , err )
73139 }
74140
75- var props editablePropertiesAspect
141+ var props descriptionAspect
76142 if err := json .Unmarshal (raw , & props ); err != nil {
77- return nil , fmt .Errorf ("parsing editableDatasetProperties : %w" , err )
143+ return nil , fmt .Errorf ("parsing %s : %w" , aspectName , err )
78144 }
79145 return & props , nil
80146}
0 commit comments