github.com/Jeffail/benthos/v3@v3.65.0/lib/output/reject.go (about)

     1  package output
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     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/lib/log"
    13  	"github.com/Jeffail/benthos/v3/lib/metrics"
    14  	"github.com/Jeffail/benthos/v3/lib/types"
    15  )
    16  
    17  //------------------------------------------------------------------------------
    18  
    19  func init() {
    20  	Constructors[TypeReject] = TypeSpec{
    21  		constructor: fromSimpleConstructor(func(conf Config, mgr types.Manager, log log.Modular, stats metrics.Type) (Type, error) {
    22  			f, err := newRejectWriter(mgr, string(conf.Reject))
    23  			if err != nil {
    24  				return nil, err
    25  			}
    26  			return NewAsyncWriter(TypeReject, 1, f, log, stats)
    27  		}),
    28  		Status: docs.StatusStable,
    29  		Summary: `
    30  Rejects all messages, treating them as though the output destination failed to publish them.`,
    31  		Description: `
    32  The routing of messages after this output depends on the type of input it came from. For inputs that support propagating nacks upstream such as AMQP or NATS the message will be nacked. However, for inputs that are sequential such as files or Kafka the messages will simply be reprocessed from scratch.
    33  
    34  If you're still scratching your head as to when this output could be useful check out [the examples below](#examples).`,
    35  		Categories: []Category{
    36  			CategoryUtility,
    37  		},
    38  		Examples: []docs.AnnotatedExample{
    39  			{
    40  				Title: "Rejecting Failed Messages",
    41  				Summary: `
    42  This input is particularly useful for routing messages that have failed during processing, where instead of routing them to some sort of dead letter queue we wish to push the error upstream. We can do this with a switch broker:`,
    43  				Config: `
    44  output:
    45    switch:
    46      retry_until_success: false
    47      cases:
    48        - check: '!errored()'
    49          output:
    50            amqp_1:
    51              url: amqps://guest:guest@localhost:5672/
    52              target_address: queue:/the_foos
    53  
    54        - output:
    55            reject: "processing failed due to: ${! error() }"
    56  `,
    57  			},
    58  		},
    59  		config: docs.FieldComponent().HasType(docs.FieldTypeString).HasDefault(""),
    60  	}
    61  }
    62  
    63  //------------------------------------------------------------------------------
    64  
    65  // RejectConfig contains configuration fields for the file based output type.
    66  type RejectConfig string
    67  
    68  // NewRejectConfig creates a new RejectConfig with default values.
    69  func NewRejectConfig() RejectConfig {
    70  	return RejectConfig("")
    71  }
    72  
    73  type rejectWriter struct {
    74  	errExpr *field.Expression
    75  }
    76  
    77  func newRejectWriter(mgr types.Manager, errorString string) (*rejectWriter, error) {
    78  	if errorString == "" {
    79  		return nil, errors.New("an error message must be provided in order to provide context for the rejection")
    80  	}
    81  	errExpr, err := interop.NewBloblangField(mgr, errorString)
    82  	if err != nil {
    83  		return nil, fmt.Errorf("failed to parse error expression: %w", err)
    84  	}
    85  	return &rejectWriter{errExpr}, nil
    86  }
    87  
    88  func (w *rejectWriter) ConnectWithContext(ctx context.Context) error {
    89  	return nil
    90  }
    91  
    92  func (w *rejectWriter) WriteWithContext(ctx context.Context, msg types.Message) error {
    93  	errStr := w.errExpr.String(0, msg)
    94  	return errors.New(errStr)
    95  }
    96  
    97  func (w *rejectWriter) CloseAsync() {
    98  }
    99  
   100  func (w *rejectWriter) WaitForClose(timeout time.Duration) error {
   101  	return nil
   102  }