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 }