Skip to content

Commit

Permalink
feat: out of band messaging
Browse files Browse the repository at this point in the history
- message service command controller to use messanger api
- send message to support out of band messages
- Part of hyperledger-archives#1058

Signed-off-by: sudesh.shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed Mar 9, 2020
1 parent 70fe0cc commit dd199e9
Show file tree
Hide file tree
Showing 22 changed files with 473 additions and 58 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ linters-settings:
- unnamedResult
- whyNoLint # TODO enable.
funlen:
lines: 60
lines: 100
statements: 40
wsl:
strict-append: true
Expand Down
80 changes: 62 additions & 18 deletions pkg/controller/command/messaging/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import (
"fmt"
"io"

"github.com/hyperledger/aries-framework-go/pkg/internal/logutil"

"github.com/hyperledger/aries-framework-go/pkg/common/log"
"github.com/hyperledger/aries-framework-go/pkg/controller/command"
"github.com/hyperledger/aries-framework-go/pkg/controller/internal/cmdutil"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/dispatcher"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/messaging/service/http"
"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdri"
"github.com/hyperledger/aries-framework-go/pkg/internal/logutil"
"github.com/hyperledger/aries-framework-go/pkg/kms/legacykms"
"github.com/hyperledger/aries-framework-go/pkg/storage"
"github.com/hyperledger/aries-framework-go/pkg/store/connection"
Expand All @@ -40,7 +39,7 @@ const (
errMsgDestinationMissing = "missing message destination"
errMsgDestSvcEndpointMissing = "missing service endpoint in message destination"
errMsgDestSvcEndpointKeysMissing = "missing service endpoint recipient/routing keys in message destination"
errMsgConnectionMatchingDIDNotFound = "unable to find connection matching theirDID[%s]"
errMsgConnectionMatchingDIDNotFound = "unable to find connection matching DID"
errMsgIDEmpty = "empty message ID"

// command methods
Expand All @@ -54,6 +53,7 @@ const (
// log constants
connectionIDString = "connectionID"
destinationString = "destination"
destinationDID = "destinationDID"
successString = "success"
)

Expand All @@ -75,10 +75,14 @@ const (
SendMsgReplyError
)

// errConnForDIDNotFound when matching connection ID not found
var errConnForDIDNotFound = fmt.Errorf(errMsgConnectionMatchingDIDNotFound)

// provider contains dependencies for the messaging controller command operations
// and is typically created by using aries.Context()
type provider interface {
OutboundDispatcher() dispatcher.Outbound
VDRIRegistry() vdri.Registry
Messenger() service.Messenger
TransientStorageProvider() storage.Provider
StorageProvider() storage.Provider
LegacyKMS() legacykms.KeyManager
Expand Down Expand Up @@ -207,20 +211,22 @@ func (o *Command) Send(rw io.Writer, req io.Reader) command.Error {
return command.NewExecuteError(SendMsgError, err)
}

return o.sendMessageToConnection(request.MessageBody, conn)
return o.sendToConnection(request.MessageBody, conn)
}

if request.TheirDID != "" {
conn, err := o.getConnectionByTheirDID(request.TheirDID)
if err != nil {
if err != nil && err != errConnForDIDNotFound {
logutil.LogError(logger, commandName, sendNewMessageCommandMethod, err.Error())
return command.NewExecuteError(SendMsgError, err)
}

return o.sendMessageToConnection(request.MessageBody, conn)
if conn != nil {
return o.sendToConnection(request.MessageBody, conn)
}
}

return o.sendMessageToDestination(request.MessageBody, request.ServiceEndpointDestination)
return o.sendToDestination(&request)
}

// Reply sends reply to existing message
Expand Down Expand Up @@ -325,11 +331,18 @@ func (o *Command) getConnectionByTheirDID(theirDID string) (*connection.Record,
}
}

return nil, fmt.Errorf(errMsgConnectionMatchingDIDNotFound, theirDID)
return nil, errConnForDIDNotFound
}

