Files
hatchet/internal/datautils/map.go
Mohammed Nafees 34074affd8 Add contextual data for trigger via events (#2092)
* add contextual data for trigger via events

* fix corrId

* string needed
2025-08-06 16:52:06 -04:00

163 lines
3.5 KiB
Go

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
}