1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-24 13:48:49 +08:00

NOISSUE - Update mProxy handlers for testability (#1889)

* update mproxy handler for testability

Signed-off-by: SammyOina <sammyoina@gmail.com>

* remove import replacement

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
This commit is contained in:
Sammy Kerata Oina 2023-08-22 21:01:47 +03:00 committed by GitHub
parent fde435060c
commit 0525a9ab75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 76 additions and 64 deletions

2
go.mod
View File

@ -29,7 +29,7 @@ require (
github.com/jmoiron/sqlx v1.3.5 github.com/jmoiron/sqlx v1.3.5
github.com/lestrrat-go/jwx/v2 v2.0.11 github.com/lestrrat-go/jwx/v2 v2.0.11
github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2 github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2
github.com/mainflux/mproxy v0.3.0 github.com/mainflux/mproxy v0.3.1-0.20230822124450-4b4dfe600cc2
github.com/mainflux/senml v1.5.0 github.com/mainflux/senml v1.5.0
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/nats-io/nats.go v1.27.1 github.com/nats-io/nats.go v1.27.1

4
go.sum
View File

@ -438,8 +438,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2 h1:QN+yhU6Twwwwz8Mu9u12f2TbPsmM/zIvndAhH1dIdWU= github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2 h1:QN+yhU6Twwwwz8Mu9u12f2TbPsmM/zIvndAhH1dIdWU=
github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2/go.mod h1:q4cTH8I3Y6kDyocJh5dBppuv4dY9drb/2kVdB6FP124= github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2/go.mod h1:q4cTH8I3Y6kDyocJh5dBppuv4dY9drb/2kVdB6FP124=
github.com/mainflux/mproxy v0.3.0 h1:tU60nu/Bd5lWb7NlJJvW6ulKxeBGJFnp5uOS9j6JXBQ= github.com/mainflux/mproxy v0.3.1-0.20230822124450-4b4dfe600cc2 h1:D5Ofrffx/4FWehczvJbmzD8lfcOkxcIS4XZE/fwl4mo=
github.com/mainflux/mproxy v0.3.0/go.mod h1:nG9MP2YbS8ax26Z8mvJOYohhi3ebYwSlOmePzbfv2ew= github.com/mainflux/mproxy v0.3.1-0.20230822124450-4b4dfe600cc2/go.mod h1:nG9MP2YbS8ax26Z8mvJOYohhi3ebYwSlOmePzbfv2ew=
github.com/mainflux/senml v1.5.0 h1:GAd1y1eMohfa6sVYcr2iQfVfkkh9l/q7B1TWF5L68xs= github.com/mainflux/senml v1.5.0 h1:GAd1y1eMohfa6sVYcr2iQfVfkkh9l/q7B1TWF5L68xs=
github.com/mainflux/senml v1.5.0/go.mod h1:SMX76mM5yenjLVjZOM27+njCGkP+AA64O46nRQiBRlE= github.com/mainflux/senml v1.5.0/go.mod h1:SMX76mM5yenjLVjZOM27+njCGkP+AA64O46nRQiBRlE=
github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=

View File

@ -139,21 +139,20 @@ func (h *handler) AuthSubscribe(ctx context.Context, topics *[]string) error {
} }
// Connect - after client successfully connected. // Connect - after client successfully connected.
func (h *handler) Connect(ctx context.Context) { func (h *handler) Connect(ctx context.Context) error {
s, ok := session.FromContext(ctx) s, ok := session.FromContext(ctx)
if !ok { if !ok {
h.logger.Error(errors.Wrap(ErrFailedConnect, ErrClientNotInitialized).Error()) return errors.Wrap(ErrFailedConnect, ErrClientNotInitialized)
return
} }
h.logger.Info(fmt.Sprintf(LogInfoConnected, s.ID)) h.logger.Info(fmt.Sprintf(LogInfoConnected, s.ID))
return nil
} }
// Publish - after client successfully published. // Publish - after client successfully published.
func (h *handler) Publish(ctx context.Context, topic *string, payload *[]byte) { func (h *handler) Publish(ctx context.Context, topic *string, payload *[]byte) error {
s, ok := session.FromContext(ctx) s, ok := session.FromContext(ctx)
if !ok { if !ok {
h.logger.Error(errors.Wrap(ErrFailedPublish, ErrClientNotInitialized).Error()) return errors.Wrap(ErrFailedPublish, ErrClientNotInitialized)
return
} }
h.logger.Info(fmt.Sprintf(LogInfoPublished, s.ID, *topic)) h.logger.Info(fmt.Sprintf(LogInfoPublished, s.ID, *topic))
// Topics are in the format: // Topics are in the format:
@ -161,8 +160,7 @@ func (h *handler) Publish(ctx context.Context, topic *string, payload *[]byte) {
channelParts := channelRegExp.FindStringSubmatch(*topic) channelParts := channelRegExp.FindStringSubmatch(*topic)
if len(channelParts) < 2 { if len(channelParts) < 2 {
h.logger.Error(errors.Wrap(ErrFailedPublish, ErrMalformedTopic).Error()) return errors.Wrap(ErrFailedPublish, ErrMalformedTopic)
return
} }
chanID := channelParts[1] chanID := channelParts[1]
@ -170,8 +168,7 @@ func (h *handler) Publish(ctx context.Context, topic *string, payload *[]byte) {
subtopic, err := parseSubtopic(subtopic) subtopic, err := parseSubtopic(subtopic)
if err != nil { if err != nil {
h.logger.Error(errors.Wrap(ErrFailedParseSubtopic, err).Error()) return errors.Wrap(ErrFailedParseSubtopic, err)
return
} }
msg := messaging.Message{ msg := messaging.Message{
@ -185,42 +182,43 @@ func (h *handler) Publish(ctx context.Context, topic *string, payload *[]byte) {
for _, pub := range h.publishers { for _, pub := range h.publishers {
if err := pub.Publish(ctx, msg.Channel, &msg); err != nil { if err := pub.Publish(ctx, msg.Channel, &msg); err != nil {
h.logger.Error(errors.Wrap(ErrFailedPublishToMsgBroker, err).Error()) return errors.Wrap(ErrFailedPublishToMsgBroker, err)
} }
} }
return nil
} }
// Subscribe - after client successfully subscribed. // Subscribe - after client successfully subscribed.
func (h *handler) Subscribe(ctx context.Context, topics *[]string) { func (h *handler) Subscribe(ctx context.Context, topics *[]string) error {
s, ok := session.FromContext(ctx) s, ok := session.FromContext(ctx)
if !ok { if !ok {
h.logger.Error(errors.Wrap(ErrFailedSubscribe, ErrClientNotInitialized).Error()) return errors.Wrap(ErrFailedSubscribe, ErrClientNotInitialized)
return
} }
h.logger.Info(fmt.Sprintf(LogInfoSubscribed, s.ID, strings.Join(*topics, ","))) h.logger.Info(fmt.Sprintf(LogInfoSubscribed, s.ID, strings.Join(*topics, ",")))
return nil
} }
// Unsubscribe - after client unsubscribed. // Unsubscribe - after client unsubscribed.
func (h *handler) Unsubscribe(ctx context.Context, topics *[]string) { func (h *handler) Unsubscribe(ctx context.Context, topics *[]string) error {
s, ok := session.FromContext(ctx) s, ok := session.FromContext(ctx)
if !ok { if !ok {
h.logger.Error(errors.Wrap(ErrFailedUnsubscribe, ErrClientNotInitialized).Error()) return errors.Wrap(ErrFailedUnsubscribe, ErrClientNotInitialized)
return
} }
h.logger.Info(fmt.Sprintf(LogInfoUnsubscribed, s.ID, strings.Join(*topics, ","))) h.logger.Info(fmt.Sprintf(LogInfoUnsubscribed, s.ID, strings.Join(*topics, ",")))
return nil
} }
// Disconnect - connection with broker or client lost. // Disconnect - connection with broker or client lost.
func (h *handler) Disconnect(ctx context.Context) { func (h *handler) Disconnect(ctx context.Context) error {
s, ok := session.FromContext(ctx) s, ok := session.FromContext(ctx)
if !ok { if !ok {
h.logger.Error(errors.Wrap(ErrFailedDisconnect, ErrClientNotInitialized).Error()) return errors.Wrap(ErrFailedDisconnect, ErrClientNotInitialized)
return
} }
h.logger.Error(fmt.Sprintf(LogInfoDisconnected, s.ID, s.Password)) h.logger.Error(fmt.Sprintf(LogInfoDisconnected, s.ID, s.Password))
if err := h.es.Disconnect(ctx, string(s.Password)); err != nil { if err := h.es.Disconnect(ctx, string(s.Password)); err != nil {
h.logger.Error(errors.Wrap(ErrFailedPublishDisconnectEvent, err).Error()) return errors.Wrap(ErrFailedPublishDisconnectEvent, err)
} }
return nil
} }
func (h *handler) authAccess(ctx context.Context, password, topic, action string) error { func (h *handler) authAccess(ctx context.Context, password, topic, action string) error {

View File

@ -234,17 +234,19 @@ func TestConnect(t *testing.T) {
cases := []struct { cases := []struct {
desc string desc string
session *session.Session session *session.Session
err error
logMsg string logMsg string
}{ }{
{ {
desc: "connect without active session", desc: "connect without active session",
session: nil, session: nil,
logMsg: errors.Wrap(mqtt.ErrFailedConnect, mqtt.ErrClientNotInitialized).Error(), err: errors.Wrap(mqtt.ErrFailedConnect, mqtt.ErrClientNotInitialized),
}, },
{ {
desc: "connect with active session", desc: "connect with active session",
session: &sessionClient, session: &sessionClient,
logMsg: fmt.Sprintf(mqtt.LogInfoConnected, clientID), logMsg: fmt.Sprintf(mqtt.LogInfoConnected, clientID),
err: nil,
}, },
} }
@ -253,8 +255,9 @@ func TestConnect(t *testing.T) {
if tc.session != nil { if tc.session != nil {
ctx = session.NewContext(ctx, tc.session) ctx = session.NewContext(ctx, tc.session)
} }
handler.Connect(ctx) err := handler.Connect(ctx)
assert.Contains(t, logBuffer.String(), tc.logMsg) assert.Contains(t, logBuffer.String(), tc.logMsg)
assert.Equal(t, tc.err, err)
} }
} }
@ -272,13 +275,14 @@ func TestPublish(t *testing.T) {
topic string topic string
payload []byte payload []byte
logMsg string logMsg string
err error
}{ }{
{ {
desc: "publish without active session", desc: "publish without active session",
session: nil, session: nil,
topic: topic, topic: topic,
payload: payload, payload: payload,
logMsg: mqtt.ErrClientNotInitialized.Error(), err: errors.Wrap(mqtt.ErrFailedPublish, mqtt.ErrClientNotInitialized),
}, },
{ {
desc: "publish with invalid topic", desc: "publish with invalid topic",
@ -286,27 +290,28 @@ func TestPublish(t *testing.T) {
topic: invalidTopic, topic: invalidTopic,
payload: payload, payload: payload,
logMsg: fmt.Sprintf(mqtt.LogInfoPublished, clientID, invalidTopic), logMsg: fmt.Sprintf(mqtt.LogInfoPublished, clientID, invalidTopic),
err: errors.Wrap(mqtt.ErrFailedPublish, mqtt.ErrMalformedTopic),
}, },
{ {
desc: "publish with invalid channel ID", desc: "publish with invalid channel ID",
session: &sessionClient, session: &sessionClient,
topic: invalidChannelIDTopic, topic: invalidChannelIDTopic,
payload: payload, payload: payload,
logMsg: errors.Wrap(mqtt.ErrFailedPublish, mqtt.ErrMalformedTopic).Error(), err: errors.Wrap(mqtt.ErrFailedPublish, mqtt.ErrMalformedTopic),
}, },
{ {
desc: "publish with malformed subtopic", desc: "publish with malformed subtopic",
session: &sessionClient, session: &sessionClient,
topic: malformedSubtopics, topic: malformedSubtopics,
payload: payload, payload: payload,
logMsg: mqtt.ErrMalformedSubtopic.Error(), err: errors.Wrap(mqtt.ErrFailedParseSubtopic, mqtt.ErrMalformedSubtopic),
}, },
{ {
desc: "publish with subtopic containing wrong character", desc: "publish with subtopic containing wrong character",
session: &sessionClient, session: &sessionClient,
topic: wrongCharSubtopics, topic: wrongCharSubtopics,
payload: payload, payload: payload,
logMsg: mqtt.ErrMalformedSubtopic.Error(), err: errors.Wrap(mqtt.ErrFailedParseSubtopic, mqtt.ErrMalformedSubtopic),
}, },
{ {
desc: "publish with subtopic", desc: "publish with subtopic",
@ -329,8 +334,9 @@ func TestPublish(t *testing.T) {
if tc.session != nil { if tc.session != nil {
ctx = session.NewContext(ctx, tc.session) ctx = session.NewContext(ctx, tc.session)
} }
handler.Publish(ctx, &tc.topic, &tc.payload) err := handler.Publish(ctx, &tc.topic, &tc.payload)
assert.Contains(t, logBuffer.String(), tc.logMsg) assert.Contains(t, logBuffer.String(), tc.logMsg)
assert.Equal(t, tc.err, err)
} }
} }
@ -343,12 +349,13 @@ func TestSubscribe(t *testing.T) {
session *session.Session session *session.Session
topic []string topic []string
logMsg string logMsg string
err error
}{ }{
{ {
desc: "subscribe without active session", desc: "subscribe without active session",
session: nil, session: nil,
topic: topics, topic: topics,
logMsg: errors.Wrap(mqtt.ErrFailedSubscribe, mqtt.ErrClientNotInitialized).Error(), err: errors.Wrap(mqtt.ErrFailedSubscribe, mqtt.ErrClientNotInitialized),
}, },
{ {
desc: "subscribe with valid session and topics", desc: "subscribe with valid session and topics",
@ -363,8 +370,9 @@ func TestSubscribe(t *testing.T) {
if tc.session != nil { if tc.session != nil {
ctx = session.NewContext(ctx, tc.session) ctx = session.NewContext(ctx, tc.session)
} }
handler.Subscribe(ctx, &tc.topic) err := handler.Subscribe(ctx, &tc.topic)
assert.Contains(t, logBuffer.String(), tc.logMsg) assert.Contains(t, logBuffer.String(), tc.logMsg)
assert.Equal(t, tc.err, err)
} }
} }
@ -377,12 +385,13 @@ func TestUnsubscribe(t *testing.T) {
session *session.Session session *session.Session
topic []string topic []string
logMsg string logMsg string
err error
}{ }{
{ {
desc: "unsubscribe without active session", desc: "unsubscribe without active session",
session: nil, session: nil,
topic: topics, topic: topics,
logMsg: errors.Wrap(mqtt.ErrFailedUnsubscribe, mqtt.ErrClientNotInitialized).Error(), err: errors.Wrap(mqtt.ErrFailedUnsubscribe, mqtt.ErrClientNotInitialized),
}, },
{ {
desc: "unsubscribe with valid session and topics", desc: "unsubscribe with valid session and topics",
@ -397,8 +406,9 @@ func TestUnsubscribe(t *testing.T) {
if tc.session != nil { if tc.session != nil {
ctx = session.NewContext(ctx, tc.session) ctx = session.NewContext(ctx, tc.session)
} }
handler.Unsubscribe(ctx, &tc.topic) err := handler.Unsubscribe(ctx, &tc.topic)
assert.Contains(t, logBuffer.String(), tc.logMsg) assert.Contains(t, logBuffer.String(), tc.logMsg)
assert.Equal(t, tc.err, err)
} }
} }
@ -411,18 +421,19 @@ func TestDisconnect(t *testing.T) {
session *session.Session session *session.Session
topic []string topic []string
logMsg string logMsg string
err error
}{ }{
{ {
desc: "disconnect without active session", desc: "disconnect without active session",
session: nil, session: nil,
topic: topics, topic: topics,
logMsg: errors.Wrap(mqtt.ErrFailedDisconnect, mqtt.ErrClientNotInitialized).Error(), err: errors.Wrap(mqtt.ErrFailedDisconnect, mqtt.ErrClientNotInitialized),
}, },
{ {
desc: "disconnect with valid session", desc: "disconnect with valid session",
session: &sessionClient, session: &sessionClient,
topic: topics, topic: topics,
logMsg: mqtt.ErrClientNotInitialized.Error(), err: nil,
}, },
} }
@ -431,8 +442,9 @@ func TestDisconnect(t *testing.T) {
if tc.session != nil { if tc.session != nil {
ctx = session.NewContext(ctx, tc.session) ctx = session.NewContext(ctx, tc.session)
} }
handler.Disconnect(ctx) err := handler.Disconnect(ctx)
assert.Contains(t, logBuffer.String(), tc.logMsg) assert.Contains(t, logBuffer.String(), tc.logMsg)
assert.Equal(t, tc.err, err)
} }
} }

View File

@ -1,11 +1,11 @@
// Copyright (c) Mainflux // Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package tracing provides tracing instrumentation for Mainflux WebSocket adapter service. // Package tracing provides tracing instrumentation for Mainflux MQTT adapter service.
// //
// This package provides tracing middleware for Mainflux WebSocket adapter service. // This package provides tracing middleware for Mainflux MQTT adapter service.
// It can be used to trace incoming requests and add tracing capabilities to // It can be used to trace incoming requests and add tracing capabilities to
// Mainflux WebSocket adapter service. // Mainflux MQTT adapter service.
// //
// For more details about tracing instrumentation for Mainflux messaging refer // For more details about tracing instrumentation for Mainflux messaging refer
// to the documentation at https://docs.mainflux.io/tracing/. // to the documentation at https://docs.mainflux.io/tracing/.

View File

@ -81,36 +81,36 @@ func (h *handlerMiddleware) AuthSubscribe(ctx context.Context, topics *[]string)
} }
// Connect traces connect operations. // Connect traces connect operations.
func (h *handlerMiddleware) Connect(ctx context.Context) { func (h *handlerMiddleware) Connect(ctx context.Context) error {
ctx, span := h.tracer.Start(ctx, connectOP) ctx, span := h.tracer.Start(ctx, connectOP)
defer span.End() defer span.End()
h.handler.Connect(ctx) return h.handler.Connect(ctx)
} }
// Disconnect traces disconnect operations. // Disconnect traces disconnect operations.
func (h *handlerMiddleware) Disconnect(ctx context.Context) { func (h *handlerMiddleware) Disconnect(ctx context.Context) error {
ctx, span := h.tracer.Start(ctx, disconnectOP) ctx, span := h.tracer.Start(ctx, disconnectOP)
defer span.End() defer span.End()
h.handler.Disconnect(ctx) return h.handler.Disconnect(ctx)
} }
// Publish traces publish operations. // Publish traces publish operations.
func (h *handlerMiddleware) Publish(ctx context.Context, topic *string, payload *[]byte) { func (h *handlerMiddleware) Publish(ctx context.Context, topic *string, payload *[]byte) error {
ctx, span := h.tracer.Start(ctx, publishOP) ctx, span := h.tracer.Start(ctx, publishOP)
defer span.End() defer span.End()
h.handler.Publish(ctx, topic, payload) return h.handler.Publish(ctx, topic, payload)
} }
// Subscribe traces subscribe operations. // Subscribe traces subscribe operations.
func (h *handlerMiddleware) Subscribe(ctx context.Context, topics *[]string) { func (h *handlerMiddleware) Subscribe(ctx context.Context, topics *[]string) error {
ctx, span := h.tracer.Start(ctx, subscribeOP) ctx, span := h.tracer.Start(ctx, subscribeOP)
defer span.End() defer span.End()
h.handler.Subscribe(ctx, topics) return h.handler.Subscribe(ctx, topics)
} }
// Unsubscribe traces unsubscribe operations. // Unsubscribe traces unsubscribe operations.
func (h *handlerMiddleware) Unsubscribe(ctx context.Context, topics *[]string) { func (h *handlerMiddleware) Unsubscribe(ctx context.Context, topics *[]string) error {
ctx, span := h.tracer.Start(ctx, unsubscribeOP) ctx, span := h.tracer.Start(ctx, unsubscribeOP)
defer span.End() defer span.End()
h.handler.Unsubscribe(ctx, topics) return h.handler.Unsubscribe(ctx, topics)
} }

View File

@ -17,17 +17,17 @@ type Handler interface {
AuthSubscribe(ctx context.Context, topics *[]string) error AuthSubscribe(ctx context.Context, topics *[]string) error
// After client successfully connected // After client successfully connected
Connect(ctx context.Context) Connect(ctx context.Context) error
// After client successfully published // After client successfully published
Publish(ctx context.Context, topic *string, payload *[]byte) Publish(ctx context.Context, topic *string, payload *[]byte) error
// After client successfully subscribed // After client successfully subscribed
Subscribe(ctx context.Context, topics *[]string) Subscribe(ctx context.Context, topics *[]string) error
// After client unsubscribed // After client unsubscribed
Unsubscribe(ctx context.Context, topics *[]string) Unsubscribe(ctx context.Context, topics *[]string) error
// Disconnect on connection with client lost // Disconnect on connection with client lost
Disconnect(ctx context.Context) Disconnect(ctx context.Context) error
} }

