Skip to content

Commit f385056

Browse files
authored
Merge pull request #25 from GoLedgerDev/develop
Feature: Events
2 parents 09b1abc + 9cd9916 commit f385056

18 files changed

+680
-13
lines changed

assets/get.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,26 @@ func (k *Key) Get(stub *sw.StubWrapper) (*Asset, errors.ICCError) {
6464
return get(stub, pvtCollection, k.Key(), false)
6565
}
6666

67+
// GetMany fetches assets entries from write set or ledger.
68+
func GetMany(stub *sw.StubWrapper, keys []Key) ([]*Asset, errors.ICCError) {
69+
var assets []*Asset
70+
71+
for _, k := range keys {
72+
var pvtCollection string
73+
if k.IsPrivate() {
74+
pvtCollection = k.TypeTag()
75+
}
76+
77+
asset, err := get(stub, pvtCollection, k.Key(), false)
78+
if err != nil {
79+
return nil, err
80+
}
81+
assets = append(assets, asset)
82+
}
83+
84+
return assets, nil
85+
}
86+
6787
// GetCommitted fetches asset entry from ledger.
6888
func (a *Asset) GetCommitted(stub *sw.StubWrapper) (*Asset, errors.ICCError) {
6989
var pvtCollection string

assets/history.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package assets
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
7+
"github.com/goledgerdev/cc-tools/errors"
8+
sw "github.com/goledgerdev/cc-tools/stubwrapper"
9+
"github.com/hyperledger/fabric-chaincode-go/shim"
10+
pb "github.com/hyperledger/fabric-protos-go/peer"
11+
)
12+
13+
type HistoryResponse struct {
14+
Result []map[string]interface{} `json:"result"`
15+
Metadata *pb.QueryResponseMetadata `json:"metadata"`
16+
}
17+
18+
func History(stub *sw.StubWrapper, key string, resolve bool) (*HistoryResponse, errors.ICCError) {
19+
var resultsIterator shim.HistoryQueryIteratorInterface
20+
21+
resultsIterator, err := stub.GetHistoryForKey(key)
22+
if err != nil {
23+
return nil, errors.WrapErrorWithStatus(err, "failed to get history for key", http.StatusInternalServerError)
24+
}
25+
defer resultsIterator.Close()
26+
27+
historyResult := make([]map[string]interface{}, 0)
28+
29+
for resultsIterator.HasNext() {
30+
queryResponse, err := resultsIterator.Next()
31+
if err != nil {
32+
return nil, errors.WrapErrorWithStatus(err, "error iterating response", 500)
33+
}
34+
35+
var data map[string]interface{}
36+
37+
err = json.Unmarshal(queryResponse.Value, &data)
38+
if err != nil {
39+
return nil, errors.WrapErrorWithStatus(err, "failed to unmarshal queryResponse values", 500)
40+
}
41+
42+
if resolve {
43+
key, err := NewKey(data)
44+
if err != nil {
45+
return nil, errors.WrapError(err, "failed to create key object to resolve result")
46+
}
47+
asset, err := key.GetRecursive(stub)
48+
if err != nil {
49+
return nil, errors.WrapError(err, "failed to resolve result")
50+
}
51+
data = asset
52+
}
53+
54+
historyResult = append(historyResult, data)
55+
}
56+
57+
response := HistoryResponse{
58+
Result: historyResult,
59+
}
60+
61+
return &response, nil
62+
}

assets/key.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package assets
22

33
import (
44
"encoding/json"
5+
"strings"
56

67
"github.com/goledgerdev/cc-tools/errors"
78
)
@@ -49,9 +50,20 @@ func NewKey(m map[string]interface{}) (k Key, err errors.ICCError) {
4950
k[t] = v
5051
}
5152

