Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

feat: Replace DIDCommMsg type with map[string]interface{} #1054

Merged
merged 1 commit into from Jan 17, 2020
Merged

feat: Replace DIDCommMsg type with map[string]interface{} #1054

merged 1 commit into from Jan 17, 2020

Conversation

soluchok
Copy link
Contributor

@soluchok soluchok commented Jan 10, 2020

The intention of this PR is to generalize all messages in the system/framework. Because our messages are JSON based messages the structure map[string]interface is enough to make messages with the same type. The benefit of this can be:

  • we cannot send an array/string etc.
  • the new message type provides common functions ID(),Type() etc.
  • message can be modified directly by key (without knowing the structure)

Since the message is a map, it can be modified by some other package/client. To prevent this we need to expose only the message interface. The interface provides Type and Scan functions. Having these functions any package/client may convert a message into a specific structure.

type DIDMsg interface {
	ID() string
	Type() string

	ThreadID() (string, error)
	Scan(v interface{}) error
}

The usage:
In our current logic, we send events to the client/user. DIDCommAction and StateMsg accordingly. In that case, we cannot expose the message to the client. Because we are using DIDCommMsg message in our services (background handling e.g Continue function). So, instead of exposing DIDCommMsg we should expose an interface DIDMsg.

Closes #1065 #1039

@codecov
Copy link

codecov bot commented Jan 14, 2020

Codecov Report

Merging #1054 into master will decrease coverage by 0.06%.
The diff coverage is 94.8%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1054      +/-   ##
==========================================
- Coverage   91.14%   91.08%   -0.07%     
==========================================
  Files         102      102              
  Lines        6446     6469      +23     
==========================================
+ Hits         5875     5892      +17     
- Misses        311      314       +3     
- Partials      260      263       +3
Impacted Files Coverage Δ
pkg/didcomm/protocol/introduce/models.go 100% <ø> (ø) ⬆️
pkg/didcomm/common/service/errors.go 100% <ø> (ø) ⬆️
pkg/didcomm/common/service/event.go 100% <ø> (ø) ⬆️
pkg/restapi/operation/common/msgservice.go 92.59% <100%> (ø) ⬆️
pkg/didcomm/protocol/didexchange/states.go 94.87% <100%> (ø) ⬆️
pkg/didcomm/protocol/route/service.go 89.22% <100%> (ø) ⬆️
pkg/framework/context/context.go 98.26% <71.42%> (-1.74%) ⬇️
pkg/didcomm/protocol/introduce/service.go 91.48% <90%> (-0.66%) ⬇️
pkg/didcomm/protocol/introduce/states.go 95.6% <91.66%> (ø) ⬆️
pkg/didcomm/common/service/did_comm_msg.go 95.74% <95.74%> (ø)
... and 2 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f2b36a0...7f3d63b. Read the comment docs.

@soluchok soluchok changed the title WIP: feat: Changed DIDCommMsg type to map[string]interface{} Replace DIDCommMsg type with map[string]interface{} Jan 15, 2020
@soluchok soluchok changed the title Replace DIDCommMsg type with map[string]interface{} feat: Replace DIDCommMsg type with map[string]interface{} Jan 15, 2020
@rolsonquadras
Copy link
Contributor

rolsonquadras commented Jan 15, 2020

@soluchok It would be nice if we can describe the reason for this change. Currently, I don't see any in PR or Issue description.

pkg/didcomm/common/service/did_comm_msg.go Outdated Show resolved Hide resolved

// ID returns the message id
func (m DIDCommMsg) ID() string {
if m == nil || m[jsonID] == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if ID is ""?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ID function will return the default value. Default is "".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be invalid error if ID is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the message is generic, it doesn't make sense to return an error. The empty type should be returned. We may provide the IsValid function to check the message if it is needed. For the functions which have some logic, I agree there should be an error. e.g ThreadID function. When we just want to get the direct value, it does not make sense to return an error.

}

// ToStruct converts msh message to struct
func (m DIDCommMsg) ToStruct(v interface{}) (err error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, why can't we use "https://github.com/mitchellh/mapstructure"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapstructure not maintainable anymore mitchellh/mapstructure#145 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think it's better than rolling our own code until we have a good reason to.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@soluchok @llorllale I believe the issue is that we need omit:empty and that feature hasn't been merged into mapstructure. correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, but I think it is not a big problem

return err
}

if svc.Accept(h) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing whole message may be risky to check acceptance criteria, for now add a TODO to pass subset of DIDComm msg map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accept may use an interface e.g

type Typer interface {
   Type() string
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are also going to check it based on ~purpose.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Moopli ^^^

Comment on lines +116 to +96
if !ok {
return ""
}
Copy link
Contributor

@llorllale llorllale Jan 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're hiding an error condition here. Likewise in ID().

Not sure what to do. Adding an error return parameter would make the usage of Type() clumsy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best way is to return a default value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewDidCommMsg() should guarantee these base values exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -275,7 +273,7 @@ func (s *Service) handle(msg *message, aEvent chan<- service.DIDCommAction) erro
s.sendMsgEvents(&service.StateMsg{
ProtocolName: DIDExchange,
Type: service.PostState,
Msg: msg.Msg.Clone(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting that we now lose this aspect (no longer exporting a copy).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, I think instead of copy we may provide didcommmsg interface.

type IDIDCommMsg interface{
    ID()
    Type()
    ToStrunct()
}

copy a message is not a cheap operation. My suggestion is to provide an interface. I have not seen that we really use this message. Anyway providing an interface looks better and cheaper.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An interface or a struct is fine by me personally. Gophers would probably prefer it be a struct though.

Copy link
Contributor

@troyronda troyronda Jan 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An interface should only be returned if that is need to satisfy the signature of another interface.
Otherwise return a struct. The consuming code should accept the struct using an interface, of course.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two public artifacts is overkill in this case. Either make it a public struct or a public interface + private struct. It's immutable and the method set [ID(), Type(), ThreadID(), Scan()] to me strikes the ideal balance between size, cohesion, and generality/usefulness.

@llorllale
Copy link
Contributor

@soluchok we lost the Clone() method. Rather than recreating it, I propose we make DIDCommMsg a struct and hide the map inside it. Leave ID(), ThreadID(), and ToStruct()/Scan() as they are. We get immutability for free this way + clients can scan to their specific struct with ToStruct()/Scan()

}

// DIDCommMsg did comm msg
type DIDCommMsg map[string]interface{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably not be in common/service anymore.

It is an implementation rather than a common interface being consumed by other packages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Give me a hint of where to move it. pkg/common/didcommmsg ?

Signed-off-by: Andrii Soluk <isoluchok@gmail.com>
@troyronda troyronda merged commit 9d11c5d into hyperledger-archives:master Jan 17, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Replace DIDCommMsg type with map[string]interface{}
6 participants