github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/utils/objectreader.go (about)

     1  /*
     2   * Copyright (c) 2020-present unTill Pro, Ltd.
     3   */
     4  
     5  package coreutils
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  
    11  	"github.com/voedger/voedger/pkg/appdef"
    12  	"github.com/voedger/voedger/pkg/goutils/logger"
    13  	"github.com/voedger/voedger/pkg/istructs"
    14  )
    15  
    16  // panics on an unsupported kind guessing that pair <name, kind> could be taken from IDef.Fields() callback only
    17  func ReadByKind(name appdef.FieldName, kind appdef.DataKind, rr istructs.IRowReader) interface{} {
    18  	defer func() {
    19  		if r := recover(); r != nil {
    20  			logger.Error(fmt.Sprintf("ReadByKind() failed: name %s, kind %s, source QName %s: %v", name, kind.String(), rr.AsQName(appdef.SystemField_QName), r))
    21  			panic(r)
    22  		}
    23  	}()
    24  	switch kind {
    25  	case appdef.DataKind_int32:
    26  		return rr.AsInt32(name)
    27  	case appdef.DataKind_int64:
    28  		return rr.AsInt64(name)
    29  	case appdef.DataKind_float32:
    30  		return rr.AsFloat32(name)
    31  	case appdef.DataKind_float64:
    32  		return rr.AsFloat64(name)
    33  	case appdef.DataKind_bytes:
    34  		return rr.AsBytes(name)
    35  	case appdef.DataKind_string:
    36  		return rr.AsString(name)
    37  	case appdef.DataKind_RecordID:
    38  		return rr.AsRecordID(name)
    39  	case appdef.DataKind_QName:
    40  		return rr.AsQName(name).String()
    41  	case appdef.DataKind_bool:
    42  		return rr.AsBool(name)
    43  	default:
    44  		panic("unsupported kind " + fmt.Sprint(kind) + " for field " + name)
    45  	}
    46  }
    47  
    48  type mapperOpts struct {
    49  	filter      func(name string, kind appdef.DataKind) bool
    50  	nonNilsOnly bool
    51  }
    52  
    53  type MapperOpt func(opt *mapperOpts)
    54  
    55  func Filter(filterFunc func(name string, kind appdef.DataKind) bool) MapperOpt {
    56  	return func(opts *mapperOpts) {
    57  		opts.filter = filterFunc
    58  	}
    59  }
    60  
    61  func WithNonNilsOnly() MapperOpt {
    62  	return func(opts *mapperOpts) {
    63  		opts.nonNilsOnly = true
    64  	}
    65  }
    66  
    67  func FieldsToMap(obj istructs.IRowReader, appDef appdef.IAppDef, optFuncs ...MapperOpt) (res map[string]interface{}) {
    68  	res = map[string]interface{}{}
    69  
    70  	qn := obj.AsQName(appdef.SystemField_QName)
    71  	if qn == appdef.NullQName {
    72  		return
    73  	}
    74  	t := appDef.Type(qn)
    75  
    76  	opts := &mapperOpts{}
    77  	for _, optFunc := range optFuncs {
    78  		optFunc(opts)
    79  	}
    80  
    81  	proceedField := func(fieldName appdef.FieldName, kind appdef.DataKind) {
    82  		if opts.filter != nil {
    83  			if !opts.filter(fieldName, kind) {
    84  				return
    85  			}
    86  		}
    87  		if kind == appdef.DataKind_Record {
    88  			if v, ok := obj.(istructs.IValue); ok {
    89  				res[fieldName] = FieldsToMap(v.AsRecord(fieldName), appDef, optFuncs...)
    90  			} else {
    91  				panic("DataKind_Record field met -> IValue must be provided")
    92  			}
    93  		} else {
    94  			res[fieldName] = ReadByKind(fieldName, kind, obj)
    95  		}
    96  	}
    97  
    98  	if fields, ok := t.(appdef.IFields); ok {
    99  		if opts.nonNilsOnly {
   100  			obj.FieldNames(func(fieldName appdef.FieldName) {
   101  				proceedField(fieldName, fields.Field(fieldName).DataKind())
   102  			})
   103  		} else {
   104  			for _, f := range fields.Fields() {
   105  				proceedField(f.Name(), f.DataKind())
   106  			}
   107  		}
   108  	}
   109  
   110  	return res
   111  }
   112  
   113  func ObjectToMap(obj istructs.IObject, appDef appdef.IAppDef, opts ...MapperOpt) (res map[string]interface{}) {
   114  	if obj.QName() == appdef.NullQName {
   115  		return map[string]interface{}{}
   116  	}
   117  	res = FieldsToMap(obj, appDef, opts...)
   118  	obj.Containers(func(container string) {
   119  		var childMap map[string]interface{}
   120  		cont := []map[string]interface{}{}
   121  		obj.Children(container, func(c istructs.IObject) {
   122  
   123  			childMap = ObjectToMap(c, appDef, opts...)
   124  			cont = append(cont, childMap)
   125  		})
   126  		res[container] = cont
   127  	})
   128  	return res
   129  }
   130  
   131  type cudsOpts struct {
   132  	filter     func(appdef.QName) bool
   133  	mapperOpts []MapperOpt
   134  }
   135  
   136  type CUDsOpt func(*cudsOpts)
   137  
   138  func WithFilter(filterFunc func(appdef.QName) bool) CUDsOpt {
   139  	return func(co *cudsOpts) {
   140  		co.filter = filterFunc
   141  	}
   142  }
   143  
   144  func WithMapperOpts(opts ...MapperOpt) CUDsOpt {
   145  	return func(co *cudsOpts) {
   146  		co.mapperOpts = opts
   147  	}
   148  }
   149  
   150  func CUDsToMap(event istructs.IDbEvent, appDef appdef.IAppDef, optFuncs ...CUDsOpt) []map[string]interface{} {
   151  	cuds := make([]map[string]interface{}, 0)
   152  	opts := cudsOpts{}
   153  	for _, f := range optFuncs {
   154  		f(&opts)
   155  	}
   156  	event.CUDs(func(rec istructs.ICUDRow) {
   157  		if opts.filter != nil && !opts.filter(rec.QName()) {
   158  			return
   159  		}
   160  		cudData := make(map[string]interface{})
   161  		cudData["sys.ID"] = rec.ID()
   162  		cudData["sys.QName"] = rec.QName().String()
   163  		cudData["IsNew"] = rec.IsNew()
   164  		cudData["fields"] = FieldsToMap(rec, appDef, opts.mapperOpts...)
   165  		cuds = append(cuds, cudData)
   166  	})
   167  	return cuds
   168  }
   169  
   170  func JSONMapToCUDBody(data []map[string]interface{}) string {
   171  	cuds := make([]CUD, 0, len(data))
   172  	for _, record := range data {
   173  		c := CUD{
   174  			Fields: make(map[string]interface{}),
   175  		}
   176  		for field, value := range record {
   177  			c.Fields[field] = value
   178  		}
   179  		cuds = append(cuds, c)
   180  	}
   181  	bb, err := json.Marshal(CUDs{Cuds: cuds})
   182  	if err != nil {
   183  		panic(err)
   184  	}
   185  	return string(bb)
   186  }
   187  
   188  func CheckValueByKind(val interface{}, kind appdef.DataKind) error {
   189  	ok := false
   190  	switch val.(type) {
   191  	case int32:
   192  		ok = kind == appdef.DataKind_int32
   193  	case int64:
   194  		ok = kind == appdef.DataKind_int64 || kind == appdef.DataKind_RecordID
   195  	case float32:
   196  		ok = kind == appdef.DataKind_float32
   197  	case float64:
   198  		ok = kind == appdef.DataKind_float64
   199  	case bool:
   200  		ok = kind == appdef.DataKind_bool
   201  	case string:
   202  		ok = kind == appdef.DataKind_string || kind == appdef.DataKind_QName
   203  	case []byte:
   204  		ok = kind == appdef.DataKind_bytes
   205  	}
   206  	if !ok {
   207  		return fmt.Errorf("provided value %v has type %T but %s is expected: %w", val, val, kind.String(), appdef.ErrInvalidError)
   208  	}
   209  	return nil
   210  }