github.com/TIBCOSoftware/flogo-lib@v0.5.9/core/mapper/exprmapper/arraymapping.go (about) 1 package exprmapper 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "github.com/TIBCOSoftware/flogo-lib/core/mapper/assign" 7 "github.com/TIBCOSoftware/flogo-lib/core/mapper/exprmapper/json/field" 8 "runtime/debug" 9 "strings" 10 11 "github.com/TIBCOSoftware/flogo-lib/core/data" 12 "github.com/TIBCOSoftware/flogo-lib/core/mapper/exprmapper/expression" 13 flogojson "github.com/TIBCOSoftware/flogo-lib/core/mapper/exprmapper/json" 14 "github.com/TIBCOSoftware/flogo-lib/core/mapper/exprmapper/ref" 15 "github.com/TIBCOSoftware/flogo-lib/logger" 16 ) 17 18 var arraylog = logger.GetLogger("array-mapping") 19 20 const ( 21 PRIMITIVE = "primitive" 22 FOREACH = "foreach" 23 NEWARRAY = "NEWARRAY" 24 ) 25 26 type ArrayMapping struct { 27 From interface{} `json:"from"` 28 To string `json:"to"` 29 Type string `json:"type"` 30 Fields []*ArrayMapping `json:"fields,omitempty"` 31 } 32 33 func (a *ArrayMapping) Validate() error { 34 //Validate root from/to field 35 if a.From == nil { 36 return fmt.Errorf("The array mapping validation failed for the mapping [%s]. Ensure valid array is mapped in the mapper. ", a.To) 37 } 38 39 if a.To == "" || len(a.To) <= 0 { 40 return fmt.Errorf("The array mapping validation failed for the mapping [%s]. Ensure valid array is mapped in the mapper. ", a.From) 41 } 42 43 if a.Type == FOREACH { 44 //Validate root from/to field 45 if a.From == NEWARRAY { 46 //Make sure no array ref fields exist 47 for _, field := range a.Fields { 48 if field.Type == FOREACH { 49 return field.Validate() 50 } 51 stringVal, ok := field.From.(string) 52 if ok && ref.IsArrayMapping(stringVal) { 53 return fmt.Errorf("The array mapping validation failed, due to invalid new array mapping [%s]", stringVal) 54 } 55 56 } 57 } else { 58 for _, field := range a.Fields { 59 if field.Type == FOREACH { 60 return field.Validate() 61 } 62 } 63 } 64 65 } 66 67 return nil 68 } 69 70 func (a *ArrayMapping) DoArrayMapping(inputScope, outputScope data.Scope, resolver data.Resolver) (err error) { 71 defer func() { 72 if r := recover(); r != nil { 73 err = fmt.Errorf("%+v", r) 74 logger.Debugf("StackTrace: %s", debug.Stack()) 75 } 76 }() 77 78 //First level must be foreach 79 switch a.Type { 80 case FOREACH: 81 //First Level 82 var fromValue interface{} 83 var err error 84 85 stringVal, _ := a.From.(string) 86 if strings.EqualFold(stringVal, NEWARRAY) { 87 log.Debugf("Init a new array for field", a.To) 88 fromValue = make([]interface{}, 1) 89 } else { 90 fromValue, err = GetExpresssionValue(stringVal, inputScope, resolver) 91 } 92 93 //Check if fields is empty for primitive array mapping 94 if a.Fields == nil || len(a.Fields) <= 0 { 95 //Set value directlly to MapTo field 96 return assign.SetValueToOutputScope(a.To, outputScope, fromValue) 97 } 98 99 //Loop array 100 fromArrayvalues, ok := fromValue.([]interface{}) 101 if !ok { 102 //Try to convert to array. 103 fromArrayvalues, err = data.CoerceToArray(fromValue) 104 if err != nil { 105 return fmt.Errorf("Failed to get array value from [%s], due to error- [%s] value not an array", a.From, a.From) 106 } 107 } 108 109 toRef := ref.NewMappingRef(a.To) 110 toMapField, err := field.ParseMappingField(toRef.GetRef()) 111 if err != nil { 112 return err 113 } 114 toValue, err := ref.GetValueFromOutputScope(toMapField, outputScope) 115 if err != nil { 116 return err 117 } 118 toValue = toInterface(toValue) 119 objArray := make([]interface{}, len(fromArrayvalues)) 120 for i, _ := range objArray { 121 objArray[i] = make(map[string]interface{}) 122 } 123 124 mappingField, err := ref.GetMapToPathFields(toMapField) 125 if err != nil { 126 return fmt.Errorf("Get fields from mapping string error, due to [%s]", err.Error()) 127 } 128 if mappingField != nil && len(mappingField.Getfields()) > 0 { 129 vv, err := flogojson.SetFieldValue(objArray, toValue, mappingField) 130 if err != nil { 131 return err 132 } 133 log.Debugf("Set Value return as %+v", vv) 134 } else { 135 toValue = objArray 136 } 137 138 if err != nil { 139 return err 140 } 141 142 for i, arrayV := range fromArrayvalues { 143 err = a.iterator(arrayV, objArray[i], a.Fields, inputScope, outputScope, resolver) 144 if err != nil { 145 log.Error(err) 146 return err 147 } 148 } 149 150 //Get Value from fields 151 toFieldName, err := ref.GetMapToAttrName(toMapField) 152 if err != nil { 153 return err 154 } 155 156 if len(mappingField.Getfields()) > 0 { 157 return assign.SetAttribute(toFieldName, toValue, outputScope) 158 } 159 return assign.SetAttribute(toFieldName, getFieldValue(toValue, toFieldName), outputScope) 160 } 161 return nil 162 } 163 164 func (a *ArrayMapping) mappingDef() *data.MappingDef { 165 return &data.MappingDef{MapTo: a.To, Value: a.From, Type: data.MtExpression} 166 } 167 168 func (a *ArrayMapping) iterator(fromValue, value interface{}, fields []*ArrayMapping, inputScope, outputScope data.Scope, resolver data.Resolver) error { 169 for _, arrayField := range fields { 170 switch arrayField.Type { 171 //Backward compatibility 172 case PRIMITIVE, "expression": 173 fValue, err := getArrayExpresssionValue(fromValue, arrayField.From, inputScope, resolver) 174 if err != nil { 175 return err 176 } 177 log.Debugf("Array mapping from %s 's value %+v", arrayField.From, fValue) 178 tomapField, err := field.ParseMappingField(ref.GetFieldNameFromArrayRef(arrayField.To)) 179 if err != nil { 180 return err 181 } 182 err = arrayField.DoMap(fValue, value, tomapField, inputScope, outputScope, resolver) 183 if err != nil { 184 return err 185 } 186 case "assign", "literal": 187 fValue, err := getArrayValue(fromValue, arrayField.From, inputScope, resolver) 188 if err != nil { 189 return err 190 } 191 log.Debugf("Array mapping from %s 's value %+v", arrayField.From, fValue) 192 tomapField, err := field.ParseMappingField(ref.GetFieldNameFromArrayRef(arrayField.To)) 193 if err != nil { 194 return err 195 } 196 err = arrayField.DoMap(fValue, value, tomapField, inputScope, outputScope, resolver) 197 if err != nil { 198 return err 199 } 200 case "object": 201 //TODO support object mapping 202 case FOREACH: 203 var fromArrayvalues []interface{} 204 if strings.EqualFold(arrayField.From.(string), NEWARRAY) { 205 log.Debugf("Init a new array for field", arrayField.To) 206 fromArrayvalues = make([]interface{}, 1) 207 } else { 208 fValue, err := getArrayExpresssionValue(fromValue, arrayField.From, inputScope, resolver) 209 if err != nil { 210 return err 211 } 212 var ok bool 213 fromArrayvalues, ok = fValue.([]interface{}) 214 if !ok { 215 return fmt.Errorf("Failed to get array value from [%s], due to error- value not an array", fValue) 216 } 217 } 218 219 toValue := toInterface(value) 220 objArray := make([]interface{}, len(fromArrayvalues)) 221 for i, _ := range objArray { 222 objArray[i] = make(map[string]interface{}) 223 } 224 225 tomapField, err := field.ParseMappingField(ref.GetFieldNameFromArrayRef(arrayField.To)) 226 if err != nil { 227 return err 228 } 229 _, err = flogojson.SetFieldValue(objArray, toValue, tomapField) 230 if err != nil { 231 return err 232 } 233 //Check if fields is empty for primitive array mapping 234 if arrayField.Fields == nil || len(arrayField.Fields) <= 0 { 235 for f, v := range fromArrayvalues { 236 objArray[f] = v 237 } 238 continue 239 } 240 241 for i, arrayV := range fromArrayvalues { 242 err = a.iterator(arrayV, objArray[i], arrayField.Fields, inputScope, outputScope, resolver) 243 if err != nil { 244 return err 245 } 246 } 247 } 248 } 249 250 return nil 251 252 } 253 254 func (a *ArrayMapping) DoMap(fromValue, value interface{}, tomapField *field.MappingField, inputScope, outputScope data.Scope, resolver data.Resolver) error { 255 switch a.Type { 256 case PRIMITIVE, "assign", "literal", "expression", "object": 257 _, err := flogojson.SetFieldValue(fromValue, value, tomapField) 258 if err != nil { 259 return err 260 } 261 case FOREACH: 262 fmt.Println("============") 263 fValue, err := getArrayExpresssionValue(fromValue, a.From, inputScope, resolver) 264 if err != nil { 265 return err 266 } 267 tValue, err := getArrayExpresssionValue(value, a.To, inputScope, resolver) 268 if err != nil { 269 return err 270 } 271 err = a.iterator(fValue, tValue, a.Fields, inputScope, outputScope, resolver) 272 if err != nil { 273 return err 274 } 275 } 276 return nil 277 } 278 279 func (a *ArrayMapping) RemovePrefixForMapTo() { 280 if a == nil { 281 return 282 } 283 284 a.To = RemovePrefixInput(a.To) 285 286 if a.Type == FOREACH { 287 //Validate root from/to field 288 if a.From == NEWARRAY { 289 //Make sure no array ref fields exist 290 for _, field := range a.Fields { 291 if field.Type == FOREACH { 292 field.RemovePrefixForMapTo() 293 } else { 294 field.To = RemovePrefixInput(field.To) 295 } 296 } 297 298 } else { 299 for _, field := range a.Fields { 300 if field.Type == FOREACH { 301 field.RemovePrefixForMapTo() 302 } else { 303 field.To = RemovePrefixInput(field.To) 304 } 305 } 306 } 307 308 } 309 } 310 311 func ParseArrayMapping(arrayDatadata interface{}) (*ArrayMapping, error) { 312 amapping := &ArrayMapping{} 313 switch t := arrayDatadata.(type) { 314 case string: 315 err := json.Unmarshal([]byte(t), amapping) 316 if err != nil { 317 return nil, err 318 } 319 case interface{}: 320 s, err := data.CoerceToString(t) 321 if err != nil { 322 return nil, fmt.Errorf("Convert array mapping value to string error, due to [%s]", err.Error()) 323 } 324 err = json.Unmarshal([]byte(s), amapping) 325 if err != nil { 326 return nil, err 327 } 328 } 329 return amapping, nil 330 } 331 332 func toInterface(data interface{}) interface{} { 333 334 switch t := data.(type) { 335 case string: 336 if strings.EqualFold("{}", t) { 337 return make(map[string]interface{}) 338 } 339 default: 340 if t == nil { 341 //TODO maybe consider other types as well 342 return make(map[string]interface{}) 343 } 344 } 345 return data 346 } 347 348 func getFieldValue(value interface{}, fieldName string) interface{} { 349 switch t := value.(type) { 350 case map[string]interface{}: 351 return t[fieldName] 352 default: 353 return value 354 } 355 return value 356 } 357 358 func getArrayExpresssionValue(object interface{}, expressionRef interface{}, inputScope data.Scope, resolver data.Resolver) (interface{}, error) { 359 stringVal, ok := expressionRef.(string) 360 if !ok { 361 //Non string value 362 return expressionRef, nil 363 } 364 exp, err := expression.ParseExpression(stringVal) 365 if err == nil { 366 //flogo expression 367 expValue, err := exp.EvalWithData(object, inputScope, resolver) 368 if err != nil { 369 err = fmt.Errorf("Execution failed for mapping [%s] due to error - %s", stringVal, err.Error()) 370 log.Error(err) 371 return nil, err 372 } 373 return expValue, nil 374 } else { 375 return getArrayValue(object, expressionRef, inputScope, resolver) 376 } 377 378 } 379 380 func getArrayValue(object interface{}, expressionRef interface{}, inputScope data.Scope, resolver data.Resolver) (interface{}, error) { 381 var fromValue interface{} 382 383 stringVal, ok := expressionRef.(string) 384 if !ok { 385 return expressionRef, nil 386 } 387 if ref.IsArrayMapping(stringVal) { 388 reference := ref.GetFieldNameFromArrayRef(stringVal) 389 toMapField, err := field.ParseMappingField(reference) 390 if err != nil { 391 return nil, err 392 } 393 394 fromValue, err = flogojson.GetFieldValue(object, toMapField) 395 if err != nil { 396 return nil, err 397 } 398 399 } else if strings.HasPrefix(stringVal, "$") { 400 fromRef := ref.NewMappingRef(stringVal) 401 var err error 402 fromValue, err = fromRef.GetValue(inputScope, resolver) 403 if err != nil { 404 return nil, fmt.Errorf("Get value from [%s] failed, due to error - %s", stringVal, err.Error()) 405 } 406 } else { 407 fromValue = expressionRef 408 } 409 410 return fromValue, nil 411 412 } 413 414 func RemovePrefixInput(str string) string { 415 if str != "" && strings.HasPrefix(str, MAP_TO_INPUT) { 416 //Remove $INPUT for mapTo 417 newMapTo := str[len(MAP_TO_INPUT):] 418 if strings.HasPrefix(newMapTo, ".") { 419 newMapTo = newMapTo[1:] 420 } 421 str = newMapTo 422 } 423 return str 424 }