Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init: Implement ADR 032 typed events #7564

Merged
merged 20 commits into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5b539e7
Add EmitTypedEvent in events
akhilkumarpilli Oct 3, 2020
ed85dd7
Add parseTypedEvent method
akhilkumarpilli Oct 5, 2020
7d9919b
Use jsonpb
anilcse Oct 6, 2020
deb1e3c
Modify unmarshal proto in events
akhilkumarpilli Oct 6, 2020
5e11d14
Merge branch 'events-adr' of github.com:akhilkumarpilli/cosmos-sdk in…
akhilkumarpilli Oct 6, 2020
171f362
Add a test for typed events
anilcse Oct 6, 2020
f158344
Fix reflect issue in parseTypedEvent
akhilkumarpilli Oct 7, 2020
faeab07
Modify event tests and add comments
akhilkumarpilli Oct 15, 2020
fe5f8ca
Merge remote-tracking branch 'upstream/master' into events-adr
akhilkumarpilli Oct 15, 2020
50f3f1f
Add EmitTypedEvents and refactor other methods
akhilkumarpilli Oct 15, 2020
45f32f8
Merge remote-tracking branch 'upstream/master' into events-adr
akhilkumarpilli Oct 15, 2020
fa6a618
Fix golangci-lint issues
akhilkumarpilli Oct 15, 2020
625501d
Merge remote-tracking branch 'upstream/master' into events-adr
akhilkumarpilli Oct 15, 2020
526d942
Update ProtoMarshalJSON params
akhilkumarpilli Oct 16, 2020
6719759
Merge remote-tracking branch 'upstream/master' into events-adr
akhilkumarpilli Oct 16, 2020
d8b6883
Merge branch 'master' into events-adr
jackzampolin Oct 23, 2020
5f472bf
Address PR comments
akhilkumarpilli Oct 27, 2020
a79c878
Merge remote-tracking branch 'upstream/master' into events-adr
akhilkumarpilli Oct 27, 2020
b16fcd0
Merge branch 'master' into events-adr
jackzampolin Oct 27, 2020
2dfd1b8
Merge branch 'master' into events-adr
mergify[bot] Oct 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions types/events.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package types

import (
"encoding/json"
"fmt"
"reflect"
"sort"
"strings"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/gogo/protobuf/jsonpb"
proto "github.com/gogo/protobuf/proto"
abci "github.com/tendermint/tendermint/abci/types"
)

Expand All @@ -25,11 +30,13 @@ func NewEventManager() *EventManager {
func (em *EventManager) Events() Events { return em.events }

// EmitEvent stores a single Event object.
// Deprecated: Use EmitTypedEvent
func (em *EventManager) EmitEvent(event Event) {
em.events = em.events.AppendEvent(event)
}

// EmitEvents stores a series of Event objects.
// Deprecated: Use EmitTypedEvents
func (em *EventManager) EmitEvents(events Events) {
em.events = em.events.AppendEvents(events)
}
Expand All @@ -39,6 +46,97 @@ func (em EventManager) ABCIEvents() []abci.Event {
return em.events.ToABCIEvents()
}

// EmitTypedEvent takes typed event and emits converting it into Event
func (em *EventManager) EmitTypedEvent(tev proto.Message) error {
event, err := TypedEventToEvent(tev)
if err != nil {
return err
}

em.EmitEvent(event)
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
return nil
}

// EmitTypedEvents takes series of typed events and emit
func (em *EventManager) EmitTypedEvents(tevs ...proto.Message) error {
events := make(Events, len(tevs))
for i, tev := range tevs {
res, err := TypedEventToEvent(tev)
if err != nil {
return err
}
events[i] = res
}

em.EmitEvents(events)
return nil
}

// TypedEventToEvent takes typed event and converts to Event object
func TypedEventToEvent(tev proto.Message) (Event, error) {
evtType := proto.MessageName(tev)
evtJSON, err := codec.ProtoMarshalJSON(tev, nil)
Copy link
Contributor

@amaury1093 amaury1093 Oct 26, 2020

Choose a reason for hiding this comment

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

I think that the nil argument won't work if tev contains an Any... or maybe it will. Just to be sure, would you mind adding a test with Any in TestEventManagerTypedEvents?

if err != nil {
return Event{}, err
}

var attrMap map[string]json.RawMessage
err = json.Unmarshal(evtJSON, &attrMap)
if err != nil {
return Event{}, err
}

attrs := make([]abci.EventAttribute, 0, len(attrMap))
for k, v := range attrMap {
attrs = append(attrs, abci.EventAttribute{
Key: []byte(k),
Value: v,
})
}

return Event{
Type: evtType,
Attributes: attrs,
}, nil
}

// ParseTypedEvent converts abci.Event back to typed event
func ParseTypedEvent(event abci.Event) (proto.Message, error) {
concreteGoType := proto.MessageType(event.Type)
if concreteGoType == nil {
return nil, fmt.Errorf("failed to retrieve the message of type %q", event.Type)
}

var value reflect.Value
if concreteGoType.Kind() == reflect.Ptr {
value = reflect.New(concreteGoType.Elem())
} else {
value = reflect.Zero(concreteGoType)
}

protoMsg, ok := value.Interface().(proto.Message)
if !ok {
return nil, fmt.Errorf("%q does not implement proto.Message", event.Type)
}

attrMap := make(map[string]json.RawMessage)
for _, attr := range event.Attributes {
attrMap[string(attr.Key)] = attr.Value
}

attrBytes, err := json.Marshal(attrMap)
if err != nil {
return nil, err
}

err = jsonpb.Unmarshal(strings.NewReader(string(attrBytes)), protoMsg)
if err != nil {
return nil, err
}

return protoMsg, nil
}

// ----------------------------------------------------------------------------
// Events
// ----------------------------------------------------------------------------
Expand Down
34 changes: 34 additions & 0 deletions types/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package types_test

import (
"encoding/json"
"reflect"
"testing"

"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
testdata "github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
)

Expand Down Expand Up @@ -68,6 +71,37 @@ func (s *eventsTestSuite) TestEventManager() {
s.Require().Equal(em.Events(), events.AppendEvent(event))
}

func (s *eventsTestSuite) TestEventManagerTypedEvents() {
em := sdk.NewEventManager()

coin := sdk.NewCoin("fakedenom", sdk.NewInt(1999999))
cat := testdata.Cat{
Moniker: "Garfield",
Lives: 6,
}
animal, err := codectypes.NewAnyWithValue(&cat)
s.Require().NoError(err)
hasAnimal := testdata.HasAnimal{
X: 1000,
Animal: animal,
}

s.Require().NoError(em.EmitTypedEvents(&coin))
s.Require().NoError(em.EmitTypedEvent(&hasAnimal))
s.Require().Len(em.Events(), 2)

msg1, err := sdk.ParseTypedEvent(em.Events().ToABCIEvents()[0])
s.Require().NoError(err)
s.Require().Equal(coin.String(), msg1.String())
s.Require().Equal(reflect.TypeOf(&coin), reflect.TypeOf(msg1))

msg2, err := sdk.ParseTypedEvent(em.Events().ToABCIEvents()[1])
s.Require().NoError(err)
s.Require().Equal(reflect.TypeOf(&hasAnimal), reflect.TypeOf(msg2))
response := msg2.(*testdata.HasAnimal)
s.Require().Equal(hasAnimal.Animal.String(), response.Animal.String())
}

func (s *eventsTestSuite) TestStringifyEvents() {
e := sdk.Events{
sdk.NewEvent("message", sdk.NewAttribute("sender", "foo")),
Expand Down