|
| 1 | +package codec |
| 2 | + |
| 3 | +import ( |
| 4 | + "encoding/json" |
| 5 | + "fmt" |
| 6 | + |
| 7 | + "github.com/amp-labs/connectors/common" |
| 8 | + "github.com/amp-labs/connectors/internal/datautils" |
| 9 | +) |
| 10 | + |
| 11 | +// DecoratedRecord merges a dynamic record with a structured extension, |
| 12 | +// producing a single flattened JSON object when marshaled. |
| 13 | +// |
| 14 | +// It embeds a [common.Record] containing user-defined key–value pairs |
| 15 | +// and a typed struct [T] representing schema-bound fields that must |
| 16 | +// coexist with those user-supplied values in the API payload. |
| 17 | +// |
| 18 | +// When marshaled, fields from both Record and Extension are serialized |
| 19 | +// at the same level in the resulting JSON. This allows connectors to |
| 20 | +// enrich arbitrary record data with well-defined metadata or attributes. |
| 21 | +// |
| 22 | +// Example: |
| 23 | +// |
| 24 | +// type MyPayloadForRecord = codec.DecoratedRecord[RecordExtension] |
| 25 | +// |
| 26 | +// type RecordExtension struct { |
| 27 | +// ObjectName string `json:"objectName"` |
| 28 | +// } |
| 29 | +// |
| 30 | +// record := common.Record{"name": "Bob"} |
| 31 | +// extension := RecordExtension{ObjectName: "users"} |
| 32 | +// item := codec.DecoratedRecord[RecordExtension]{Record: record, Extension: extension} |
| 33 | +// |
| 34 | +// // Output: |
| 35 | +// // {"name": "Bob", "objectName": "users"} |
| 36 | +type DecoratedRecord[T any] struct { |
| 37 | + common.Record |
| 38 | + Extension T |
| 39 | +} |
| 40 | + |
| 41 | +func (d *DecoratedRecord[T]) MarshalJSON() ([]byte, error) { |
| 42 | + // Create a copy of records. |
| 43 | + jsonProperties, err := datautils.FromMap(d.Record).DeepCopy() |
| 44 | + if err != nil { |
| 45 | + return nil, err |
| 46 | + } |
| 47 | + |
| 48 | + // Marshal the extension struct. |
| 49 | + extBytes, err := json.Marshal(d.Extension) |
| 50 | + if err != nil { |
| 51 | + return nil, fmt.Errorf("marshal extension: %w", err) |
| 52 | + } |
| 53 | + |
| 54 | + var additionalProperties map[string]any |
| 55 | + if err = json.Unmarshal(extBytes, &additionalProperties); err != nil { |
| 56 | + return nil, fmt.Errorf("unmarshal extension: %w", err) |
| 57 | + } |
| 58 | + |
| 59 | + // Enhance final JSON map with properties from extension. |
| 60 | + datautils.FromMap(jsonProperties).AddMapValues(additionalProperties) |
| 61 | + |
| 62 | + // Marshall combined map. |
| 63 | + return json.Marshal(jsonProperties) |
| 64 | +} |
0 commit comments