View File

@ -67,7 +67,9 @@ func stream(ctx context.Context, dir direction, r, w net.Conn, h Handler, errs c
} }
if dir == up { if dir == up {
notify(ctx, pkt, h) if err := notify(ctx, pkt, h); err != nil {
errs <- wrap(ctx, err, dir)
}
} }
} }
} }
@ -101,18 +103,18 @@ func authorize(ctx context.Context, pkt packets.ControlPacket, h Handler) error
} }
} }
func notify(ctx context.Context, pkt packets.ControlPacket, h Handler) { func notify(ctx context.Context, pkt packets.ControlPacket, h Handler) error {
switch p := pkt.(type) { switch p := pkt.(type) {
case *packets.ConnectPacket: case *packets.ConnectPacket:
h.Connect(ctx) return h.Connect(ctx)
case *packets.PublishPacket: case *packets.PublishPacket:
h.Publish(ctx, &p.TopicName, &p.Payload) return h.Publish(ctx, &p.TopicName, &p.Payload)
case *packets.SubscribePacket: case *packets.SubscribePacket:
h.Subscribe(ctx, &p.Topics) return h.Subscribe(ctx, &p.Topics)
case *packets.UnsubscribePacket: case *packets.UnsubscribePacket:
h.Unsubscribe(ctx, &p.Topics) return h.Unsubscribe(ctx, &p.Topics)
default: default:
return return nil
} }
} }

2
vendor/modules.txt vendored
View File

@ -430,7 +430,7 @@ github.com/magiconair/properties
# github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2 # github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2
## explicit; go 1.20 ## explicit; go 1.20
github.com/mainflux/callhome/pkg/client github.com/mainflux/callhome/pkg/client
# github.com/mainflux/mproxy v0.3.0 # github.com/mainflux/mproxy v0.3.1-0.20230822124450-4b4dfe600cc2
## explicit; go 1.19 ## explicit; go 1.19
github.com/mainflux/mproxy/pkg/logger github.com/mainflux/mproxy/pkg/logger
github.com/mainflux/mproxy/pkg/mqtt github.com/mainflux/mproxy/pkg/mqtt