github.com/Jeffail/benthos/v3@v3.65.0/lib/processor/number.go (about) 1 package processor 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strconv" 7 "time" 8 9 "github.com/Jeffail/benthos/v3/internal/bloblang/field" 10 "github.com/Jeffail/benthos/v3/internal/docs" 11 "github.com/Jeffail/benthos/v3/internal/interop" 12 "github.com/Jeffail/benthos/v3/internal/tracing" 13 "github.com/Jeffail/benthos/v3/lib/log" 14 "github.com/Jeffail/benthos/v3/lib/metrics" 15 "github.com/Jeffail/benthos/v3/lib/types" 16 ) 17 18 //------------------------------------------------------------------------------ 19 20 func init() { 21 Constructors[TypeNumber] = TypeSpec{ 22 constructor: NewNumber, 23 Status: docs.StatusDeprecated, 24 Footnotes: ` 25 ## Alternatives 26 27 All functionality of this processor has been superseded by the 28 [bloblang](/docs/components/processors/bloblang) processor.`, 29 FieldSpecs: docs.FieldSpecs{ 30 docs.FieldCommon("operator", "The [operator](#operators) to apply."), 31 docs.FieldCommon("value", "A value used by the operator.").IsInterpolated(), 32 PartsFieldSpec, 33 }, 34 } 35 } 36 37 //------------------------------------------------------------------------------ 38 39 // NumberConfig contains configuration fields for the Number processor. 40 type NumberConfig struct { 41 Parts []int `json:"parts" yaml:"parts"` 42 Operator string `json:"operator" yaml:"operator"` 43 Value interface{} `json:"value" yaml:"value"` 44 } 45 46 // NewNumberConfig returns a NumberConfig with default values. 47 func NewNumberConfig() NumberConfig { 48 return NumberConfig{ 49 Parts: []int{}, 50 Operator: "add", 51 Value: 0, 52 } 53 } 54 55 //------------------------------------------------------------------------------ 56 57 type numberOperator func(content, value float64) float64 58 59 func newNumberAddOperator() numberOperator { 60 return func(content, value float64) float64 { 61 return content + value 62 } 63 } 64 65 func newNumberSubtractOperator() numberOperator { 66 return func(content, value float64) float64 { 67 return content - value 68 } 69 } 70 71 func getNumberOperator(opStr string) (numberOperator, error) { 72 switch opStr { 73 case "add": 74 return newNumberAddOperator(), nil 75 case "subtract": 76 return newNumberSubtractOperator(), nil 77 } 78 return nil, fmt.Errorf("operator not recognised: %v", opStr) 79 } 80 81 //------------------------------------------------------------------------------ 82 83 // Number is a processor that performs number based operations on payloads. 84 type Number struct { 85 parts []int 86 87 interpolatedValue *field.Expression 88 value float64 89 operator numberOperator 90 91 conf Config 92 log log.Modular 93 stats metrics.Type 94 95 mCount metrics.StatCounter 96 mErr metrics.StatCounter 97 mSent metrics.StatCounter 98 mBatchSent metrics.StatCounter 99 } 100 101 // NewNumber returns a Number processor. 102 func NewNumber( 103 conf Config, mgr types.Manager, log log.Modular, stats metrics.Type, 104 ) (Type, error) { 105 n := &Number{ 106 parts: conf.Number.Parts, 107 conf: conf, 108 log: log, 109 stats: stats, 110 111 mCount: stats.GetCounter("count"), 112 mErr: stats.GetCounter("error"), 113 mSent: stats.GetCounter("sent"), 114 mBatchSent: stats.GetCounter("batch.sent"), 115 } 116 117 var err error 118 switch t := conf.Number.Value.(type) { 119 case string: 120 n.interpolatedValue, err = interop.NewBloblangField(mgr, t) 121 case float64: 122 n.value = t 123 case int: 124 n.value = float64(t) 125 case json.Number: 126 n.value, err = t.Float64() 127 default: 128 err = fmt.Errorf("value type '%T' not allowed", t) 129 } 130 if err != nil { 131 return nil, fmt.Errorf("failed to parse value: %v", err) 132 } 133 134 if n.operator, err = getNumberOperator(conf.Number.Operator); err != nil { 135 return nil, err 136 } 137 return n, nil 138 } 139 140 //------------------------------------------------------------------------------ 141 142 // ProcessMessage applies the processor to a message, either creating >0 143 // resulting messages or a response to be sent back to the message source. 144 func (n *Number) ProcessMessage(msg types.Message) ([]types.Message, types.Response) { 145 n.mCount.Incr(1) 146 newMsg := msg.Copy() 147 148 proc := func(index int, span *tracing.Span, part types.Part) error { 149 value := n.value 150 if n.interpolatedValue != nil { 151 interpStr := n.interpolatedValue.String(index, msg) 152 var err error 153 if value, err = strconv.ParseFloat(interpStr, 64); err != nil { 154 n.mErr.Incr(1) 155 n.log.Debugf("Failed to parse interpolated value into float: %v\n", err) 156 return fmt.Errorf("failed to parse interpolated value '%v' into float: %v", interpStr, err) 157 } 158 } 159 160 data, err := strconv.ParseFloat(string(part.Get()), 64) 161 if err != nil { 162 n.mErr.Incr(1) 163 n.log.Debugf("Failed to parse content into float: %v\n", err) 164 return err 165 } 166 data = n.operator(data, value) 167 part.Set([]byte(strconv.FormatFloat(data, 'f', -1, 64))) 168 return nil 169 } 170 171 IteratePartsWithSpanV2(TypeNumber, n.parts, newMsg, proc) 172 173 n.mBatchSent.Incr(1) 174 n.mSent.Incr(int64(newMsg.Len())) 175 return []types.Message{newMsg}, nil 176 } 177 178 // CloseAsync shuts down the processor and stops processing requests. 179 func (n *Number) CloseAsync() { 180 } 181 182 // WaitForClose blocks until the processor has closed down. 183 func (n *Number) WaitForClose(timeout time.Duration) error { 184 return nil 185 } 186 187 //------------------------------------------------------------------------------