func (o *Command) sendMessageToConnection(msg json.RawMessage, conn *connection.Record) command.Error {
err := o.ctx.OutboundDispatcher().SendToDID(msg, conn.MyDID, conn.TheirDID)
func (o *Command) sendToConnection(msg json.RawMessage, conn *connection.Record) command.Error {
didcommMsg, err := service.ParseDIDCommMsgMap(msg)
if err != nil {
logutil.LogError(logger, commandName, sendNewMessageCommandMethod, err.Error(),
logutil.CreateKeyValueString(connectionIDString, conn.ConnectionID))
return command.NewExecuteError(SendMsgError, err)
}

err = o.ctx.Messenger().Send(didcommMsg, conn.MyDID, conn.TheirDID)
if err != nil {
logutil.LogError(logger, commandName, sendNewMessageCommandMethod, err.Error(),
logutil.CreateKeyValueString(connectionIDString, conn.ConnectionID))
Expand All @@ -342,7 +355,35 @@ func (o *Command) sendMessageToConnection(msg json.RawMessage, conn *connection.
return nil
}

func (o *Command) sendMessageToDestination(msg json.RawMessage, dest *ServiceEndpointDestinationParams) command.Error {
func (o *Command) sendToDestination(rqst *SendNewMessageArgs) command.Error {
var dest *service.Destination

var err error

// prepare destination
if rqst.TheirDID != "" {
dest, err = service.GetDestination(rqst.TheirDID, o.ctx.VDRIRegistry())

if err != nil {
logutil.LogError(logger, commandName, sendNewMessageCommandMethod, err.Error(),
logutil.CreateKeyValueString(destinationDID, rqst.TheirDID))

return command.NewExecuteError(SendMsgError, err)
}
} else if rqst.ServiceEndpointDestination != nil {
dest = &service.Destination{
RoutingKeys: rqst.ServiceEndpointDestination.RoutingKeys,
RecipientKeys: rqst.ServiceEndpointDestination.RecipientKeys,
ServiceEndpoint: rqst.ServiceEndpointDestination.ServiceEndpoint,
}
}

if dest == nil {
logutil.LogError(logger, commandName, sendNewMessageCommandMethod, errMsgDestinationMissing)

return command.NewExecuteError(SendMsgError, fmt.Errorf(errMsgDestinationMissing))
}

_, sigPubKey, err := o.ctx.LegacyKMS().CreateKeySet()
if err != nil {
logutil.LogError(logger, commandName, sendNewMessageCommandMethod, err.Error(),
Expand All @@ -351,11 +392,14 @@ func (o *Command) sendMessageToDestination(msg json.RawMessage, dest *ServiceEnd
return command.NewExecuteError(SendMsgError, err)
}

err = o.ctx.OutboundDispatcher().Send(msg, sigPubKey, &service.Destination{
RoutingKeys: dest.RoutingKeys,
RecipientKeys: dest.RecipientKeys,
ServiceEndpoint: dest.ServiceEndpoint,
})
didcommMsg, err := service.ParseDIDCommMsgMap(rqst.MessageBody)
if err != nil {
logutil.LogError(logger, commandName, sendNewMessageCommandMethod, err.Error(),
logutil.CreateKeyValueString(destinationString, dest.ServiceEndpoint))
return command.NewExecuteError(SendMsgError, err)
}

err = o.ctx.Messenger().SendToDestination(didcommMsg, sigPubKey, dest)
if err != nil {
logutil.LogError(logger, commandName, sendNewMessageCommandMethod, err.Error(),
logutil.CreateKeyValueString(destinationString, dest.ServiceEndpoint))
Expand Down
66 changes: 59 additions & 7 deletions pkg/controller/command/messaging/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ import (
"github.com/hyperledger/aries-framework-go/pkg/controller/internal/mocks/webhook"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/dispatcher"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/messaging/service/http"
mockdispatcher "github.com/hyperledger/aries-framework-go/pkg/internal/mock/didcomm/dispatcher"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdri"
"github.com/hyperledger/aries-framework-go/pkg/internal/mock/didcomm/msghandler"
"github.com/hyperledger/aries-framework-go/pkg/internal/mock/didcomm/protocol"
"github.com/hyperledger/aries-framework-go/pkg/internal/mock/didcomm/protocol/generic"
mocksvc "github.com/hyperledger/aries-framework-go/pkg/internal/mock/didcomm/service"
mockdiddoc "github.com/hyperledger/aries-framework-go/pkg/mock/diddoc"
mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms/legacykms"
"github.com/hyperledger/aries-framework-go/pkg/mock/storage"
mockvdri "github.com/hyperledger/aries-framework-go/pkg/mock/vdri"
"github.com/hyperledger/aries-framework-go/pkg/store/connection"
)

Expand Down Expand Up @@ -466,8 +470,9 @@ func TestCommand_Send(t *testing.T) {
tests := []struct {
name string
testConnection *connection.Record
messenger *mockdispatcher.MockOutbound
messenger *mocksvc.MockMessenger
kms *mockkms.CloseableKMS
vdri *mockvdri.MockVDRIRegistry
requestJSON string
errorCode command.Code
errorMsg string
Expand All @@ -484,7 +489,7 @@ func TestCommand_Send(t *testing.T) {
testConnection: &connection.Record{ConnectionID: "sample-conn-ID-001",
State: "completed", MyDID: "mydid", TheirDID: "theirDID-001"},
requestJSON: `{"message_body": {"text":"sample"}, "connection_id": "sample-conn-ID-001"}`,
messenger: &mockdispatcher.MockOutbound{SendErr: fmt.Errorf("sample-err-01")},
messenger: &mocksvc.MockMessenger{ErrSend: fmt.Errorf("sample-err-01")},
errorCode: SendMsgError,
errorMsg: "sample-err-01",
},
Expand All @@ -494,13 +499,13 @@ func TestCommand_Send(t *testing.T) {
State: "completed", MyDID: "mydid", TheirDID: "theirDID-z"},
requestJSON: `{"message_body": {"text":"sample"}, "their_did": "theirDID-001"}`,
errorCode: SendMsgError,
errorMsg: "unable to find connection matching theirDID",
errorMsg: "DID not found",
},
{
name: "send message to destination",
requestJSON: `{"message_body": {"text":"sample"},"service_endpoint": {"serviceEndpoint": "sdfsdf",
"recipientKeys":["test"]}}`,
messenger: &mockdispatcher.MockOutbound{SendErr: fmt.Errorf("sample-err-01")},
messenger: &mocksvc.MockMessenger{ErrSendToDestination: fmt.Errorf("sample-err-01")},
errorCode: SendMsgError,
errorMsg: "sample-err-01",
},
Expand All @@ -512,6 +517,32 @@ func TestCommand_Send(t *testing.T) {
errorCode: SendMsgError,
errorMsg: "sample-kmserr-01",
},
{
name: "failed to resolve destination from DID",
requestJSON: `{"message_body": {"text":"sample"}, "their_did": "theirDID-001"}`,
vdri: &mockvdri.MockVDRIRegistry{ResolveErr: fmt.Errorf("sample-err-01")},
errorCode: SendMsgError,
errorMsg: "sample-err-01",
},
{
name: "invalid message body - scenario 1",
requestJSON: `{"message_body": "sample-input", "their_did": "theirDID-001"}`,
vdri: &mockvdri.MockVDRIRegistry{
ResolveFunc: func(didID string, opts ...vdri.ResolveOpts) (doc *did.Doc, e error) {
return mockdiddoc.GetMockDIDDoc(), nil
},
},
errorCode: SendMsgError,
errorMsg: "invalid payload data format",
},
{
name: "invalid message body - scenario 2",
testConnection: &connection.Record{ConnectionID: "sample-conn-ID-001",
State: "completed", MyDID: "mydid", TheirDID: "theirDID-001"},
requestJSON: `{"message_body": "sample-input", "connection_id": "sample-conn-ID-001"}`,
errorCode: SendMsgError,
errorMsg: "invalid payload data format",
},
}

t.Parallel()
Expand All @@ -530,13 +561,17 @@ func TestCommand_Send(t *testing.T) {
}

if tc.messenger != nil {
provider.CustomOutbound = tc.messenger
provider.CustomMessenger = tc.messenger
}

if tc.kms != nil {
provider.CustomKMS = tc.kms
}

if tc.vdri != nil {
provider.CustomVDRI = tc.vdri
}

cmd, err := New(provider, msghandler.NewMockMsgServiceProvider(), webhook.NewMockWebhookNotifier())
require.NoError(t, err)
require.NotNil(t, cmd)
Expand All @@ -553,7 +588,7 @@ func TestCommand_Send(t *testing.T) {
})
}

func TestOperation_Reply(t *testing.T) {
func TestCommand_Reply(t *testing.T) {
t.Run("Test input args validation", func(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -622,3 +657,20 @@ func TestOperation_Reply(t *testing.T) {
require.Contains(t, cmdErr.Error(), "to be implemented")
})
}

func TestCommand_SendToDestinationFailures(t *testing.T) {
prov := &protocol.MockProvider{}
prov.CustomVDRI = &mockvdri.MockVDRIRegistry{
ResolveFunc: func(didID string, opts ...vdri.ResolveOpts) (doc *did.Doc, e error) {
return mockdiddoc.GetMockDIDDoc(), nil
},
}
cmd, err := New(prov, msghandler.NewMockMsgServiceProvider(), webhook.NewMockWebhookNotifier())

require.NoError(t, err)
require.NotNil(t, cmd)

cErr := cmd.sendToDestination(&SendNewMessageArgs{})
require.Error(t, cErr)
require.Equal(t, cErr.Error(), errMsgDestinationMissing)
}
6 changes: 4 additions & 2 deletions pkg/controller/rest/messaging/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import (
"github.com/hyperledger/aries-framework-go/pkg/controller/command/messaging"
"github.com/hyperledger/aries-framework-go/pkg/controller/internal/cmdutil"
"github.com/hyperledger/aries-framework-go/pkg/controller/rest"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/dispatcher"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdri"
"github.com/hyperledger/aries-framework-go/pkg/kms/legacykms"
"github.com/hyperledger/aries-framework-go/pkg/storage"
)
Expand All @@ -36,7 +37,8 @@ const (
// provider contains dependencies for the common controller operations
// and is typically created by using aries.Context()
type provider interface {
OutboundDispatcher() dispatcher.Outbound
VDRIRegistry() vdri.Registry
Messenger() service.Messenger
TransientStorageProvider() storage.Provider
StorageProvider() storage.Provider
LegacyKMS() legacykms.KeyManager
Expand Down

0 comments on commit dd199e9

Please sign in to comment.