package datautils import ( "encoding/json" "fmt" "github.com/mitchellh/mapstructure" "github.com/rs/zerolog" "github.com/hatchet-dev/hatchet/pkg/constants" "github.com/hatchet-dev/hatchet/pkg/errors" "github.com/hatchet-dev/hatchet/pkg/logger" "github.com/hatchet-dev/hatchet/pkg/validator" ) func ToJSONMap(data interface{}) (map[string]interface{}, error) { // Marshal and unmarshal to/from JSON to get a map[string]interface{}. There are probably better // or more efficient ways to do this, but this is the easiest way for now. jsonBytes, err := json.Marshal(data) if err != nil { return nil, err } return JSONBytesToMap(jsonBytes) } func JSONBytesToMap(jsonBytes []byte) (map[string]interface{}, error) { dataMap := map[string]interface{}{} err := json.Unmarshal(jsonBytes, &dataMap) if err != nil { return nil, err } if dataMap == nil { return map[string]interface{}{}, nil } return dataMap, nil } type DataDecoderValidator interface { DecodeAndValidate(input, target interface{}) error } type DefaultDataDecoderValidator struct { logger *zerolog.Logger alerter errors.Alerter validator validator.Validator tagName string } type DataDecoderValidatorOpt func(*DataDecoderValidatorOpts) type DataDecoderValidatorOpts struct { logger *zerolog.Logger alerter errors.Alerter validator validator.Validator tagName string } func defaultDataDecoderValidatorOpts() *DataDecoderValidatorOpts { logger := logger.NewDefaultLogger("data-decoder-validator") return &DataDecoderValidatorOpts{ logger: &logger, alerter: nil, validator: validator.NewDefaultValidator(), tagName: "json", } } func WithValidator(v validator.Validator) DataDecoderValidatorOpt { return func(opts *DataDecoderValidatorOpts) { opts.validator = v } } func WithLogger(l *zerolog.Logger) DataDecoderValidatorOpt { return func(opts *DataDecoderValidatorOpts) { opts.logger = l } } func WithAlerter(a errors.Alerter) DataDecoderValidatorOpt { return func(opts *DataDecoderValidatorOpts) { opts.alerter = a } } func WithTagName(t string) DataDecoderValidatorOpt { return func(opts *DataDecoderValidatorOpts) { opts.tagName = t } } func NewDataDecoderValidator( f ...DataDecoderValidatorOpt, ) DataDecoderValidator { opts := defaultDataDecoderValidatorOpts() for _, opt := range f { opt(opts) } return &DefaultDataDecoderValidator{opts.logger, opts.alerter, opts.validator, opts.tagName} } func (j *DefaultDataDecoderValidator) DecodeAndValidate(input, target interface{}) error { if input == nil { return nil } if target == nil { return fmt.Errorf("target cannot be nil") } var requestErr error config := &mapstructure.DecoderConfig{ Result: target, TagName: j.tagName, } decoder, err := mapstructure.NewDecoder(config) if err != nil { return err } if err := decoder.Decode(input); err != nil { return err } // validate the request object if requestErr = j.validator.Validate(target); requestErr != nil { return requestErr } return nil } // ExtractCorrelationId extracts correlationId from additionalMetadata if it exists func ExtractCorrelationId(additionalMetadata string) *string { if additionalMetadata == "" { return nil } var metadata map[string]any if err := json.Unmarshal([]byte(additionalMetadata), &metadata); err != nil { return nil } if corrId, exists := metadata[string(constants.CorrelationIdKey)]; exists { if corrIdStr, ok := corrId.(string); ok { return &corrIdStr } } return nil }