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

feat: Add events to the transaction model in GraphQL #43

Merged
merged 8 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21
require (
github.com/99designs/gqlgen v0.17.45
github.com/cockroachdb/pebble v1.1.0
github.com/gnolang/gno v0.0.0-20240429120125-3832b1312d7d
github.com/gnolang/gno v0.0.0-20240502173657-6482c18da2c7
github.com/go-chi/chi/v5 v5.0.12
github.com/go-chi/httprate v0.9.0
github.com/google/uuid v1.6.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/gnolang/gno v0.0.0-20240429120125-3832b1312d7d h1:vD4URFbnv8UL/NG8fX5pjVPRyQ1tvCtqTzU5I996EtA=
github.com/gnolang/gno v0.0.0-20240429120125-3832b1312d7d/go.mod h1:vbm5sS6KZj2rkwJ9C6AtdpPZB7aZDhfZ3lJUC+qa/to=
github.com/gnolang/gno v0.0.0-20240502173657-6482c18da2c7 h1:G9dv/bhGj4aWbar1nCD4ckjdb/NrGwqNK5sWaFi+jZA=
github.com/gnolang/gno v0.0.0-20240502173657-6482c18da2c7/go.mod h1:vbm5sS6KZj2rkwJ9C6AtdpPZB7aZDhfZ3lJUC+qa/to=
github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
Expand Down
1,026 changes: 877 additions & 149 deletions serve/graph/generated.go

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions serve/graph/model/models_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 53 additions & 0 deletions serve/graph/model/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,17 @@ func (t *Transaction) getMessages() []*TransactionMessage {

//nolint:errname // Provide a field named `error` as the GraphQL response value
type TransactionResponse struct {
events []TransactionEvent
response abci.ResponseDeliverTx

mu sync.Mutex
onceEvents sync.Once
}

func NewTransactionResponse(response abci.ResponseDeliverTx) *TransactionResponse {
return &TransactionResponse{
response: response,
}
}

func (tr *TransactionResponse) Log() string {
Expand All @@ -149,6 +159,33 @@ func (tr *TransactionResponse) Data() string {
return string(tr.response.Data)
}

func (tr *TransactionResponse) Events() []TransactionEvent {
return tr.getEvents()
}

func (tr *TransactionResponse) getEvents() []TransactionEvent {
// This function creates a 'TransactionEvent' with 'abci.Event' interface data.
// and executed once.
makeEvents := func() {
events := make([]TransactionEvent, 0)

for _, event := range tr.response.Events {
transactionEvent, err := makeTransactionEvent(event)
if err == nil {
events = append(events, transactionEvent)
}
}

tr.mu.Lock()
tr.events = events
tr.mu.Unlock()
}

tr.onceEvents.Do(makeEvents)

return tr.events
}

type TransactionMessage struct {
Value MessageValue
Route string
Expand Down Expand Up @@ -217,6 +254,22 @@ func (tm *TransactionMessage) VMMsgRun() MsgRun {
return tm.Value.(MsgRun)
}

func makeTransactionEvent(abciEvent abci.Event) (TransactionEvent, error) {
var event TransactionEvent

data, err := json.Marshal(abciEvent)
if err != nil {
return event, err
}

err = json.Unmarshal(data, &event)
if err != nil {
return event, err
}

return event, nil
}

func makeBankMsgSend(value std.Msg) BankMsgSend {
decodedMessage, err := cast[bank.MsgSend](value)
if err != nil {
Expand Down
52 changes: 52 additions & 0 deletions serve/graph/schema/filter/transaction_filter.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,58 @@ input TransactionFilter {
`success` enables you to filter between successful and unsuccessful transactions.
"""
success: Boolean

"""
`events` are what the transaction has emitted.
`events` can be filtered with a specific event to query its transactions.
`events` is entered as an array and works exclusively.
ex) `events[0] || events[1] || events[2]`
"""
events: [TransactionEventInput!]
}

"""
Transaction's event to filter transactions.
"TransactionEventInput" can be configured as a filter with a transaction event's `type` and `pkg_path` and `func`, and `attrs`.
"""
input TransactionEventInput {
"""
`type` is the type of transaction event emitted.
"""
type: String

"""
`pkg_path` is the path to the package that emitted the event.
"""
pkg_path: String

"""
`func` is the name of the function that emitted the event.
"""
func: String

"""
`attrs` filters transactions whose events contain attributes.
`attrs` is entered as an array and works exclusively.
ex) `attrs[0] || attrs[1] || attrs[2]`
"""
attrs: [TransactionEventAttributeInput!]
}

"""
Transaction event's attribute to filter transaction.
"TransactionEventAttributeInput" can be configured as a filter with a event attribute's `key` and `value`.
"""
input TransactionEventAttributeInput {
"""
`key` is the key of the event attribute.
"""
key: String

"""
`value` is the value of the event attribute.
"""
value: String
}

"""
Expand Down
47 changes: 47 additions & 0 deletions serve/graph/schema/types/transaction.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,51 @@ type TransactionResponse {
The response data associated with the Transaction execution, if any.
"""
data: String!

"""
The emitted events associated with the transaction execution, if any.
"""
events: [TransactionEvent!]
}

"""
`TransactionEvent' is the event information emitted by the transaction.
It has `log`, `info`, `error`, and `data`.
"""
type TransactionEvent {
"""
`type` is the type of transaction event emitted.
"""
type: String!

"""
`pkg_path` is the path to the package that emitted the event.
"""
pkg_path: String!

"""
`func` is the name of the function that emitted the event.
"""
func: String!

"""
`attrs` is the event's attribute information.
"""
attrs: [TransactionEventAttribute!]
}

"""
`TransactionEventAttribute` is the attributes that the event has.
It has `key` and `value`.
"""
type TransactionEventAttribute {
"""
The key of the event attribute.
"""
key: String!

"""
The value of the event attribute.
"""
value: String!
}
73 changes: 73 additions & 0 deletions serve/graph/transaction_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func FilteredTransactionBy(tx *model.Transaction, filter model.TransactionFilter
return false
}

if !filteredTransactionByEvents(tx, filter.Events) {
return false
}

if filter.Message != nil {
if !filteredTransactionByMessageRoute(tx, filter.Message.Route) {
return false
Expand All @@ -52,6 +56,75 @@ func filteredTransactionBySuccess(tx *model.Transaction, success *bool) bool {
return deref(success) == tx.Success()
}

// `filteredTransactionByEvents` checks for events in the transaction's results.
func filteredTransactionByEvents(tx *model.Transaction, eventInputs []*model.TransactionEventInput) bool {
if len(eventInputs) == 0 {
return true
}

events := tx.Response().Events()
if len(events) == 0 {
return false
}

for _, event := range events {
for _, eventInput := range eventInputs {
if filteredTransactionEventBy(event, eventInput) {
return true
}
}
}

return false
}

// `filteredTransactionEventBy` checks the conditions of a event.
func filteredTransactionEventBy(event model.TransactionEvent, eventInput *model.TransactionEventInput) bool {
if eventInput.Type != nil && deref(eventInput.Type) != event.Type {
return false
}

if eventInput.PkgPath != nil && deref(eventInput.PkgPath) != event.PkgPath {
return false
}

if eventInput.Func != nil && deref(eventInput.Func) != event.Func {
return false
}

if eventInput.Attrs != nil && !filteredEventAttributesBy(event.Attrs, eventInput.Attrs) {
return false
}

return true
}

// `filteredEventAttributesBy` check the conditions of event attributes
func filteredEventAttributesBy(
attrs []*model.TransactionEventAttribute,
filterAttrs []*model.TransactionEventAttributeInput,
) bool {
if len(attrs) == 0 {
return true
}

for _, attr := range attrs {
for _, attributeFilter := range filterAttrs {
if attributeFilter.Key != nil && attr.Key != deref(attributeFilter.Key) {
continue
}

if attributeFilter.Value != nil && attr.Value != deref(attributeFilter.Value) {
continue
}

return true
}
}

return false
}

// `filteredAmountBy` checks a token represented as a string(<value><denomination>)
// against a range of amount and a denomination.
func filteredAmountBy(amountStr string, amountInput *model.AmountInput) bool {
Expand Down
Loading