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 }