|  | @@ -1,4 +1,4 @@
 | 
	
		
			
				|  |  | -package main
 | 
	
		
			
				|  |  | +package di
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import (
 | 
	
		
			
				|  |  |  	"context"
 | 
	
	
		
			
				|  | @@ -11,41 +11,39 @@ import (
 | 
	
		
			
				|  |  |  	"strconv"
 | 
	
		
			
				|  |  |  	"time"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	"github.com/aws/aws-sdk-go-v2/aws"
 | 
	
		
			
				|  |  |  	"github.com/aws/aws-sdk-go-v2/service/sqs"
 | 
	
		
			
				|  |  |  	"github.com/aws/aws-sdk-go-v2/service/sqs/types"
 | 
	
		
			
				|  |  |  	"github.com/davecgh/go-spew/spew"
 | 
	
		
			
				|  |  | +	"github.com/fgm/izidic"
 | 
	
		
			
				|  |  |  	"github.com/google/uuid"
 | 
	
		
			
				|  |  | -	"gopkg.in/yaml.v2"
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func lister(ctx context.Context, w io.Writer, client *sqs.Client) string {
 | 
	
		
			
				|  |  | -	lqo, err := client.ListQueues(ctx, &sqs.ListQueuesInput{
 | 
	
		
			
				|  |  | -		MaxResults:      aws.Int32(10),
 | 
	
		
			
				|  |  | -		NextToken:       nil,
 | 
	
		
			
				|  |  | -		QueueNamePrefix: aws.String(""),
 | 
	
		
			
				|  |  | -	})
 | 
	
		
			
				|  |  | -	if err != nil {
 | 
	
		
			
				|  |  | -		log.Fatalf("failed listing queues: %v", err)
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	y := yaml.NewEncoder(w)
 | 
	
		
			
				|  |  | -	y.Encode(lqo.QueueUrls)
 | 
	
		
			
				|  |  | -	return lqo.QueueUrls[0]
 | 
	
		
			
				|  |  | +type Event struct {
 | 
	
		
			
				|  |  | +	MessageID uuid.UUID `json:"MessageId"`
 | 
	
		
			
				|  |  | +	BodySum   string    `json:"MD5OfBody"`
 | 
	
		
			
				|  |  | +	AttrSum   string    `json:"MD5MofMessageAttributes"`
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	EventAttributes
 | 
	
		
			
				|  |  | +	MessageAttributes map[string]types.MessageAttributeValue
 | 
	
		
			
				|  |  | +	Body              []byte
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func receiver(ctx context.Context, w io.Writer, client *sqs.Client, qURL string) {
 | 
	
		
			
				|  |  | -	rmi := sqs.ReceiveMessageInput{
 | 
	
		
			
				|  |  | -		QueueUrl:              &qURL,
 | 
	
		
			
				|  |  | -		AttributeNames:        []types.QueueAttributeName{"All"},
 | 
	
		
			
				|  |  | -		MessageAttributeNames: []string{"All"},
 | 
	
		
			
				|  |  | -		VisibilityTimeout:     1,
 | 
	
		
			
				|  |  | -		WaitTimeSeconds:       5,
 | 
	
		
			
				|  |  | +func (e Event) IsRetryable() bool {
 | 
	
		
			
				|  |  | +	maybeRetry, ok := e.MessageAttributes["retry"]
 | 
	
		
			
				|  |  | +	if !ok {
 | 
	
		
			
				|  |  | +		return false
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	msg, err := client.ReceiveMessage(ctx, &rmi)
 | 
	
		
			
				|  |  | -	if err != nil {
 | 
	
		
			
				|  |  | -		log.Fatalf("failed receiving from queue %s: %v", err)
 | 
	
		
			
				|  |  | +	if maybeRetry.DataType == nil || *maybeRetry.DataType != "String" || maybeRetry.StringValue == nil {
 | 
	
		
			
				|  |  | +		return false
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	spew.Fdump(w, msg.Messages)
 | 
	
		
			
				|  |  | +	return *maybeRetry.StringValue == "1"
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +type EventAttributes struct {
 | 
	
		
			
				|  |  | +	SenderID                         string    `json:"SenderId"`
 | 
	
		
			
				|  |  | +	SentTime                         time.Time `json:"SentTimestamp"`
 | 
	
		
			
				|  |  | +	ApproximateReceiveCount          int       `json:"ApproximateReceiveCount"`
 | 
	
		
			
				|  |  | +	ApproximateFirstReceiveTimestamp time.Time `json:"ApproximateFirstReceiveTimestamp"`
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  type Handler func(ctx context.Context, enc *json.Encoder, msgID uuid.UUID, sent time.Time, input []byte, meta map[string]types.MessageAttributeValue) error
 | 
	
	
		
			
				|  | @@ -59,7 +57,25 @@ func (m message) String() string {
 | 
	
		
			
				|  |  |  	return *m.MessageId
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func consumer(ctx context.Context, w io.Writer, enc *json.Encoder, client *sqs.Client, qURL string, hdl Handler) error {
 | 
	
		
			
				|  |  | +func ConsumerService(dic *izidic.Container) (any, error) {
 | 
	
		
			
				|  |  | +	cli := dic.MustService("sqs").(*sqs.Client)
 | 
	
		
			
				|  |  | +	w := dic.MustParam("writer").(io.Writer)
 | 
	
		
			
				|  |  | +	hdl := dic.MustParam("handler").(Handler)
 | 
	
		
			
				|  |  | +	enc := json.NewEncoder(w)
 | 
	
		
			
				|  |  | +	return func(ctx context.Context, qURL string) error {
 | 
	
		
			
				|  |  | +		return consumeMessage(ctx, w, enc, cli, qURL, hdl)
 | 
	
		
			
				|  |  | +	}, nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func ReceiverService(dic *izidic.Container) (any, error) {
 | 
	
		
			
				|  |  | +	cli := dic.MustService("sqs").(*sqs.Client)
 | 
	
		
			
				|  |  | +	w := dic.MustParam("writer").(io.Writer)
 | 
	
		
			
				|  |  | +	return func(ctx context.Context, qURL string) {
 | 
	
		
			
				|  |  | +		receiveMessage(ctx, w, cli, qURL)
 | 
	
		
			
				|  |  | +	}, nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func consumeMessage(ctx context.Context, _ io.Writer, enc *json.Encoder, client *sqs.Client, qURL string, hdl Handler) error {
 | 
	
		
			
				|  |  |  	rmi := sqs.ReceiveMessageInput{
 | 
	
		
			
				|  |  |  		QueueUrl:              &qURL,
 | 
	
		
			
				|  |  |  		AttributeNames:        []types.QueueAttributeName{"All"},
 | 
	
	
		
			
				|  | @@ -84,7 +100,7 @@ func consumer(ctx context.Context, w io.Writer, enc *json.Encoder, client *sqs.C
 | 
	
		
			
				|  |  |  		msg := message(recv.Messages[0])
 | 
	
		
			
				|  |  |  		evt, err := validateMessage(msg)
 | 
	
		
			
				|  |  |  		if err != nil {
 | 
	
		
			
				|  |  | -			log.Printf("invalid message %s: %w, dropping it anyway", msg, err)
 | 
	
		
			
				|  |  | +			log.Printf("invalid message %s: %v, dropping it anyway", msg, err)
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  |  			if err := hdl(ctx, enc, evt.MessageID, evt.SentTime, evt.Body, evt.MessageAttributes); err != nil {
 | 
	
		
			
				|  |  |  				log.Printf("message %s failed processing : %v, dropping it anyway\n", msg, err)
 | 
	
	
		
			
				|  | @@ -92,33 +108,36 @@ func consumer(ctx context.Context, w io.Writer, enc *json.Encoder, client *sqs.C
 | 
	
		
			
				|  |  |  				log.Printf("message %s processed successfully\n", msg)
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -		dmi := sqs.DeleteMessageInput{
 | 
	
		
			
				|  |  | -			QueueUrl:      &qURL,
 | 
	
		
			
				|  |  | -			ReceiptHandle: msg.ReceiptHandle,
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		_, err = client.DeleteMessage(ctx, &dmi)
 | 
	
		
			
				|  |  | -		if err != nil {
 | 
	
		
			
				|  |  | -			log.Printf("Error deleting message %s after successful processing: %v\n", msg, err)
 | 
	
		
			
				|  |  | -			continue
 | 
	
		
			
				|  |  | +		if evt.IsRetryable() {
 | 
	
		
			
				|  |  | +			log.Printf("message %s not deleted, for retry", msg)
 | 
	
		
			
				|  |  | +		} else {
 | 
	
		
			
				|  |  | +			dmi := sqs.DeleteMessageInput{
 | 
	
		
			
				|  |  | +				QueueUrl:      &qURL,
 | 
	
		
			
				|  |  | +				ReceiptHandle: msg.ReceiptHandle,
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			_, err = client.DeleteMessage(ctx, &dmi)
 | 
	
		
			
				|  |  | +			if err != nil {
 | 
	
		
			
				|  |  | +				log.Printf("Error deleting message %s after successful processing: %v\n", msg, err)
 | 
	
		
			
				|  |  | +				continue
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			log.Printf("message %s deleted after processing\n", msg)
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -		log.Printf("message %s deleted after processing\n", msg)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -type EventAttributes struct {
 | 
	
		
			
				|  |  | -	SenderID                         string    `json:"SenderId"`
 | 
	
		
			
				|  |  | -	SentTime                         time.Time `json:"SentTimestamp"`
 | 
	
		
			
				|  |  | -	ApproximateReceiveCount          int       `json:"ApproximateReceiveCount"`
 | 
	
		
			
				|  |  | -	ApproximateFirstReceiveTimestamp time.Time `json:"ApproximateFirstReceiveTimestamp"`
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -type Event struct {
 | 
	
		
			
				|  |  | -	MessageID uuid.UUID `json:"MessageId"`
 | 
	
		
			
				|  |  | -	BodySum   string    `json:"MD5OfBody"`
 | 
	
		
			
				|  |  | -	AttrSum   string    `json:"MD5MofMessageAttributes"`
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	EventAttributes
 | 
	
		
			
				|  |  | -	MessageAttributes map[string]types.MessageAttributeValue
 | 
	
		
			
				|  |  | -	Body              []byte
 | 
	
		
			
				|  |  | +func receiveMessage(ctx context.Context, w io.Writer, client *sqs.Client, qURL string) {
 | 
	
		
			
				|  |  | +	rmi := sqs.ReceiveMessageInput{
 | 
	
		
			
				|  |  | +		QueueUrl:              &qURL,
 | 
	
		
			
				|  |  | +		AttributeNames:        []types.QueueAttributeName{"All"},
 | 
	
		
			
				|  |  | +		MessageAttributeNames: []string{"All"},
 | 
	
		
			
				|  |  | +		VisibilityTimeout:     1,
 | 
	
		
			
				|  |  | +		WaitTimeSeconds:       5,
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	msg, err := client.ReceiveMessage(ctx, &rmi)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		log.Fatalf("failed receiving from queue %s: %v", err)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	spew.Fdump(w, msg.Messages)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func validateMessage(msg message) (*Event, error) {
 |