github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/transporter/mqtt/mqtt_subscriber.go (about)

     1  package mqtt
     2  
     3  import (
     4  	"context"
     5  	"net/url"
     6  	"strconv"
     7  	"strings"
     8  
     9  	mqtt "github.com/eclipse/paho.mqtt.golang"
    10  	"github.com/golang/protobuf/proto"
    11  	"github.com/pkg/errors"
    12  
    13  	"github.com/machinefi/w3bstream/pkg/depends/conf/logger"
    14  	confmqtt "github.com/machinefi/w3bstream/pkg/depends/conf/mqtt"
    15  	"github.com/machinefi/w3bstream/pkg/depends/protocol/eventpb"
    16  	"github.com/machinefi/w3bstream/pkg/depends/x/contextx"
    17  	"github.com/machinefi/w3bstream/pkg/modules/transporter/proxy"
    18  	"github.com/machinefi/w3bstream/pkg/types"
    19  )
    20  
    21  type subscriber struct {
    22  	cli   *confmqtt.Client
    23  	topic string
    24  }
    25  
    26  func ParseInboundMessage(msg mqtt.Message) (*eventpb.Event, error) {
    27  	topic := msg.Topic()
    28  
    29  	parts := strings.Split(topic, "/")
    30  
    31  	if len(parts) == 1 {
    32  		ev := &eventpb.Event{}
    33  		err := proto.Unmarshal(msg.Payload(), ev)
    34  		return ev, err
    35  	}
    36  
    37  	if len(parts) != 4 && len(parts) != 5 {
    38  		return nil, ErrInvalidTopicParts
    39  	}
    40  
    41  	if parts[1] != "push" {
    42  		return nil, ErrNotInboundTopic
    43  	}
    44  
    45  	ev := &eventpb.Event{
    46  		Header: &eventpb.Header{
    47  			Token:     parts[2],
    48  			EventType: parts[3],
    49  		},
    50  		Payload: msg.Payload(),
    51  	}
    52  	if len(parts) == 5 {
    53  		values, err := url.ParseQuery(parts[4])
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  		if v := values.Get("ts"); v != "" {
    58  			ev.Header.PubTime, _ = strconv.ParseInt(v, 10, 64)
    59  		}
    60  		if v := values.Get("id"); v != "" {
    61  			ev.Header.EventId = v
    62  		}
    63  	}
    64  	return ev, nil
    65  }
    66  
    67  func (s *subscriber) subscribing(ctx context.Context) error {
    68  	ctx = contextx.WithContextCompose(
    69  		types.WithProxyClientContext(types.MustProxyClientFromContext(ctx)),
    70  	)(context.Background())
    71  	return s.cli.WithQoS(confmqtt.QOS__ONLY_ONCE).WithTopic(s.topic + "/#").Subscribe(
    72  		func(c mqtt.Client, msg mqtt.Message) {
    73  			ctx, l := logger.NewSpanContext(ctx, "transporter.mqtt.subscriber.handle")
    74  			defer l.End()
    75  
    76  			ev, err := ParseInboundMessage(msg)
    77  			if err != nil {
    78  				l.Error(err)
    79  				return
    80  			}
    81  
    82  			l = l.WithValues(
    83  				"topic", msg.Topic(),
    84  				"id", ev.Header.GetEventId(),
    85  				"type", ev.Header.GetEventType(),
    86  				"time", ev.Header.GetPubTime(),
    87  				"data", len(ev.Payload),
    88  			)
    89  
    90  			_, err = proxy.Forward(ctx, s.topic, ev)
    91  			if err != nil {
    92  				l.Error(errors.Wrap(err, "forward"))
    93  				return
    94  			}
    95  			l.Info("event forwarded")
    96  			// rsp, err := json.Marshal(ret)
    97  			// if err != nil {
    98  			// 	l.Error(errors.Wrap(err, "marshal rsp"))
    99  			// 	return
   100  			// }
   101  
   102  			// topic := path.Join("ack", ret.(*event.EventRsp).PublisherKey)
   103  			// cli := s.cli.WithTopic(topic).WithQoS(confmqtt.QOS__ONCE)
   104  			// if err = cli.Publish(rsp); err != nil {
   105  			// 	l.Error(errors.Wrap(err, "publish rsp"))
   106  			// }
   107  		},
   108  	)
   109  }
   110  
   111  var (
   112  	ErrInvalidTopicParts = errors.New("invalid topic parts")
   113  	ErrNotInboundTopic   = errors.New("not inbound topic")
   114  )