From 7a46df5443ae77f60440dd612a27d60964bc0eb3 Mon Sep 17 00:00:00 2001 From: Alex Shorsher Date: Sun, 6 Mar 2022 14:30:11 -0500 Subject: [PATCH 1/2] add `fetchreferences` param to events API `fetchreferences` will add the referred object to the GET /events response Signed-off-by: Alex Shorsher --- docs/swagger/swagger.yaml | 6 ++ internal/apiserver/route_get_events.go | 8 +- internal/apiserver/route_get_events_test.go | 24 ++++++ internal/orchestrator/data_query.go | 18 +++++ internal/orchestrator/data_query_test.go | 89 +++++++++++++++++++++ internal/orchestrator/orchestrator.go | 4 + internal/orchestrator/orchestrator_test.go | 4 + mocks/orchestratormocks/orchestrator.go | 32 ++++++++ 8 files changed, 184 insertions(+), 1 deletion(-) diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 3a2850d0d3..a64851fe2c 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -3693,6 +3693,12 @@ paths: schema: example: default type: string + - description: 'TODO: Description' + in: query + name: fetchreferences + schema: + example: "true" + type: string - description: Server-side request timeout (millseconds, or set a custom suffix like 10s) in: header diff --git a/internal/apiserver/route_get_events.go b/internal/apiserver/route_get_events.go index 4aca371a67..76b669eb73 100644 --- a/internal/apiserver/route_get_events.go +++ b/internal/apiserver/route_get_events.go @@ -18,6 +18,7 @@ package apiserver import ( "net/http" + "strings" "github.com/hyperledger/firefly/internal/config" "github.com/hyperledger/firefly/internal/i18n" @@ -33,13 +34,18 @@ var getEvents = &oapispec.Route{ PathParams: []*oapispec.PathParam{ {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, }, - QueryParams: nil, + QueryParams: []*oapispec.QueryParam{ + {Name: "fetchreferences", Example: "true", Description: i18n.MsgTBD, IsBool: true}, + }, FilterFactory: database.EventQueryFactory, Description: i18n.MsgTBD, JSONInputValue: nil, JSONOutputValue: func() interface{} { return []*fftypes.Event{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { + if strings.EqualFold(r.QP["fetchreferences"], "true") { + return filterResult(getOr(r.Ctx).GetEventsWithReferences(r.Ctx, r.PP["ns"], r.Filter)) + } return filterResult(getOr(r.Ctx).GetEvents(r.Ctx, r.PP["ns"], r.Filter)) }, } diff --git a/internal/apiserver/route_get_events_test.go b/internal/apiserver/route_get_events_test.go index 775c822646..864c77408a 100644 --- a/internal/apiserver/route_get_events_test.go +++ b/internal/apiserver/route_get_events_test.go @@ -17,9 +17,11 @@ package apiserver import ( + "encoding/json" "net/http/httptest" "testing" + "github.com/hyperledger/firefly/pkg/database" "github.com/hyperledger/firefly/pkg/fftypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -37,3 +39,25 @@ func TestGetEvents(t *testing.T) { assert.Equal(t, 200, res.Result().StatusCode) } + +func TestGetEventsWithReferences(t *testing.T) { + o, r := newTestAPIServer() + req := httptest.NewRequest("GET", "/api/v1/namespaces/mynamespace/events?fetchreferences", nil) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + res := httptest.NewRecorder() + + var ten int64 = 10 + o.On("GetEventsWithReferences", mock.Anything, "mynamespace", mock.Anything). + Return([]*fftypes.EnrichedEvent{}, &database.FilterResult{ + TotalCount: &ten, + }, nil) + r.ServeHTTP(res, req) + + assert.Equal(t, 200, res.Result().StatusCode) + var resWithCount filterResultsWithCount + err := json.NewDecoder(res.Body).Decode(&resWithCount) + assert.NoError(t, err) + assert.NotNil(t, resWithCount.Items) + assert.Equal(t, int64(0), resWithCount.Count) + assert.Equal(t, int64(10), resWithCount.Total) +} diff --git a/internal/orchestrator/data_query.go b/internal/orchestrator/data_query.go index 2251262d69..8c94295296 100644 --- a/internal/orchestrator/data_query.go +++ b/internal/orchestrator/data_query.go @@ -307,3 +307,21 @@ func (or *orchestrator) GetTransactionBlockchainEvents(ctx context.Context, ns, ) return or.database.GetBlockchainEvents(ctx, filter) } + +func (or *orchestrator) GetEventsWithReferences(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.EnrichedEvent, *database.FilterResult, error) { + filter = or.scopeNS(ns, filter) + events, fr, err := or.database.GetEvents(ctx, filter) + if err != nil { + return nil, nil, err + } + + enriched := make([]*fftypes.EnrichedEvent, len(events)) + for i, event := range events { + enrichedEvent, err := or.txHelper.EnrichEvent(or.ctx, event) + if err != nil { + return nil, nil, err + } + enriched[i] = enrichedEvent + } + return enriched, fr, err +} diff --git a/internal/orchestrator/data_query_test.go b/internal/orchestrator/data_query_test.go index 16218dd6c8..7f0565f014 100644 --- a/internal/orchestrator/data_query_test.go +++ b/internal/orchestrator/data_query_test.go @@ -571,6 +571,95 @@ func TestGetEvents(t *testing.T) { assert.NoError(t, err) } +func TestGetEventsWithReferencesFail(t *testing.T) { + or := newTestOrchestrator() + u := fftypes.NewUUID() + or.mdi.On("GetEvents", mock.Anything, mock.Anything).Return(nil, nil, fmt.Errorf("pop")) + fb := database.EventQueryFactory.NewFilter(context.Background()) + f := fb.And(fb.Eq("id", u)) + _, _, err := or.GetEventsWithReferences(context.Background(), "ns1", f) + assert.EqualError(t, err, "pop") +} + +func TestGetEventsWithReferences(t *testing.T) { + or := newTestOrchestrator() + u := fftypes.NewUUID() + + // Setup the IDs + ref1 := fftypes.NewUUID() + ev1 := fftypes.NewUUID() + ref2 := fftypes.NewUUID() + ev2 := fftypes.NewUUID() + ref3 := fftypes.NewUUID() + ev3 := fftypes.NewUUID() + + blockchainEvent := &fftypes.Event{ + ID: ev1, + Sequence: 10000001, + Reference: ref1, + Type: fftypes.EventTypeBlockchainEventReceived, + } + + txEvent := &fftypes.Event{ + ID: ev2, + Sequence: 10000002, + Reference: ref2, + Type: fftypes.EventTypeTransactionSubmitted, + } + + msgEvent := &fftypes.Event{ + ID: ev3, + Sequence: 10000003, + Reference: ref3, + Type: fftypes.EventTypeMessageConfirmed, + } + + or.mth.On("EnrichEvent", mock.Anything, blockchainEvent).Return(&fftypes.EnrichedEvent{ + Event: *blockchainEvent, + BlockchainEvent: &fftypes.BlockchainEvent{ + ID: ref1, + }, + }, nil) + + or.mth.On("EnrichEvent", mock.Anything, txEvent).Return(&fftypes.EnrichedEvent{ + Event: *txEvent, + Transaction: &fftypes.Transaction{ + ID: ref2, + }, + }, nil) + + or.mth.On("EnrichEvent", mock.Anything, msgEvent).Return(&fftypes.EnrichedEvent{ + Event: *msgEvent, + Message: &fftypes.Message{ + Header: fftypes.MessageHeader{ + ID: ref3, + }, + }, + }, nil) + + or.mdi.On("GetEvents", mock.Anything, mock.Anything).Return([]*fftypes.Event{ + blockchainEvent, + txEvent, + msgEvent, + }, nil, nil) + fb := database.EventQueryFactory.NewFilter(context.Background()) + f := fb.And(fb.Eq("id", u)) + _, _, err := or.GetEventsWithReferences(context.Background(), "ns1", f) + assert.NoError(t, err) +} + +func TestGetEventsWithReferencesEnrichFail(t *testing.T) { + or := newTestOrchestrator() + u := fftypes.NewUUID() + + or.mdi.On("GetEvents", mock.Anything, mock.Anything).Return([]*fftypes.Event{{ID: fftypes.NewUUID()}}, nil, nil) + or.mth.On("EnrichEvent", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) + fb := database.EventQueryFactory.NewFilter(context.Background()) + f := fb.And(fb.Eq("id", u)) + _, _, err := or.GetEventsWithReferences(context.Background(), "ns1", f) + assert.EqualError(t, err, "pop") +} + func TestGetBlockchainEventByID(t *testing.T) { or := newTestOrchestrator() diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go index 1492f40cde..c6d0ea9cd4 100644 --- a/internal/orchestrator/orchestrator.go +++ b/internal/orchestrator/orchestrator.go @@ -43,6 +43,7 @@ import ( "github.com/hyperledger/firefly/internal/sharedstorage/ssfactory" "github.com/hyperledger/firefly/internal/syncasync" "github.com/hyperledger/firefly/internal/tokens/tifactory" + "github.com/hyperledger/firefly/internal/txcommon" "github.com/hyperledger/firefly/pkg/blockchain" "github.com/hyperledger/firefly/pkg/database" "github.com/hyperledger/firefly/pkg/dataexchange" @@ -118,6 +119,7 @@ type Orchestrator interface { GetOperations(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Operation, *database.FilterResult, error) GetEventByID(ctx context.Context, ns, id string) (*fftypes.Event, error) GetEvents(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Event, *database.FilterResult, error) + GetEventsWithReferences(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.EnrichedEvent, *database.FilterResult, error) GetBlockchainEventByID(ctx context.Context, id *fftypes.UUID) (*fftypes.BlockchainEvent, error) GetBlockchainEvents(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.BlockchainEvent, *database.FilterResult, error) @@ -163,6 +165,7 @@ type orchestrator struct { node *fftypes.UUID metrics metrics.Manager operations operations.Manager + txHelper txcommon.Helper } func NewOrchestrator() Orchestrator { @@ -193,6 +196,7 @@ func (or *orchestrator) Init(ctx context.Context, cancelCtx context.CancelFunc) if err == nil { err = or.initNamespaces(ctx) } + or.txHelper = txcommon.NewTransactionHelper(or.database) // Bind together the blockchain interface callbacks, with the events manager or.bc.bi = or.blockchain or.bc.ei = or.events diff --git a/internal/orchestrator/orchestrator_test.go b/internal/orchestrator/orchestrator_test.go index 7ca4ebb6b2..7a7dd6950a 100644 --- a/internal/orchestrator/orchestrator_test.go +++ b/internal/orchestrator/orchestrator_test.go @@ -43,6 +43,7 @@ import ( "github.com/hyperledger/firefly/mocks/privatemessagingmocks" "github.com/hyperledger/firefly/mocks/sharedstoragemocks" "github.com/hyperledger/firefly/mocks/tokenmocks" + "github.com/hyperledger/firefly/mocks/txcommonmocks" "github.com/hyperledger/firefly/pkg/fftypes" "github.com/hyperledger/firefly/pkg/tokens" "github.com/stretchr/testify/assert" @@ -72,6 +73,7 @@ type testOrchestrator struct { mmi *metricsmocks.Manager mom *operationmocks.Manager mbp *batchpinmocks.Submitter + mth *txcommonmocks.Helper } func newTestOrchestrator() *testOrchestrator { @@ -100,6 +102,7 @@ func newTestOrchestrator() *testOrchestrator { mmi: &metricsmocks.Manager{}, mom: &operationmocks.Manager{}, mbp: &batchpinmocks.Submitter{}, + mth: &txcommonmocks.Helper{}, } tor.orchestrator.database = tor.mdi tor.orchestrator.data = tor.mdm @@ -119,6 +122,7 @@ func newTestOrchestrator() *testOrchestrator { tor.orchestrator.metrics = tor.mmi tor.orchestrator.operations = tor.mom tor.orchestrator.batchpin = tor.mbp + tor.orchestrator.txHelper = tor.mth tor.mdi.On("Name").Return("mock-di").Maybe() tor.mem.On("Name").Return("mock-ei").Maybe() tor.mps.On("Name").Return("mock-ps").Maybe() diff --git a/mocks/orchestratormocks/orchestrator.go b/mocks/orchestratormocks/orchestrator.go index 4a744a50f1..a71f4acc36 100644 --- a/mocks/orchestratormocks/orchestrator.go +++ b/mocks/orchestratormocks/orchestrator.go @@ -598,6 +598,38 @@ func (_m *Orchestrator) GetEvents(ctx context.Context, ns string, filter databas return r0, r1, r2 } +// GetEventsWithReferences provides a mock function with given fields: ctx, ns, filter +func (_m *Orchestrator) GetEventsWithReferences(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.EnrichedEvent, *database.FilterResult, error) { + ret := _m.Called(ctx, ns, filter) + + var r0 []*fftypes.EnrichedEvent + if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*fftypes.EnrichedEvent); ok { + r0 = rf(ctx, ns, filter) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*fftypes.EnrichedEvent) + } + } + + var r1 *database.FilterResult + if rf, ok := ret.Get(1).(func(context.Context, string, database.AndFilter) *database.FilterResult); ok { + r1 = rf(ctx, ns, filter) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*database.FilterResult) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, database.AndFilter) error); ok { + r2 = rf(ctx, ns, filter) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // GetMessageByID provides a mock function with given fields: ctx, ns, id func (_m *Orchestrator) GetMessageByID(ctx context.Context, ns string, id string) (*fftypes.Message, error) { ret := _m.Called(ctx, ns, id) From a35986bbb4141ad13a184a113e85341c4129de99 Mon Sep 17 00:00:00 2001 From: Alex Shorsher Date: Wed, 9 Mar 2022 14:48:26 -0500 Subject: [PATCH 2/2] update docs w/ discord and subscription filters Signed-off-by: Alex Shorsher --- docs/contributors/contributors.md | 9 +++------ docs/gettingstarted/events.md | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/contributors/contributors.md b/docs/contributors/contributors.md index 667190f077..965b7fa3e9 100644 --- a/docs/contributors/contributors.md +++ b/docs/contributors/contributors.md @@ -22,12 +22,9 @@ We welcome anyone to contribute to the FireFly project! If you're interested, th --- -## 🚀 Connect with us on Rocket Chat -You can chat with maintainers and other contributors on Rocket Chat in the `firefly` channel: -[https://chat.hyperledger.org/channel/firefly](https://chat.hyperledger.org/channel/firefly) - -If you don't have a Linux Foundation ID, you can sign up for a free account here: -[https://wiki.hyperledger.org/display/CA/Setting+up+an+LFID](https://wiki.hyperledger.org/display/CA/Setting+up+an+LFID) +## 🚀 Connect with us on Discord +You can chat with maintainers and other contributors on Discord in the `firefly` channel: +[https://discord.gg/hyperledger](https://discord.gg/hyperledger) ## 📅 Join our Community Calls Community calls are a place to talk to other contributors, maintainers, and other people interested in FireFly. Maintainers often discuss upcoming changes and proposed new features on these calls. These calls are a great way for the community to give feedback on new ideas, ask questions about FireFly, and hear how others are using FireFly to solve real world problems. diff --git a/docs/gettingstarted/events.md b/docs/gettingstarted/events.md index ada3b30634..6ec7eee078 100644 --- a/docs/gettingstarted/events.md +++ b/docs/gettingstarted/events.md @@ -149,11 +149,20 @@ in the event. "transport": "websockets", "name": "app1", "filter": { - "author": ".*", + "blockchainevent": { + "listener": ".*", + "name": ".*" + }, "events": ".*", - "group": ".*", - "tag": ".*", - "topics": ".*" + "message": { + "author": ".*", + "group": ".*", + "tag": ".*", + "topics": ".*" + }, + "transaction": { + "type": ".*" + } }, "options": { "firstEvent": "newest",