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  }