github.com/Jeffail/benthos/v3@v3.65.0/lib/processor/insert_part.go (about) 1 package processor 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/Jeffail/benthos/v3/internal/bloblang/field" 8 "github.com/Jeffail/benthos/v3/internal/docs" 9 "github.com/Jeffail/benthos/v3/internal/interop" 10 "github.com/Jeffail/benthos/v3/lib/log" 11 "github.com/Jeffail/benthos/v3/lib/message" 12 "github.com/Jeffail/benthos/v3/lib/metrics" 13 "github.com/Jeffail/benthos/v3/lib/types" 14 ) 15 16 //------------------------------------------------------------------------------ 17 18 func init() { 19 Constructors[TypeInsertPart] = TypeSpec{ 20 constructor: NewInsertPart, 21 Categories: []Category{ 22 CategoryComposition, 23 }, 24 Summary: ` 25 Insert a new message into a batch at an index. If the specified index is greater 26 than the length of the existing batch it will be appended to the end.`, 27 Description: ` 28 The index can be negative, and if so the message will be inserted from the end 29 counting backwards starting from -1. E.g. if index = -1 then the new message 30 will become the last of the batch, if index = -2 then the new message will be 31 inserted before the last message, and so on. If the negative index is greater 32 than the length of the existing batch it will be inserted at the beginning. 33 34 The new message will have metadata copied from the first pre-existing message of 35 the batch. 36 37 This processor will interpolate functions within the 'content' field, you can 38 find a list of functions [here](/docs/configuration/interpolation#bloblang-queries).`, 39 FieldSpecs: docs.FieldSpecs{ 40 docs.FieldCommon("index", "The index within the batch to insert the message at."), 41 docs.FieldCommon("content", "The content of the message being inserted.").IsInterpolated(), 42 }, 43 } 44 } 45 46 //------------------------------------------------------------------------------ 47 48 // InsertPartConfig contains configuration fields for the InsertPart processor. 49 type InsertPartConfig struct { 50 Index int `json:"index" yaml:"index"` 51 Content string `json:"content" yaml:"content"` 52 } 53 54 // NewInsertPartConfig returns a InsertPartConfig with default values. 55 func NewInsertPartConfig() InsertPartConfig { 56 return InsertPartConfig{ 57 Index: -1, 58 Content: "", 59 } 60 } 61 62 //------------------------------------------------------------------------------ 63 64 // InsertPart is a processor that inserts a new message part at a specific 65 // index. 66 type InsertPart struct { 67 part *field.Expression 68 69 conf Config 70 log log.Modular 71 stats metrics.Type 72 73 mCount metrics.StatCounter 74 mSent metrics.StatCounter 75 mBatchSent metrics.StatCounter 76 } 77 78 // NewInsertPart returns a InsertPart processor. 79 func NewInsertPart( 80 conf Config, mgr types.Manager, log log.Modular, stats metrics.Type, 81 ) (Type, error) { 82 part, err := interop.NewBloblangField(mgr, conf.InsertPart.Content) 83 if err != nil { 84 return nil, fmt.Errorf("failed to parse content expression: %v", err) 85 } 86 return &InsertPart{ 87 part: part, 88 89 conf: conf, 90 log: log, 91 stats: stats, 92 93 mCount: stats.GetCounter("count"), 94 mSent: stats.GetCounter("sent"), 95 mBatchSent: stats.GetCounter("batch.sent"), 96 }, nil 97 } 98 99 //------------------------------------------------------------------------------ 100 101 // ProcessMessage applies the processor to a message, either creating >0 102 // resulting messages or a response to be sent back to the message source. 103 func (p *InsertPart) ProcessMessage(msg types.Message) ([]types.Message, types.Response) { 104 p.mCount.Incr(1) 105 106 newPartBytes := p.part.Bytes(0, msg) 107 index := p.conf.InsertPart.Index 108 msgLen := msg.Len() 109 if index < 0 { 110 index = msgLen + index + 1 111 if index < 0 { 112 index = 0 113 } 114 } else if index > msgLen { 115 index = msgLen 116 } 117 118 newMsg := message.New(nil) 119 newPart := msg.Get(0).Copy() 120 newPart.Set(newPartBytes) 121 msg.Iter(func(i int, p types.Part) error { 122 if i == index { 123 newMsg.Append(newPart) 124 } 125 newMsg.Append(p.Copy()) 126 return nil 127 }) 128 if index == msg.Len() { 129 newMsg.Append(newPart) 130 } 131 132 p.mBatchSent.Incr(1) 133 p.mSent.Incr(int64(newMsg.Len())) 134 msgs := [1]types.Message{newMsg} 135 return msgs[:], nil 136 } 137 138 // CloseAsync shuts down the processor and stops processing requests. 139 func (p *InsertPart) CloseAsync() { 140 } 141 142 // WaitForClose blocks until the processor has closed down. 143 func (p *InsertPart) WaitForClose(timeout time.Duration) error { 144 return nil 145 } 146 147 //------------------------------------------------------------------------------