github.com/observiq/carbon@v0.9.11-0.20200820160507-1b872e368a5e/operator/builtin/transformer/restructure.go (about)

     1  package transformer
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  
     8  	"github.com/antonmedv/expr"
     9  	"github.com/antonmedv/expr/vm"
    10  	"github.com/observiq/carbon/entry"
    11  	"github.com/observiq/carbon/errors"
    12  	"github.com/observiq/carbon/operator"
    13  	"github.com/observiq/carbon/operator/helper"
    14  )
    15  
    16  func init() {
    17  	operator.Register("restructure", func() operator.Builder { return NewRestructureOperatorConfig("") })
    18  }
    19  
    20  func NewRestructureOperatorConfig(operatorID string) *RestructureOperatorConfig {
    21  	return &RestructureOperatorConfig{
    22  		TransformerConfig: helper.NewTransformerConfig(operatorID, "restructure"),
    23  	}
    24  }
    25  
    26  // RestructureOperatorConfig is the configuration of a restructure operator
    27  type RestructureOperatorConfig struct {
    28  	helper.TransformerConfig `yaml:",inline"`
    29  
    30  	Ops []Op `json:"ops" yaml:"ops"`
    31  }
    32  
    33  // Build will build a restructure operator from the supplied configuration
    34  func (c RestructureOperatorConfig) Build(context operator.BuildContext) (operator.Operator, error) {
    35  	transformerOperator, err := c.TransformerConfig.Build(context)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	restructureOperator := &RestructureOperator{
    41  		TransformerOperator: transformerOperator,
    42  		ops:                 c.Ops,
    43  	}
    44  
    45  	return restructureOperator, nil
    46  }
    47  
    48  // RestructureOperator is an operator that can restructure incoming entries using operations
    49  type RestructureOperator struct {
    50  	helper.TransformerOperator
    51  	ops []Op
    52  }
    53  
    54  // Process will process an entry with a restructure transformation.
    55  func (p *RestructureOperator) Process(ctx context.Context, entry *entry.Entry) error {
    56  	return p.ProcessWith(ctx, entry, p.Transform)
    57  }
    58  
    59  // Transform will apply the restructure operations to an entry
    60  func (p *RestructureOperator) Transform(entry *entry.Entry) (*entry.Entry, error) {
    61  	for _, op := range p.ops {
    62  		err := op.Apply(entry)
    63  		if err != nil {
    64  			return entry, err
    65  		}
    66  	}
    67  	return entry, nil
    68  }
    69  
    70  /*****************
    71    Op Definitions
    72  *****************/
    73  
    74  // Op is a designated operation on an entry
    75  type Op struct {
    76  	OpApplier
    77  }
    78  
    79  // OpApplier is an entity that applies an operation
    80  type OpApplier interface {
    81  	Apply(entry *entry.Entry) error
    82  	Type() string
    83  }
    84  
    85  // UnmarshalJSON will unmarshal JSON into an operation
    86  func (o *Op) UnmarshalJSON(raw []byte) error {
    87  	var typeDecoder map[string]rawMessage
    88  	err := json.Unmarshal(raw, &typeDecoder)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	return o.unmarshalDecodedType(typeDecoder)
    94  }
    95  
    96  // UnmarshalYAML will unmarshal YAML into an operation
    97  func (o *Op) UnmarshalYAML(unmarshal func(interface{}) error) error {
    98  	var typeDecoder map[string]rawMessage
    99  	err := unmarshal(&typeDecoder)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	return o.unmarshalDecodedType(typeDecoder)
   105  }
   106  
   107  type rawMessage struct {
   108  	unmarshal func(interface{}) error
   109  }
   110  
   111  func (msg *rawMessage) UnmarshalYAML(unmarshal func(interface{}) error) error {
   112  	msg.unmarshal = unmarshal
   113  	return nil
   114  }
   115  
   116  func (msg *rawMessage) UnmarshalJSON(raw []byte) error {
   117  	msg.unmarshal = func(dest interface{}) error {
   118  		return json.Unmarshal(raw, dest)
   119  	}
   120  	return nil
   121  }
   122  
   123  func (msg *rawMessage) Unmarshal(v interface{}) error {
   124  	return msg.unmarshal(v)
   125  }
   126  
   127  func (o *Op) unmarshalDecodedType(typeDecoder map[string]rawMessage) error {
   128  	var rawMessage rawMessage
   129  	var opType string
   130  	for k, v := range typeDecoder {
   131  		if opType != "" {
   132  			return fmt.Errorf("only one Op type can be defined per operation")
   133  		}
   134  		opType = k
   135  		rawMessage = v
   136  	}
   137  
   138  	if opType == "" {
   139  		return fmt.Errorf("no Op type defined")
   140  	}
   141  
   142  	if rawMessage.unmarshal == nil {
   143  		return fmt.Errorf("op fields cannot be empty")
   144  	}
   145  
   146  	opApplier, err := o.getOpApplier(opType, rawMessage)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	o.OpApplier = opApplier
   152  	return nil
   153  }
   154  
   155  func (o *Op) getOpApplier(opType string, rawMessage rawMessage) (OpApplier, error) {
   156  	switch opType {
   157  	case "move":
   158  		var move OpMove
   159  		err := rawMessage.Unmarshal(&move)
   160  		return &move, err
   161  	case "add":
   162  		var add OpAdd
   163  		err := rawMessage.Unmarshal(&add)
   164  		return &add, err
   165  	case "remove":
   166  		var remove OpRemove
   167  		err := rawMessage.Unmarshal(&remove)
   168  		return &remove, err
   169  	case "retain":
   170  		var retain OpRetain
   171  		err := rawMessage.Unmarshal(&retain)
   172  		return &retain, err
   173  	case "flatten":
   174  		var flatten OpFlatten
   175  		err := rawMessage.Unmarshal(&flatten)
   176  		return &flatten, err
   177  	default:
   178  		return nil, fmt.Errorf("unknown op type '%s'", opType)
   179  	}
   180  }
   181  
   182  // MarshalJSON will marshal an operation as JSON
   183  func (o Op) MarshalJSON() ([]byte, error) {
   184  	return json.Marshal(map[string]interface{}{
   185  		o.Type(): o.OpApplier,
   186  	})
   187  }
   188  
   189  // MarshalYAML will marshal an operation as YAML
   190  func (o Op) MarshalYAML() (interface{}, error) {
   191  	return map[string]interface{}{
   192  		o.Type(): o.OpApplier,
   193  	}, nil
   194  }
   195  
   196  /******
   197    Add
   198  *******/
   199  
   200  // OpAdd is an operation for adding fields to an entry
   201  type OpAdd struct {
   202  	Field     entry.Field `json:"field" yaml:"field"`
   203  	Value     interface{} `json:"value,omitempty" yaml:"value,omitempty"`
   204  	program   *vm.Program
   205  	ValueExpr *string `json:"value_expr,omitempty" yaml:"value_expr,omitempty"`
   206  }
   207  
   208  // Apply will perform the add operation on an entry
   209  func (op *OpAdd) Apply(e *entry.Entry) error {
   210  	switch {
   211  	case op.Value != nil:
   212  		err := e.Set(op.Field, op.Value)
   213  		if err != nil {
   214  			return err
   215  		}
   216  	case op.program != nil:
   217  		env := helper.GetExprEnv(e)
   218  		defer helper.PutExprEnv(env)
   219  
   220  		result, err := vm.Run(op.program, env)
   221  		if err != nil {
   222  			return fmt.Errorf("evaluate value_expr: %s", err)
   223  		}
   224  		err = e.Set(op.Field, result)
   225  		if err != nil {
   226  			return err
   227  		}
   228  	default:
   229  		// Should never reach here if we went through the unmarshalling code
   230  		return fmt.Errorf("neither value or value_expr are are set")
   231  	}
   232  
   233  	return nil
   234  }
   235  
   236  // Type will return the type of operation
   237  func (op *OpAdd) Type() string {
   238  	return "add"
   239  }
   240  
   241  type opAddRaw struct {
   242  	Field     *entry.Field `json:"field"      yaml:"field"`
   243  	Value     interface{}  `json:"value"      yaml:"value"`
   244  	ValueExpr *string      `json:"value_expr" yaml:"value_expr"`
   245  }
   246  
   247  // UnmarshalJSON will unmarshal JSON into the add operation
   248  func (op *OpAdd) UnmarshalJSON(raw []byte) error {
   249  	var addRaw opAddRaw
   250  	err := json.Unmarshal(raw, &addRaw)
   251  	if err != nil {
   252  		return fmt.Errorf("decode OpAdd: %s", err)
   253  	}
   254  
   255  	return op.unmarshalFromOpAddRaw(addRaw)
   256  }
   257  
   258  // UnmarshalYAML will unmarshal YAML into the add operation
   259  func (op *OpAdd) UnmarshalYAML(unmarshal func(interface{}) error) error {
   260  	var addRaw opAddRaw
   261  	err := unmarshal(&addRaw)
   262  	if err != nil {
   263  		return fmt.Errorf("decode OpAdd: %s", err)
   264  	}
   265  
   266  	return op.unmarshalFromOpAddRaw(addRaw)
   267  }
   268  
   269  func (op *OpAdd) unmarshalFromOpAddRaw(addRaw opAddRaw) error {
   270  	if addRaw.Field == nil {
   271  		return fmt.Errorf("decode OpAdd: missing required field 'field'")
   272  	}
   273  
   274  	switch {
   275  	case addRaw.Value != nil && addRaw.ValueExpr != nil:
   276  		return fmt.Errorf("decode OpAdd: only one of 'value' or 'value_expr' may be defined")
   277  	case addRaw.Value == nil && addRaw.ValueExpr == nil:
   278  		return fmt.Errorf("decode OpAdd: exactly one of 'value' or 'value_expr' must be defined")
   279  	case addRaw.Value != nil:
   280  		op.Field = *addRaw.Field
   281  		op.Value = addRaw.Value
   282  	case addRaw.ValueExpr != nil:
   283  		compiled, err := expr.Compile(*addRaw.ValueExpr, expr.AllowUndefinedVariables())
   284  		if err != nil {
   285  			return fmt.Errorf("decode OpAdd: failed to compile expression '%s': %w", *addRaw.ValueExpr, err)
   286  		}
   287  		op.Field = *addRaw.Field
   288  		op.program = compiled
   289  		op.ValueExpr = addRaw.ValueExpr
   290  	}
   291  
   292  	return nil
   293  }
   294  
   295  /*********
   296    Remove
   297  *********/
   298  
   299  // OpRemove is operation for removing fields from an entry
   300  type OpRemove struct {
   301  	Field entry.Field
   302  }
   303  
   304  // Apply will perform the remove operation on an entry
   305  func (op *OpRemove) Apply(e *entry.Entry) error {
   306  	e.Delete(op.Field)
   307  	return nil
   308  }
   309  
   310  // Type will return the type of operation
   311  func (op *OpRemove) Type() string {
   312  	return "remove"
   313  }
   314  
   315  // UnmarshalJSON will unmarshal JSON into a remove operation
   316  func (op *OpRemove) UnmarshalJSON(raw []byte) error {
   317  	return json.Unmarshal(raw, &op.Field)
   318  }
   319  
   320  // UnmarshalYAML will unmarshal YAML into a remove operation
   321  func (op *OpRemove) UnmarshalYAML(unmarshal func(interface{}) error) error {
   322  	return unmarshal(&op.Field)
   323  }
   324  
   325  // MarshalJSON will marshal a remove operation into JSON
   326  func (op OpRemove) MarshalJSON() ([]byte, error) {
   327  	return json.Marshal(op.Field)
   328  }
   329  
   330  // MarshalYAML will marshal a remove operation into YAML
   331  func (op OpRemove) MarshalYAML() (interface{}, error) {
   332  	return op.Field.String(), nil
   333  }
   334  
   335  /*********
   336    Retain
   337  *********/
   338  
   339  // OpRetain is an operation for retaining fields
   340  type OpRetain struct {
   341  	Fields []entry.Field
   342  }
   343  
   344  // Apply will perform the retain operation on an entry
   345  func (op *OpRetain) Apply(e *entry.Entry) error {
   346  	newEntry := entry.New()
   347  	newEntry.Timestamp = e.Timestamp
   348  	for _, field := range op.Fields {
   349  		val, ok := e.Get(field)
   350  		if !ok {
   351  			continue
   352  		}
   353  		err := newEntry.Set(field, val)
   354  		if err != nil {
   355  			return err
   356  		}
   357  	}
   358  	*e = *newEntry
   359  	return nil
   360  }
   361  
   362  // Type will return the type of operation
   363  func (op *OpRetain) Type() string {
   364  	return "retain"
   365  }
   366  
   367  // UnmarshalJSON will unmarshal JSON into a retain operation
   368  func (op *OpRetain) UnmarshalJSON(raw []byte) error {
   369  	return json.Unmarshal(raw, &op.Fields)
   370  }
   371  
   372  // UnmarshalYAML will unmarshal YAML into a retain operation
   373  func (op *OpRetain) UnmarshalYAML(unmarshal func(interface{}) error) error {
   374  	return unmarshal(&op.Fields)
   375  }
   376  
   377  // MarshalJSON will marshal a retain operation into JSON
   378  func (op OpRetain) MarshalJSON() ([]byte, error) {
   379  	return json.Marshal(op.Fields)
   380  }
   381  
   382  // MarshalYAML will marshal a retain operation into YAML
   383  func (op OpRetain) MarshalYAML() (interface{}, error) {
   384  	return op.Fields, nil
   385  }
   386  
   387  /*******
   388    Move
   389  *******/
   390  
   391  // OpMove is an operation for moving entry fields
   392  type OpMove struct {
   393  	From entry.Field `json:"from" yaml:"from,flow"`
   394  	To   entry.Field `json:"to" yaml:"to,flow"`
   395  }
   396  
   397  // Apply will perform the move operation on an entry
   398  func (op *OpMove) Apply(e *entry.Entry) error {
   399  	val, ok := e.Delete(op.From)
   400  	if !ok {
   401  		return fmt.Errorf("apply move: field %s does not exist on record", op.From)
   402  	}
   403  
   404  	return e.Set(op.To, val)
   405  }
   406  
   407  // Type will return the type of operation
   408  func (op *OpMove) Type() string {
   409  	return "move"
   410  }
   411  
   412  /**********
   413    Flatten
   414  **********/
   415  
   416  // OpFlatten is an operation for flattening fields
   417  type OpFlatten struct {
   418  	Field entry.RecordField
   419  }
   420  
   421  // Apply will perform the flatten operation on an entry
   422  func (op *OpFlatten) Apply(e *entry.Entry) error {
   423  	parent := op.Field.Parent()
   424  	val, ok := e.Delete(op.Field)
   425  	if !ok {
   426  		// The field doesn't exist, so ignore it
   427  		return fmt.Errorf("apply flatten: field %s does not exist on record", op.Field)
   428  	}
   429  
   430  	valMap, ok := val.(map[string]interface{})
   431  	if !ok {
   432  		// The field we were asked to flatten was not a map, so put it back
   433  		err := e.Set(op.Field, val)
   434  		if err != nil {
   435  			return errors.Wrap(err, "reset non-map field")
   436  		}
   437  		return fmt.Errorf("apply flatten: field %s is not a map", op.Field)
   438  	}
   439  
   440  	for k, v := range valMap {
   441  		err := e.Set(parent.Child(k), v)
   442  		if err != nil {
   443  			return err
   444  		}
   445  	}
   446  	return nil
   447  }
   448  
   449  // Type will return the type of operation
   450  func (op *OpFlatten) Type() string {
   451  	return "flatten"
   452  }
   453  
   454  // UnmarshalJSON will unmarshal JSON into a flatten operation
   455  func (op *OpFlatten) UnmarshalJSON(raw []byte) error {
   456  	return json.Unmarshal(raw, &op.Field)
   457  }
   458  
   459  // UnmarshalYAML will unmarshal YAML into a flatten operation
   460  func (op *OpFlatten) UnmarshalYAML(unmarshal func(interface{}) error) error {
   461  	return unmarshal(&op.Field)
   462  }
   463  
   464  // MarshalJSON will marshal a flatten operation into JSON
   465  func (op OpFlatten) MarshalJSON() ([]byte, error) {
   466  	return json.Marshal(op.Field)
   467  }
   468  
   469  // MarshalYAML will marshal a flatten operation into YAML
   470  func (op OpFlatten) MarshalYAML() (interface{}, error) {
   471  	return op.Field.String(), nil
   472  }