github.com/Jeffail/benthos/v3@v3.65.0/public/service/example_stream_builder_yaml_test.go (about)

     1  package service_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/Jeffail/benthos/v3/public/service"
    11  
    12  	// Import all standard Benthos components
    13  	_ "github.com/Jeffail/benthos/v3/public/components/all"
    14  )
    15  
    16  // This example demonstrates how to use a stream builder to parse and execute a
    17  // full Benthos config.
    18  func Example_streamBuilderConfig() {
    19  	panicOnErr := func(err error) {
    20  		if err != nil {
    21  			panic(err)
    22  		}
    23  	}
    24  
    25  	builder := service.NewStreamBuilder()
    26  
    27  	// Set the full Benthos configuration of the stream.
    28  	err := builder.SetYAML(`
    29  input:
    30    generate:
    31      count: 1
    32      interval: 1ms
    33      mapping: 'root = "hello world"'
    34  
    35  pipeline:
    36    processors:
    37      - bloblang: 'root = content().uppercase()'
    38  
    39  output:
    40    stdout: {}
    41  `)
    42  	panicOnErr(err)
    43  
    44  	// Build a stream with our configured components.
    45  	stream, err := builder.Build()
    46  	panicOnErr(err)
    47  
    48  	// And run it, blocking until it gracefully terminates once the generate
    49  	// input has generated a message and it has flushed through the stream.
    50  	err = stream.Run(context.Background())
    51  	panicOnErr(err)
    52  
    53  	// Output: HELLO WORLD
    54  }
    55  
    56  // This example demonstrates how to use a stream builder to assemble a stream of
    57  // Benthos components by adding snippets of configs for different component
    58  // types, and then execute it. You can use the Add methods to append any number
    59  // of components to the stream, following fan in and fan out patterns for inputs
    60  // and outputs respectively.
    61  func Example_streamBuilderConfigAddMethods() {
    62  	panicOnErr := func(err error) {
    63  		if err != nil {
    64  			panic(err)
    65  		}
    66  	}
    67  
    68  	builder := service.NewStreamBuilder()
    69  
    70  	err := builder.AddInputYAML(`
    71  generate:
    72    count: 1
    73    interval: 1ms
    74    mapping: 'root = "hello world"'
    75  `)
    76  	panicOnErr(err)
    77  
    78  	err = builder.AddProcessorYAML(`bloblang: 'root = content().uppercase()'`)
    79  	panicOnErr(err)
    80  
    81  	err = builder.AddOutputYAML(`stdout: {}`)
    82  	panicOnErr(err)
    83  
    84  	// Build a stream with our configured components.
    85  	stream, err := builder.Build()
    86  	panicOnErr(err)
    87  
    88  	// And run it, blocking until it gracefully terminates once the generate
    89  	// input has generated a message and it has flushed through the stream.
    90  	err = stream.Run(context.Background())
    91  	panicOnErr(err)
    92  
    93  	// Output: HELLO WORLD
    94  }
    95  
    96  // This example demonstrates how to use a stream builder to assemble a
    97  // processing pipeline that you can push messages into and extract via closures.
    98  func Example_streamBuilderPush() {
    99  	panicOnErr := func(err error) {
   100  		if err != nil {
   101  			panic(err)
   102  		}
   103  	}
   104  
   105  	builder := service.NewStreamBuilder()
   106  	err := builder.SetLoggerYAML(`level: NONE`)
   107  	panicOnErr(err)
   108  
   109  	err = builder.AddProcessorYAML(`bloblang: 'root = content().uppercase()'`)
   110  	panicOnErr(err)
   111  
   112  	err = builder.AddProcessorYAML(`bloblang: 'root = "check this out: " + content()'`)
   113  	panicOnErr(err)
   114  
   115  	// Obtain a closure func that allows us to push data into the stream, this
   116  	// is treated like any other input, which also means it's possible to use
   117  	// this along with regular configured inputs.
   118  	sendFn, err := builder.AddProducerFunc()
   119  	panicOnErr(err)
   120  
   121  	// Define a closure func that receives messages as an output of the stream.
   122  	// It's also possible to use this along with regular configured outputs.
   123  	var outputBuf bytes.Buffer
   124  	err = builder.AddConsumerFunc(func(c context.Context, m *service.Message) error {
   125  		msgBytes, err := m.AsBytes()
   126  		if err != nil {
   127  			return err
   128  		}
   129  
   130  		_, err = fmt.Fprintf(&outputBuf, "received: %s\n", msgBytes)
   131  		return err
   132  	})
   133  	panicOnErr(err)
   134  
   135  	stream, err := builder.Build()
   136  	panicOnErr(err)
   137  
   138  	go func() {
   139  		perr := sendFn(context.Background(), service.NewMessage([]byte("hello world")))
   140  		panicOnErr(perr)
   141  
   142  		perr = sendFn(context.Background(), service.NewMessage([]byte("I'm pushing data into the stream")))
   143  		panicOnErr(perr)
   144  
   145  		perr = stream.StopWithin(time.Second)
   146  		panicOnErr(perr)
   147  	}()
   148  
   149  	// And run it, blocking until it gracefully terminates once the generate
   150  	// input has generated a message and it has flushed through the stream.
   151  	err = stream.Run(context.Background())
   152  	panicOnErr(err)
   153  
   154  	fmt.Println(outputBuf.String())
   155  
   156  	// Output: received: check this out: HELLO WORLD
   157  	// received: check this out: I'M PUSHING DATA INTO THE STREAM
   158  }
   159  
   160  // This example demonstrates using the stream builder API to create and run two
   161  // independent streams.
   162  func Example_streamBuilderMultipleStreams() {
   163  	panicOnErr := func(err error) {
   164  		if err != nil {
   165  			panic(err)
   166  		}
   167  	}
   168  
   169  	// Build the first stream pipeline. Note that we configure each pipeline
   170  	// with its HTTP server disabled as otherwise we would see a port collision
   171  	// when they both attempt to bind to the default address `0.0.0.0:4195`.
   172  	//
   173  	// Alternatively, we could choose to configure each with their own address
   174  	// with the field `http.address`, or we could call `SetHTTPMux` on the
   175  	// builder in order to explicitly override the configured server.
   176  	builderOne := service.NewStreamBuilder()
   177  
   178  	err := builderOne.SetYAML(`
   179  http:
   180    enabled: false
   181  
   182  input:
   183    generate:
   184      count: 1
   185      interval: 1ms
   186      mapping: 'root = "hello world one"'
   187  
   188  pipeline:
   189    processors:
   190      - bloblang: 'root = content().uppercase()'
   191  
   192  output:
   193    stdout: {}
   194  `)
   195  	panicOnErr(err)
   196  
   197  	streamOne, err := builderOne.Build()
   198  	panicOnErr(err)
   199  
   200  	builderTwo := service.NewStreamBuilder()
   201  
   202  	err = builderTwo.SetYAML(`
   203  http:
   204    enabled: false
   205  
   206  input:
   207    generate:
   208      count: 1
   209      interval: 1ms
   210      mapping: 'root = "hello world two"'
   211  
   212  pipeline:
   213    processors:
   214      - sleep:
   215          duration: 500ms
   216      - bloblang: 'root = content().capitalize()'
   217  
   218  output:
   219    stdout: {}
   220  `)
   221  	panicOnErr(err)
   222  
   223  	streamTwo, err := builderTwo.Build()
   224  	panicOnErr(err)
   225  
   226  	var wg sync.WaitGroup
   227  	wg.Add(2)
   228  
   229  	go func() {
   230  		defer wg.Done()
   231  		panicOnErr(streamOne.Run(context.Background()))
   232  	}()
   233  	go func() {
   234  		defer wg.Done()
   235  		panicOnErr(streamTwo.Run(context.Background()))
   236  	}()
   237  
   238  	wg.Wait()
   239  
   240  	// Output: HELLO WORLD ONE
   241  	// Hello World Two
   242  }