53+
// Validate if @key corresponds to asset type
54+
key, keyExists := k["@key"]
55+
if keyExists && key != nil {
56+
_, typeExists := k["@assetType"].(string)
57+
if typeExists {
58+
index := strings.Index(k["@key"].(string), k["@assetType"].(string))
59+
if index != 0 {
60+
keyExists = false
61+
}
62+
}
63+
}
64+
5265
// Generate object key
53-
_, keyExists := k["@key"]
54-
if !keyExists {
66+
if !keyExists || key == nil {
5567
var keyStr string
5668
keyStr, err = GenerateKey(k)
5769
if err != nil {

assets/references.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,18 +127,20 @@ func (k Key) Refs(stub *sw.StubWrapper) ([]Key, errors.ICCError) {
127127
}
128128

129129
// Referrers returns an array of Keys of all the assets pointing to asset.
130-
func (a Asset) Referrers(stub *sw.StubWrapper) ([]Key, errors.ICCError) {
130+
// assetTypeFilter can be used to filter the results by asset type.
131+
func (a Asset) Referrers(stub *sw.StubWrapper, assetTypeFilter ...string) ([]Key, errors.ICCError) {
131132
assetKey := a.Key()
132-
return referrers(stub, assetKey)
133+
return referrers(stub, assetKey, assetTypeFilter)
133134
}
134135

135136
// Referrers returns an array of Keys of all the assets pointing to key.
136-
func (k Key) Referrers(stub *sw.StubWrapper) ([]Key, errors.ICCError) {
137+
// assetTypeFilter can be used to filter the results by asset type.
138+
func (k Key) Referrers(stub *sw.StubWrapper, assetTypeFilter ...string) ([]Key, errors.ICCError) {
137139
assetKey := k.Key()
138-
return referrers(stub, assetKey)
140+
return referrers(stub, assetKey, assetTypeFilter)
139141
}
140142

141-
func referrers(stub *sw.StubWrapper, assetKey string) ([]Key, errors.ICCError) {
143+
func referrers(stub *sw.StubWrapper, assetKey string, assetTypeFilter []string) ([]Key, errors.ICCError) {
142144
queryIt, err := stub.GetStateByPartialCompositeKey(assetKey, []string{})
143145
if err != nil {
144146
return nil, errors.WrapErrorWithStatus(err, "failed to check reference index", 500)
@@ -194,15 +196,28 @@ func referrers(stub *sw.StubWrapper, assetKey string) ([]Key, errors.ICCError) {
194196

195197
var ret []Key
196198
for _, retKey := range retKeys {
197-
ret = append(ret, Key{
198-
"@assetType": strings.Split(retKey, ":")[0],
199-
"@key": retKey,
200-
})
199+
assetType := strings.Split(retKey, ":")[0]
200+
if len(assetTypeFilter) <= 0 || contains(assetTypeFilter, assetType) {
201+
ret = append(ret, Key{
202+
"@assetType": strings.Split(retKey, ":")[0],
203+
"@key": retKey,
204+
})
205+
}
201206
}
202207

203208
return ret, nil
204209
}
205210

211+
func contains(s []string, str string) bool {
212+
for _, v := range s {
213+
if v == str {
214+
return true
215+
}
216+
}
217+
218+
return false
219+
}
220+
206221
// validateRefs checks if subAsset references exist in blockchain.
207222
func (a Asset) validateRefs(stub *sw.StubWrapper) errors.ICCError {
208223
// Fetch references contained in asset

events/event.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package events
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/goledgerdev/cc-tools/errors"
8+
sw "github.com/goledgerdev/cc-tools/stubwrapper"
9+
)
10+
11+
type EventType float64
12+
13+
const (
14+
EventLog EventType = iota
15+
EventTransaction
16+
EventCustom
17+
)
18+
19+
// Event is the struct defining a primitive event.
20+
type Event struct {
21+
// Tag is how the event will be referenced
22+
Tag string `json:"tag"`
23+
24+
// Label is the pretty event name for logs
25+
Label string `json:"label"`
26+
27+
// Description is a simple explanation describing the meaning of the event.
28+
Description string `json:"description"`
29+
30+
// BaseLog is the basisc log message for the event
31+
BaseLog string `json:"baseLog"`
32+
33+
// Type is the type of event
34+
Type EventType `json:"type"`
35+
36+
// Receivers is an array that specifies which organizations will receive the event.
37+
// Accepts either basic strings for exact matches
38+
// eg. []string{'org1MSP', 'org2MSP'}
39+
// or regular expressions
40+
// eg. []string{`$org\dMSP`} and cc-tools will
41+
// check for a match with regular expression `org\dMSP`
42+
Receivers []string `json:"receivers,omitempty"`
43+
44+
// Transaction is the transaction that the event triggers (if of type EventTransaction)
45+
Transaction string `json:"transaction"`
46+
47+
// Channel is the channel of the transaction that the event triggers (if of type EventTransaction)
48+
// If empty, the event will trigger on the same channel as the transaction that calls the event
49+
Channel string `json:"channel"`
50+
51+
// Chaincode is the chaincode of the transaction that the event triggers (if of type EventTransaction)
52+
// If empty, the event will trigger on the same chaincode as the transaction that calls the event
53+
Chaincode string `json:"chaincode"`
54+
55+
// CustomFunction is used an event of type "EventCustom" is called.
56+
// It is a function that receives a stub and a payload and returns an error.
57+
CustomFunction func(*sw.StubWrapper, []byte) error `json:"-"`
58+
59+
// ReadOnly indicates if the CustomFunction has the ability to alter the world state (if of type EventCustom).
60+
ReadOnly bool `json:"readOnly"`
61+
}
62+
63+
func (event Event) CallEvent(stub *sw.StubWrapper, payload []byte) errors.ICCError {
64+
err := stub.SetEvent(event.Tag, payload)
65+
if err != nil {
66+
return errors.WrapError(err, "stub.SetEvent call error")
67+
}
68+
69+
return nil
70+
}
71+
72+
func CallEvent(stub *sw.StubWrapper, eventTag string, payload []byte) errors.ICCError {
73+
event := FetchEvent(eventTag)
74+
if event == nil {
75+
return errors.NewCCError(fmt.Sprintf("event named %s does not exist", eventTag), http.StatusBadRequest)
76+
}
77+
78+
return event.CallEvent(stub, payload)
79+
}

events/eventList.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package events
2+
3+
// eventList is the list which should contain all defined events
4+
var eventList = []Event{}
5+
6+
// EventList returns a copy of the eventList variable.
7+
func EventList() []Event {
8+
listCopy := make([]Event, len(eventList))
9+
copy(listCopy, eventList)
10+
return listCopy
11+
}
12+
13+
// InitEventList appends custom events to eventList to avoid initialization loop.
14+
func InitEventList(l []Event) {
15+
eventList = l
16+
}
17+
18+
// FetchEvent returns a pointer to the event object or nil if event is not found.
19+
func FetchEvent(eventTag string) *Event {
20+
for _, event := range eventList {
21+
if event.Tag == eventTag {
22+
return &event
23+
}
24+
}
25+
return nil
26+
}

stubwrapper/stubWrapper.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,12 @@ func (sw *StubWrapper) SplitCompositeKey(compositeKey string) (string, []string,
210210
}
211211
return key, keys, nil
212212
}
213+
214+
func (sw *StubWrapper) SetEvent(name string, payload []byte) errors.ICCError {
215+
err := sw.Stub.SetEvent(name, payload)
216+
if err != nil {
217+
return errors.WrapError(err, "stub.SetEvent call error")
218+
}
219+
220+
return nil
221+
}

test/assets_assetType_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ func TestArrayFromAssetTypeList(t *testing.T) {
277277
"required": true,
278278
"label": "Library Name",
279279
"dataType": "string",
280-
"writers": []interface{}{"org3MSP"},
280+
"writers": []interface{}{`$org\dMSP`},
281281
},
282282
map[string]interface{}{
283283
"tag": "books",
@@ -289,6 +289,11 @@ func TestArrayFromAssetTypeList(t *testing.T) {
289289
"label": "Entrance Code for the Library",
290290
"dataType": "->secret",
291291
},
292+
map[string]interface{}{
293+
"tag": "librarian",
294+
"label": "Librarian",
295+
"dataType": "->person",
296+
},
292297
},
293298
},
294299
}

test/assets_events_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package test
2+
3+
import (
4+
"log"
5+
"reflect"
6+
"testing"
7+
8+
"github.com/goledgerdev/cc-tools/events"
9+
)
10+
11+
func TestFetchEvent(t *testing.T) {
12+
event := *events.FetchEvent("createLibraryLog")
13+
expectedEvent := testEventTypeList[0]
14+
15+
if !reflect.DeepEqual(event, expectedEvent) {
16+
log.Println("these should be deeply equal")
17+
log.Println(event)
18+
log.Println(expectedEvent)
19+
t.FailNow()
20+
}
21+
}
22+
23+
func TestFetchEventList(t *testing.T) {
24+
eventList := events.EventList()
25+
expectedEventList := testEventTypeList
26+
27+
if !reflect.DeepEqual(eventList, expectedEventList) {
28+
log.Println("these should be deeply equal")
29+
log.Println(eventList)
30+
log.Println(expectedEventList)
31+
t.FailNow()
32+
}
33+
}

0 commit comments

Comments
 (0)