Feature/refactor api (#34)

* Refactor error handling and comments

* Refactor and improve error handling and logs
This commit is contained in:
walker-16 2022-11-23 09:06:29 -03:00 committed by GitHub
parent d7e2cd55fe
commit 8ed4804d63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 671 additions and 274 deletions

View File

@ -1,9 +0,0 @@
package api
// API codes used in guardian node.
// The same codes are used to facilitate portability to the new api.
const (
InvalidArgument = 3
NotFound = 5
Internal = 13
)

View File

@ -1,12 +0,0 @@
package errors
import "errors"
func IsOf(received error, targets ...error) bool {
for _, t := range targets {
if errors.Is(received, t) {
return true
}
}
return false
}

View File

@ -3,27 +3,26 @@ module github.com/wormhole-foundation/wormhole-explorer/api
go 1.19
require (
github.com/ansrivas/fiberprometheus/v2 v2.4.1
github.com/certusone/wormhole/node v0.0.0-20220907133901-8e231501b6cd
github.com/gofiber/fiber/v2 v2.39.0
github.com/ipfs/go-log/v2 v2.5.1
github.com/spf13/viper v1.13.0
go.mongodb.org/mongo-driver v1.10.3
go.uber.org/zap v1.22.0
)
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/ansrivas/fiberprometheus/v2 v2.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/ethereum/go-ethereum v1.10.6 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/gofiber/adaptor/v2 v2.1.29 // indirect
github.com/gogo/protobuf v1.3.3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/klauspost/compress v1.15.12 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@ -32,7 +31,6 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
@ -55,7 +53,6 @@ require (
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.22.0 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.1.0 // indirect

View File

@ -83,6 +83,7 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbE
github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM=
github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -105,7 +106,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/certusone/wormhole/node v0.0.0-20220907133901-8e231501b6cd h1:j/D2a0Hvs+JBKteCTFGhi9f7MA0SaDE+BPFoGNqSENo=
github.com/certusone/wormhole/node v0.0.0-20220907133901-8e231501b6cd/go.mod h1:hbEdpBxPoBplIJ+PMVitVGX5194g+Opd7dvONcxhykQ=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@ -173,8 +173,6 @@ github.com/gofiber/adaptor/v2 v2.1.25/go.mod h1:gOxtwMVqUStB5goAYtKd+hSvGupdd+aR
github.com/gofiber/adaptor/v2 v2.1.29 h1:JnYd6fbqVM9D4zPchk+kg89PfxyuKqZKhBWGQDHfKH4=
github.com/gofiber/adaptor/v2 v2.1.29/go.mod h1:z4mAV9mMsUgIEVGGS5Ii6ZMTJq4VdV1KWL1JAbsZdUA=
github.com/gofiber/fiber/v2 v2.36.0/go.mod h1:tgCr+lierLwLoVHHO/jn3Niannv34WRkQETU8wiL9fQ=
github.com/gofiber/fiber/v2 v2.38.1 h1:GEQ/Yt3Wsf2a30iTqtLXlBYJZso0JXPovt/tmj5H9jU=
github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8=
github.com/gofiber/fiber/v2 v2.39.0 h1:uhWpYQ6EHN8J7FOPYbI2hrdBD/KNZBC5CjbuOd4QUt4=
github.com/gofiber/fiber/v2 v2.39.0/go.mod h1:Cmuu+elPYGqlvQvdKyjtYsjGMi69PDp8a1AY2I5B2gM=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@ -271,9 +269,6 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y
github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
@ -302,8 +297,6 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A=
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
@ -371,8 +364,6 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
@ -420,7 +411,6 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI=
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@ -482,7 +472,6 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.38.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc=
github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY=
github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
@ -512,19 +501,16 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
@ -709,7 +695,6 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -746,8 +731,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -891,7 +874,6 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -1,23 +1,26 @@
// Package governor handle the request of governor data from governor endpoint defined in the api.
package governor
import (
"github.com/gofiber/fiber/v2"
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"go.uber.org/zap"
)
// Controller definition.
type Controller struct {
srv *Service
logger *zap.Logger
}
// NewController create a new controler.
func NewController(serv *Service, logger *zap.Logger) *Controller {
return &Controller{srv: serv, logger: logger.With(zap.String("module", "GovernorController"))}
}
// FindGovernorConfigurations handler for the endpoint /governor/config/
func (c *Controller) FindGovernorConfigurations(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
p := middleware.GetPaginationFromContext(ctx)
governorConfigs, err := c.srv.FindGovernorConfig(ctx.Context(), p)
if err != nil {
return err
@ -25,9 +28,10 @@ func (c *Controller) FindGovernorConfigurations(ctx *fiber.Ctx) error {
return ctx.JSON(governorConfigs)
}
// FindGovernorConfigurationByGuardianAddress handler for the endpoint /governor/config/:guardian_address.
func (c *Controller) FindGovernorConfigurationByGuardianAddress(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
guardianAddress, err := middleware.ExtractGuardianAddress(ctx)
p := middleware.GetPaginationFromContext(ctx)
guardianAddress, err := middleware.ExtractGuardianAddress(ctx, c.logger)
if err != nil {
return err
}
@ -38,8 +42,9 @@ func (c *Controller) FindGovernorConfigurationByGuardianAddress(ctx *fiber.Ctx)
return ctx.JSON(govConfig)
}
// FindGovernorStatus handler for the endpoint /governor/status/.
func (c *Controller) FindGovernorStatus(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
p := middleware.GetPaginationFromContext(ctx)
governorStatus, err := c.srv.FindGovernorStatus(ctx.Context(), p)
if err != nil {
return err
@ -47,9 +52,10 @@ func (c *Controller) FindGovernorStatus(ctx *fiber.Ctx) error {
return ctx.JSON(governorStatus)
}
// FindGovernorStatusByGuardianAddress handler for the endpoint /governor/status/:guardian_address.
func (c *Controller) FindGovernorStatusByGuardianAddress(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
guardianAddress, err := middleware.ExtractGuardianAddress(ctx)
p := middleware.GetPaginationFromContext(ctx)
guardianAddress, err := middleware.ExtractGuardianAddress(ctx, c.logger)
if err != nil {
return err
}
@ -60,8 +66,9 @@ func (c *Controller) FindGovernorStatusByGuardianAddress(ctx *fiber.Ctx) error {
return ctx.JSON(govStatus)
}
// GetGovernorLimit handler for the endpoint /governor/limit/
func (c *Controller) GetGovernorLimit(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
p := middleware.GetPaginationFromContext(ctx)
governorLimit, err := c.srv.GetGovernorLimit(ctx.Context(), p)
if err != nil {
return err
@ -69,8 +76,9 @@ func (c *Controller) GetGovernorLimit(ctx *fiber.Ctx) error {
return ctx.JSON(governorLimit)
}
// FindNotionalLimit handler for the endpoint governor/notional/limit/
func (c *Controller) FindNotionalLimit(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
p := middleware.GetPaginationFromContext(ctx)
notionalLimit, err := c.srv.FindNotionalLimit(ctx.Context(), p)
if err != nil {
return err
@ -78,9 +86,10 @@ func (c *Controller) FindNotionalLimit(ctx *fiber.Ctx) error {
return ctx.JSON(notionalLimit)
}
// GetNotionalLimitByChainID handler for the endpoint governor/notional/limit/:chain.
func (c *Controller) GetNotionalLimitByChainID(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx)
p := middleware.GetPaginationFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx, c.logger)
if err != nil {
return err
}
@ -91,8 +100,9 @@ func (c *Controller) GetNotionalLimitByChainID(ctx *fiber.Ctx) error {
return ctx.JSON(notionalLimit)
}
// GetAvailableNotional handler for the endpoint governor/notional/available/
func (c *Controller) GetAvailableNotional(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
p := middleware.GetPaginationFromContext(ctx)
notionalAvaialabilies, err := c.srv.GetAvailableNotional(ctx.Context(), p)
if err != nil {
return err
@ -100,9 +110,10 @@ func (c *Controller) GetAvailableNotional(ctx *fiber.Ctx) error {
return ctx.JSON(notionalAvaialabilies)
}
// GetAvailableNotionalByChainID handler for the endpoint governor/notional/available/:chain
func (c *Controller) GetAvailableNotionalByChainID(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx)
p := middleware.GetPaginationFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx, c.logger)
if err != nil {
return err
}
@ -113,9 +124,10 @@ func (c *Controller) GetAvailableNotionalByChainID(ctx *fiber.Ctx) error {
return ctx.JSON(response)
}
// GetMaxNotionalAvailableByChainID handler for the endpoint governor/max_available/:chain.
func (c *Controller) GetMaxNotionalAvailableByChainID(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx)
p := middleware.GetPaginationFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx, c.logger)
if err != nil {
return err
}
@ -126,8 +138,9 @@ func (c *Controller) GetMaxNotionalAvailableByChainID(ctx *fiber.Ctx) error {
return ctx.JSON(response)
}
// GetEnqueueVass handler for the endpoint governor/enqueued_vaas/
func (c *Controller) GetEnqueueVass(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
p := middleware.GetPaginationFromContext(ctx)
enqueuedVaas, err := c.srv.GetEnqueueVass(ctx.Context(), p)
if err != nil {
return err
@ -135,9 +148,10 @@ func (c *Controller) GetEnqueueVass(ctx *fiber.Ctx) error {
return ctx.JSON(enqueuedVaas)
}
// GetEnqueueVassByChainID handler for the endpoint governor/enqueued_vaas/:chain.
func (c *Controller) GetEnqueueVassByChainID(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx)
p := middleware.GetPaginationFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx, c.logger)
if err != nil {
return err
}

View File

@ -1,3 +1,4 @@
// Package governor handle the request of governor data from governor endpoint defined in the api.
package governor
import (
@ -6,7 +7,7 @@ import (
"github.com/certusone/wormhole/node/pkg/vaa"
)
// GovConfigPage definition.
// GovConfigPage represent a governor configuration.
type GovConfig struct {
ID string `bson:"_id" json:"id"`
CreatedAt *time.Time `bson:"createdAt" json:"createdAt"`
@ -29,7 +30,7 @@ type GovConfigfTokens struct {
Price float64 `bson:"price" json:"price"`
}
// GovStatusPage definition.
// GovStatusPage represent a governor status.
type GovStatus struct {
ID string `bson:"_id" json:"id"`
CreatedAt *time.Time `bson:"createdAt" json:"createdAt"`
@ -50,17 +51,17 @@ type GovStatusChainEmitter struct {
EnqueuedVass interface{} `bson:"enqueuedvaas" json:"enqueuedvaas"`
}
// NotionalLimit definition.
// NotionalLimit represent the notional limit value and maximun tranasction size for a chainID.
type NotionalLimit struct {
ChainID vaa.ChainID `bson:"chainid" json:"chainid"`
NotionalLimit *int64 `bson:"notionalLimit" json:"notionalLimit"`
MaxTrasactionSize *int64 `bson:"maxTransactionSize" json:"maxTransactionSize"`
}
// NotionalLimitDetail definition.
// NotionalLimitDetail represent a notional limit value
type NotionalLimitDetail struct {
ID string `bson:"_id" json:"id"`
ChainID vaa.ChainID `bson:"chainid" json:"chainid"`
ChainID vaa.ChainID `bson:"chainId" json:"chainId"`
NodeName string `bson:"nodename" json:"nodename"`
NotionalLimit *int64 `bson:"notionalLimit" json:"notionalLimit"`
MaxTrasactionSize *int64 `bson:"maxTransactionSize" json:"maxTransactionSize"`
@ -68,11 +69,13 @@ type NotionalLimitDetail struct {
UpdatedAt *time.Time `bson:"updatedAt" json:"updatedAt"`
}
// NotionalAvailable represent the available notional for chainID.
type NotionalAvailable struct {
ChainID vaa.ChainID `bson:"chainid" json:"chainId"`
AvailableNotional *int64 `bson:"availableNotional" json:"availableNotional"`
}
// NotionalAvailableDetail represent a notional available value.
type NotionalAvailableDetail struct {
ID string `bson:"_id" json:"id"`
ChainID vaa.ChainID `bson:"chainId" json:"chainId"`
@ -108,6 +111,7 @@ type EnqueuedVaa struct {
TxHash string `bson:"txHash" json:"txHash"`
}
// EnqueuedVaas definition.
type EnqueuedVaas struct {
ChainID vaa.ChainID `bson:"chainid" json:"chainId"`
EnqueuedVaa []*EnqueuedVaa `bson:"enqueuedVaas" json:"enqueuedVaas"`

View File

@ -1,14 +1,16 @@
// Package governor handle the request of governor data from governor endpoint defined in the api.
package governor
import (
"context"
"errors"
"fmt"
"sort"
"time"
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"github.com/pkg/errors"
errs "github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@ -17,10 +19,7 @@ import (
const minGuardianNum = 13
var (
ErrWrongQuery = errors.New("MALFORMED_QUERY")
)
// Repository definition.
type Repository struct {
db *mongo.Database
logger *zap.Logger
@ -30,6 +29,7 @@ type Repository struct {
}
}
// NewRepository create a new Repository.
func NewRepository(db *mongo.Database, logger *zap.Logger) *Repository {
return &Repository{db: db,
logger: logger.With(zap.String("module", "GovernorRepository")),
@ -43,21 +43,25 @@ func NewRepository(db *mongo.Database, logger *zap.Logger) *Repository {
}
}
// GovernorQuery respresent a query for the governors mongodb documents.
type GovernorQuery struct {
pagination.Pagination
id string
}
// QueryGovernor create a new GovernorQuery with default pagination values.
func QueryGovernor() *GovernorQuery {
page := pagination.FirstPage()
return &GovernorQuery{Pagination: *page}
}
// SetID set the id field of the GovernorQuery struct.
func (q *GovernorQuery) SetID(id string) *GovernorQuery {
q.id = id
return q
}
// SetPagination set the pagination field of the GovernorQuery struct.
func (q *GovernorQuery) SetPagination(p *pagination.Pagination) *GovernorQuery {
q.Pagination = *p
return q
@ -71,6 +75,7 @@ func (q *GovernorQuery) toBSON() *bson.D {
return &r
}
// FindGovConfigurations get a list of *GovConfig.
func (r *Repository) FindGovConfigurations(ctx context.Context, q *GovernorQuery) ([]*GovConfig, error) {
if q == nil {
q = QueryGovernor()
@ -87,19 +92,26 @@ func (r *Repository) FindGovConfigurations(ctx context.Context, q *GovernorQuery
options := options.Find().SetProjection(projection).SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort)
cur, err := r.collections.governorConfig.Find(ctx, q.toBSON(), options)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Find command to get governor configurations",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
var govConfigs []*GovConfig
err = cur.All(ctx, &govConfigs)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*GovConfig", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return govConfigs, err
}
// FindGovConfiguration get a *GovConfig. The q parameter define the filter to apply to the query.
func (r *Repository) FindGovConfiguration(ctx context.Context, q *GovernorQuery) (*GovConfig, error) {
if q == nil {
return nil, ErrWrongQuery
return nil, errs.ErrMalformedQuery
}
var govConfig GovConfig
projection := bson.D{
@ -113,11 +125,18 @@ func (r *Repository) FindGovConfiguration(ctx context.Context, q *GovernorQuery)
options := options.FindOne().SetProjection(projection)
err := r.collections.governorConfig.FindOne(ctx, q.toBSON(), options).Decode(&govConfig)
if err != nil {
return nil, err
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, errs.ErrNotFound
}
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute FindOne command to get governor configuration",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return &govConfig, err
}
// FindGovernorStatus get a list of *GovStatus.
func (r *Repository) FindGovernorStatus(ctx context.Context, q *GovernorQuery) ([]*GovStatus, error) {
if q == nil {
q = QueryGovernor()
@ -132,19 +151,26 @@ func (r *Repository) FindGovernorStatus(ctx context.Context, q *GovernorQuery) (
options := options.Find().SetProjection(projection).SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort)
cur, err := r.collections.governorStatus.Find(ctx, q.toBSON(), options)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Find command to get all governor status",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
var govStatus []*GovStatus
err = cur.All(ctx, &govStatus)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*GovStatus", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return govStatus, err
}
// FindOneGovernorStatus get a *GovStatus. The q parameter define the filter to apply to the query.
func (r *Repository) FindOneGovernorStatus(ctx context.Context, q *GovernorQuery) (*GovStatus, error) {
if q == nil {
return nil, ErrWrongQuery
return nil, errs.ErrMalformedQuery
}
var govConfig GovStatus
projection := bson.D{
@ -156,37 +182,49 @@ func (r *Repository) FindOneGovernorStatus(ctx context.Context, q *GovernorQuery
options := options.FindOne().SetProjection(projection)
err := r.collections.governorStatus.FindOne(ctx, q.toBSON(), options).Decode(&govConfig)
if err != nil {
return nil, err
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, errs.ErrNotFound
}
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute FindOne command to get governor status",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return &govConfig, err
}
// NotionalLimitQuery
type NotionalLimitQuery struct {
pagination.Pagination
id string
chainID vaa.ChainID
}
// QueryNotionalLimit create a new NotionalLimitQuery with default pagination values.
func QueryNotionalLimit() *NotionalLimitQuery {
page := pagination.FirstPage()
return &NotionalLimitQuery{Pagination: *page}
}
// SetID set the id field of the NotionalLimitQuery struct.
func (q *NotionalLimitQuery) SetID(id string) *NotionalLimitQuery {
q.id = id
return q
}
// SetChain set the chainID field of the NotionalLimitQuery struct.
func (q *NotionalLimitQuery) SetChain(chainID vaa.ChainID) *NotionalLimitQuery {
q.chainID = chainID
return q
}
// SetPagination set the Pagination field of the NotionalLimitQuery struct.
func (q *NotionalLimitQuery) SetPagination(p *pagination.Pagination) *NotionalLimitQuery {
q.Pagination = *p
return q
}
// FindNotionalLimit get a list *NotionalLimit.
func (r *Repository) FindNotionalLimit(ctx context.Context, q *NotionalLimitQuery) ([]*NotionalLimit, error) {
// agreggation stages to get notionalLimit for each chainID.
matchStage1 := bson.D{{Key: "$match", Value: bson.D{}}}
@ -259,24 +297,31 @@ func (r *Repository) FindNotionalLimit(ctx context.Context, q *NotionalLimitQuer
// execute aggregate operations.
cur, err := r.collections.governorConfig.Aggregate(ctx, pipeLine)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get notional limit",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// decodes to NotionalLimit.
var notionalLimits []*NotionalLimit
err = cur.All(ctx, &notionalLimits)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*NotionalLimit", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// check records exists.
if len(notionalLimits) == 0 {
return nil, errors.New("not found")
return nil, errs.ErrNotFound
}
return notionalLimits, nil
}
// GetNotionalLimitByChainID get a list *NotionalLimitDetail.
func (r *Repository) GetNotionalLimitByChainID(ctx context.Context, q *NotionalLimitQuery) ([]*NotionalLimitDetail, error) {
// agreggation stages to get notionalLimit by chainID.
matchStage1 := bson.D{{Key: "$match", Value: bson.D{}}}
@ -333,19 +378,26 @@ func (r *Repository) GetNotionalLimitByChainID(ctx context.Context, q *NotionalL
// execute aggregate operations.
cur, err := r.collections.governorConfig.Aggregate(ctx, pipeLine)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get notional limit by chainID",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// decode to []NotionalLimitRecord.
var notionalLimits []*NotionalLimitDetail
err = cur.All(ctx, &notionalLimits)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*NotionalLimitDetail", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return notionalLimits, nil
}
// GetAvailableNotional get a list of *NotionalAvailable.
func (r *Repository) GetAvailableNotional(ctx context.Context, q *NotionalLimitQuery) ([]*NotionalAvailable, error) {
// stage.
matchStage1 := bson.D{{Key: "$match", Value: bson.D{}}}
@ -421,24 +473,31 @@ func (r *Repository) GetAvailableNotional(ctx context.Context, q *NotionalLimitQ
// execute aggregate operations.
cur, err := r.collections.governorStatus.Aggregate(ctx, pipeLine)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get available notional",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// decode to []NotionalLimitRecord.
var notionalAvailables []*NotionalAvailable
err = cur.All(ctx, &notionalAvailables)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*NotionalAvailable", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// check exists records
if len(notionalAvailables) == 0 {
return nil, errors.New("not found")
return nil, errs.ErrNotFound
}
return notionalAvailables, nil
}
// GetAvailableNotionalByChainID get a list of *NotionalAvailableDetail.
func (r *Repository) GetAvailableNotionalByChainID(ctx context.Context, q *NotionalLimitQuery) ([]*NotionalAvailableDetail, error) {
// stage definitions.
matchStage1 := bson.D{{Key: "$match", Value: bson.D{}}}
@ -497,19 +556,26 @@ func (r *Repository) GetAvailableNotionalByChainID(ctx context.Context, q *Notio
// execute aggregate operations.
cur, err := r.collections.governorStatus.Aggregate(ctx, pipeLine)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get available notional by chainID",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// decode to []NotionalLimitRecord.
var notionalAvailability []*NotionalAvailableDetail
err = cur.All(ctx, &notionalAvailability)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*NotionalAvailableDetail", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return notionalAvailability, nil
}
// GetMaxNotionalAvailableByChainID get a *MaxNotionalAvailableRecord.
func (r *Repository) GetMaxNotionalAvailableByChainID(ctx context.Context, q *NotionalLimitQuery) (*MaxNotionalAvailableRecord, error) {
// stage definitions.
matchStage1 := bson.D{{Key: "$match", Value: bson.D{}}}
@ -577,55 +643,67 @@ func (r *Repository) GetMaxNotionalAvailableByChainID(ctx context.Context, q *No
// execute aggregate operations.
cur, err := r.collections.governorStatus.Aggregate(ctx, pipeLine)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get maximun available notional by chainID",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// decode to []NotionalLimitRecord.
var rows []*MaxNotionalAvailableRecord
err = cur.All(ctx, &rows)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*MaxNotionalAvailableRecord", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// check exists records
if len(rows) == 0 {
return nil, errors.New("not found")
return nil, errs.ErrNotFound
}
if len(rows) < minGuardianNum {
return nil, errors.New("not found")
return nil, errs.ErrNotFound
}
maxNotionalLimit := rows[minGuardianNum-1]
return maxNotionalLimit, nil
}
// EnqueuedVaaQuery respresent a query for enqueuedVaa queries.
type EnqueuedVaaQuery struct {
pagination.Pagination
id string
chainID vaa.ChainID
}
// QueryEnqueuedVaa create a new EnqueuedVaaQuery with default pagination values.
func QueryEnqueuedVaa() *EnqueuedVaaQuery {
page := pagination.FirstPage()
return &EnqueuedVaaQuery{Pagination: *page}
}
// SetID set the id field of the EnqueuedVaaQuery struct.
func (q *EnqueuedVaaQuery) SetID(id string) *EnqueuedVaaQuery {
q.id = id
return q
}
// SetChain set the chainID field of the EnqueuedVaaQuery struct.
func (q *EnqueuedVaaQuery) SetChain(chainID vaa.ChainID) *EnqueuedVaaQuery {
q.chainID = chainID
return q
}
// SetPagination set the pagination field of the EnqueuedVaaQuery struct.
func (q *EnqueuedVaaQuery) SetPagination(p *pagination.Pagination) *EnqueuedVaaQuery {
q.Pagination = *p
return q
}
// GetEnqueueVass get a list of *EnqueuedVaas.
func (r *Repository) GetEnqueueVass(ctx context.Context, q *EnqueuedVaaQuery) ([]*EnqueuedVaas, error) {
// match stage.
matchStage1 := bson.D{{Key: "$match", Value: bson.D{}}}
@ -678,7 +756,10 @@ func (r *Repository) GetEnqueueVass(ctx context.Context, q *EnqueuedVaaQuery) ([
cur, err := r.collections.governorStatus.Aggregate(ctx, pipeLine)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get enqueued vaas",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
var rows []struct {
@ -697,11 +778,14 @@ func (r *Repository) GetEnqueueVass(ctx context.Context, q *EnqueuedVaaQuery) ([
// decode query response.
err = cur.All(ctx, &rows)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to rows", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
if len(rows) == 0 {
return nil, errors.New("not found")
return nil, errs.ErrNotFound
}
// TODO: Change this logic to mongo query code.
@ -732,7 +816,7 @@ func (r *Repository) GetEnqueueVass(ctx context.Context, q *EnqueuedVaaQuery) ([
}
if len(enqueuedVaas) == 0 {
return nil, errors.New("not found")
return nil, errs.ErrNotFound
}
// group by chainID.
@ -760,6 +844,7 @@ func (r *Repository) GetEnqueueVass(ctx context.Context, q *EnqueuedVaaQuery) ([
return response, nil
}
// GetEnqueueVassByChainID get a list of *EnqueuedVaaDetail by chainID.
func (r *Repository) GetEnqueueVassByChainID(ctx context.Context, q *EnqueuedVaaQuery) ([]*EnqueuedVaaDetail, error) {
// stage definitions.
matchStage1 := bson.D{{Key: "$match", Value: bson.D{}}}
@ -823,7 +908,10 @@ func (r *Repository) GetEnqueueVassByChainID(ctx context.Context, q *EnqueuedVaa
cur, err := r.collections.governorStatus.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get enqueued vaas by chainID",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// decode query response.
@ -840,7 +928,10 @@ func (r *Repository) GetEnqueueVassByChainID(ctx context.Context, q *EnqueuedVaa
}
err = cur.All(ctx, &rows)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to rows", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// TODO: Change this logic to mongo query code.
@ -871,7 +962,7 @@ func (r *Repository) GetEnqueueVassByChainID(ctx context.Context, q *EnqueuedVaa
}
if len(response) == 0 {
return nil, errors.New("not found")
return nil, errs.ErrNotFound
}
// sort response by sequence.
@ -881,6 +972,7 @@ func (r *Repository) GetEnqueueVassByChainID(ctx context.Context, q *EnqueuedVaa
return response, nil
}
// GetGovernorLimit get a list of *GovernorLimit.
func (r *Repository) GetGovernorLimit(ctx context.Context, q *GovernorQuery) ([]*GovernorLimit, error) {
// lookup.
lookupStage1 := bson.D{
@ -1017,19 +1109,25 @@ func (r *Repository) GetGovernorLimit(ctx context.Context, q *GovernorQuery) ([]
// execute aggregate operations.
cur, err := r.collections.governorConfig.Aggregate(ctx, pipeLine)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get governor limit",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// decodes to RawDocRecord.
var governorLimits []*GovernorLimit
err = cur.All(ctx, &governorLimits)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*GovernorLimit", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
// check exists records
if len(governorLimits) == 0 {
return nil, errors.New("not found")
return nil, errs.ErrNotFound
}
return governorLimits, nil

View File

@ -1,11 +1,12 @@
// Package governor handle the request of governor data from governor endpoint defined in the api.
package governor
import (
"context"
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"github.com/wormhole-foundation/wormhole-explorer/api/services"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
"github.com/wormhole-foundation/wormhole-explorer/api/response"
"go.uber.org/zap"
)
@ -20,118 +21,118 @@ func NewService(dao *Repository, logger *zap.Logger) *Service {
}
// FindGovernorConfig get a list of governor configurations.
func (s *Service) FindGovernorConfig(ctx context.Context, p *pagination.Pagination) (*services.Response[[]*GovConfig], error) {
func (s *Service) FindGovernorConfig(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*GovConfig], error) {
if p == nil {
p = pagination.FirstPage()
}
query := QueryGovernor().SetPagination(p)
govConfigs, err := s.repo.FindGovConfigurations(ctx, query)
res := services.Response[[]*GovConfig]{Data: govConfigs, Error: err}
res := response.Response[[]*GovConfig]{Data: govConfigs}
return &res, err
}
// FindGovernorConfigByGuardianAddress get a governor configuration by guardianAddress.
func (s *Service) FindGovernorConfigByGuardianAddress(ctx context.Context, guardianAddress string, p *pagination.Pagination) (*services.Response[*GovConfig], error) {
func (s *Service) FindGovernorConfigByGuardianAddress(ctx context.Context, guardianAddress string, p *pagination.Pagination) (*response.Response[*GovConfig], error) {
query := QueryGovernor().SetID(guardianAddress).SetPagination(p)
govConfig, err := s.repo.FindGovConfiguration(ctx, query)
res := services.Response[*GovConfig]{Data: govConfig, Error: err}
res := response.Response[*GovConfig]{Data: govConfig}
return &res, err
}
// FindGovernorStatus get a list of governor status.
func (s *Service) FindGovernorStatus(ctx context.Context, p *pagination.Pagination) (*services.Response[[]*GovStatus], error) {
func (s *Service) FindGovernorStatus(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*GovStatus], error) {
if p == nil {
p = pagination.FirstPage()
}
query := QueryGovernor().SetPagination(p)
govStatus, err := s.repo.FindGovernorStatus(ctx, query)
res := services.Response[[]*GovStatus]{Data: govStatus, Error: err}
res := response.Response[[]*GovStatus]{Data: govStatus}
return &res, err
}
// FindGovernorStatusByGuardianAddress get a governor status by guardianAddress.
func (s *Service) FindGovernorStatusByGuardianAddress(ctx context.Context, guardianAddress string, p *pagination.Pagination) (*services.Response[*GovStatus], error) {
func (s *Service) FindGovernorStatusByGuardianAddress(ctx context.Context, guardianAddress string, p *pagination.Pagination) (*response.Response[*GovStatus], error) {
query := QueryGovernor().SetID(guardianAddress).SetPagination(p)
govStatus, err := s.repo.FindOneGovernorStatus(ctx, query)
res := services.Response[*GovStatus]{Data: govStatus, Error: err}
res := response.Response[*GovStatus]{Data: govStatus}
return &res, err
}
// FindNotionalLimit get a notional limit for each chainID.
func (s *Service) FindNotionalLimit(ctx context.Context, p *pagination.Pagination) (*services.Response[[]*NotionalLimit], error) {
func (s *Service) FindNotionalLimit(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*NotionalLimit], error) {
if p == nil {
p = pagination.FirstPage()
}
query := QueryNotionalLimit().SetPagination(p)
notionalLimit, err := s.repo.FindNotionalLimit(ctx, query)
res := services.Response[[]*NotionalLimit]{Data: notionalLimit, Error: err}
res := response.Response[[]*NotionalLimit]{Data: notionalLimit}
return &res, err
}
// GetNotionalLimitByChainID get a notional limit by chainID.
func (s *Service) GetNotionalLimitByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*services.Response[[]*NotionalLimitDetail], error) {
func (s *Service) GetNotionalLimitByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*response.Response[[]*NotionalLimitDetail], error) {
query := QueryNotionalLimit().SetPagination(p).SetChain(chainID)
notionalLimit, err := s.repo.GetNotionalLimitByChainID(ctx, query)
res := services.Response[[]*NotionalLimitDetail]{Data: notionalLimit, Error: err}
res := response.Response[[]*NotionalLimitDetail]{Data: notionalLimit}
return &res, err
}
// GetAvailableNotional get a available notional for each chainID.
func (s *Service) GetAvailableNotional(ctx context.Context, p *pagination.Pagination) (*services.Response[[]*NotionalAvailable], error) {
func (s *Service) GetAvailableNotional(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*NotionalAvailable], error) {
if p == nil {
p = pagination.FirstPage()
}
query := QueryNotionalLimit().SetPagination(p)
notionalAvailability, err := s.repo.GetAvailableNotional(ctx, query)
res := services.Response[[]*NotionalAvailable]{Data: notionalAvailability, Error: err}
res := response.Response[[]*NotionalAvailable]{Data: notionalAvailability}
return &res, err
}
// GetAvailableNotionalByChainID get a available notional by chainID.
func (s *Service) GetAvailableNotionalByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*services.Response[[]*NotionalAvailableDetail], error) {
func (s *Service) GetAvailableNotionalByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*response.Response[[]*NotionalAvailableDetail], error) {
query := QueryNotionalLimit().SetPagination(p).SetChain(chainID)
notionaLAvailability, err := s.repo.GetAvailableNotionalByChainID(ctx, query)
res := services.Response[[]*NotionalAvailableDetail]{Data: notionaLAvailability, Error: err}
res := response.Response[[]*NotionalAvailableDetail]{Data: notionaLAvailability}
return &res, err
}
// GetMaxNotionalAvailableByChainID get a maximun notional value by chainID.
func (s *Service) GetMaxNotionalAvailableByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*services.Response[*MaxNotionalAvailableRecord], error) {
func (s *Service) GetMaxNotionalAvailableByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*response.Response[*MaxNotionalAvailableRecord], error) {
query := QueryNotionalLimit().SetPagination(p).SetChain(chainID)
maxNotionaLAvailable, err := s.repo.GetMaxNotionalAvailableByChainID(ctx, query)
res := services.Response[*MaxNotionalAvailableRecord]{Data: maxNotionaLAvailable, Error: err}
res := response.Response[*MaxNotionalAvailableRecord]{Data: maxNotionaLAvailable}
return &res, err
}
// GetEnqueueVass.
func (s *Service) GetEnqueueVass(ctx context.Context, p *pagination.Pagination) (*services.Response[[]*EnqueuedVaas], error) {
// GetEnqueueVaas get all the enqueued vaa.
func (s *Service) GetEnqueueVass(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*EnqueuedVaas], error) {
if p == nil {
p = pagination.FirstPage()
}
query := QueryEnqueuedVaa().SetPagination(p)
enqueuedVaaResponse, err := s.repo.GetEnqueueVass(ctx, query)
res := services.Response[[]*EnqueuedVaas]{Data: enqueuedVaaResponse, Error: err}
res := response.Response[[]*EnqueuedVaas]{Data: enqueuedVaaResponse}
return &res, err
}
// GetEnqueueVassByChainID by chainID.
func (s *Service) GetEnqueueVassByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*services.Response[[]*EnqueuedVaaDetail], error) {
// GetEnqueueVassByChainID get enequeued vaa by chainID.
func (s *Service) GetEnqueueVassByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*response.Response[[]*EnqueuedVaaDetail], error) {
if p == nil {
p = pagination.FirstPage()
}
query := QueryEnqueuedVaa().SetPagination(p).SetChain(chainID)
enqueuedVaaRecord, err := s.repo.GetEnqueueVassByChainID(ctx, query)
res := services.Response[[]*EnqueuedVaaDetail]{Data: enqueuedVaaRecord, Error: err}
res := response.Response[[]*EnqueuedVaaDetail]{Data: enqueuedVaaRecord}
return &res, err
}
// GetGovernorLimit get governor limit.
func (s *Service) GetGovernorLimit(ctx context.Context, p *pagination.Pagination) (*services.Response[[]*GovernorLimit], error) {
func (s *Service) GetGovernorLimit(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*GovernorLimit], error) {
if p == nil {
p = pagination.FirstPage()
}
query := QueryGovernor().SetPagination(p)
governorLimit, err := s.repo.GetGovernorLimit(ctx, query)
res := services.Response[[]*GovernorLimit]{Data: governorLimit, Error: err}
res := response.Response[[]*GovernorLimit]{Data: governorLimit}
return &res, err
}

View File

@ -1,19 +1,19 @@
// Package observations handle the request of observations data from governor endpoint defined in the api.
package observations
import (
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/gofiber/fiber/v2"
"github.com/wormhole-foundation/wormhole-explorer/api/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"go.uber.org/zap"
)
// Controller definition.
type Controller struct {
srv *Service
logger *zap.Logger
}
// NewController create a new controler.
func NewController(srv *Service, logger *zap.Logger) *Controller {
return &Controller{
srv: srv,
@ -21,8 +21,9 @@ func NewController(srv *Service, logger *zap.Logger) *Controller {
}
}
// FindAll handler for the endpoint /observations/.
func (c *Controller) FindAll(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
p := middleware.GetPaginationFromContext(ctx)
obs, err := c.srv.FindAll(ctx.Context(), p)
if err != nil {
return err
@ -30,9 +31,13 @@ func (c *Controller) FindAll(ctx *fiber.Ctx) error {
return ctx.JSON(obs)
}
// FindAllByChain handler for the endpoint /observations/:chain.
func (c *Controller) FindAllByChain(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx)
p := middleware.GetPaginationFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx, c.logger)
if err != nil {
return err
}
obs, err := c.srv.FindByChain(ctx.Context(), chainID, p)
if err != nil {
return err
@ -40,12 +45,14 @@ func (c *Controller) FindAllByChain(ctx *fiber.Ctx) error {
return ctx.JSON(obs)
}
// FindAllByEmitter handler for the endpoint /observations/:chain/:emitter.
func (c *Controller) FindAllByEmitter(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, addr, _, err := middleware.ExtractVAAParams(ctx)
if errors.IsOf(err, middleware.ErrMalformedChain, middleware.ErrMalformedAddr) {
p := middleware.GetPaginationFromContext(ctx)
chainID, addr, err := middleware.ExtractVAAChainIDEmitter(ctx, c.logger)
if err != nil {
return err
}
obs, err := c.srv.FindByEmitter(ctx.Context(), chainID, addr, p)
if err != nil {
return err
@ -53,9 +60,10 @@ func (c *Controller) FindAllByEmitter(ctx *fiber.Ctx) error {
return ctx.JSON(obs)
}
// FindAllByVAA handler for the endpoint /observations/:chain/:emitter/:sequence
func (c *Controller) FindAllByVAA(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, addr, seq, err := middleware.ExtractVAAParams(ctx)
p := middleware.GetPaginationFromContext(ctx)
chainID, addr, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
if err != nil {
return err
}
@ -66,18 +74,21 @@ func (c *Controller) FindAllByVAA(ctx *fiber.Ctx) error {
return ctx.JSON(obs)
}
// FindOne handler for the endpoint /observations/:chain/:emitter/:sequence/:signer/:hash
func (c *Controller) FindOne(ctx *fiber.Ctx) error {
chainID, addr, seq, err := middleware.ExtractVAAParams(ctx)
chainID, addr, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
if err != nil {
return err
}
signer := ctx.Params("signer")
signerAddr, err := vaa.StringToAddress(signer)
signerAddr, err := middleware.ExtractObservationSigner(ctx, c.logger)
if err != nil {
return err
}
hash := ctx.Params("hash")
obs, err := c.srv.FindOne(ctx.Context(), chainID, addr, seq, &signerAddr, []byte(hash))
hash, err := middleware.ExtractObservationHash(ctx, c.logger)
if err != nil {
return err
}
obs, err := c.srv.FindOne(ctx.Context(), chainID, addr, seq, signerAddr, []byte(hash))
if err != nil {
return err
}

View File

@ -1,10 +1,13 @@
// Package observations handle the request of observations data from governor endpoint defined in the api.
package observations
import (
"github.com/certusone/wormhole/node/pkg/vaa"
"time"
"github.com/certusone/wormhole/node/pkg/vaa"
)
// ObservationDoc represent an observation document.
type ObservationDoc struct {
ID string `bson:"_id" json:"id"`
Version uint8 `bson:"version" json:"version"`
@ -15,7 +18,6 @@ type ObservationDoc struct {
TxHash []byte `bson:"txHash" json:"txHash"`
GuardianAddr string `bson:"guardianAddr" json:"guardianAddr"`
Signature []byte `bson:"signature" json:"signature"`
UpdatedAt *time.Time `bson:"updatedAt" json:"updatedAt"`
IndexedAt *time.Time `bson:"indexedAt" json:"indexedAt"`
UpdatedAt *time.Time `bson:"updatedAt" json:"updatedAt"`
IndexedAt *time.Time `bson:"indexedAt" json:"indexedAt"`
}

View File

@ -1,16 +1,21 @@
// Package observations handle the request of observations data from governor endpoint defined in the api.
package observations
import (
"context"
"errors"
"fmt"
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"github.com/pkg/errors"
errs "github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap"
)
// Repository definition.
type Repository struct {
db *mongo.Database
logger *zap.Logger
@ -19,6 +24,7 @@ type Repository struct {
}
}
// NewRepository create a new Repository.
func NewRepository(db *mongo.Database, logger *zap.Logger) *Repository {
return &Repository{db: db,
logger: logger.With(zap.String("module", "ObservationsRepository")),
@ -26,6 +32,8 @@ func NewRepository(db *mongo.Database, logger *zap.Logger) *Repository {
}
}
// Find get a list of ObservationDoc pointers.
// The input parameter [q *ObservationQuery] define the filters to apply in the query.
func (r *Repository) Find(ctx context.Context, q *ObservationQuery) ([]*ObservationDoc, error) {
if q == nil {
q = Query()
@ -33,32 +41,43 @@ func (r *Repository) Find(ctx context.Context, q *ObservationQuery) ([]*Observat
sort := bson.D{{q.SortBy, q.GetSortInt()}}
cur, err := r.collections.observations.Find(ctx, q.toBSON(), options.Find().SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort))
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Find command to get observations",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
var obs []*ObservationDoc
err = cur.All(ctx, &obs)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*ObservationDoc", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return obs, err
}
var (
ErrWrongQuery = errors.New("MALFORMED_QUERY")
)
// Find get ObservationDoc pointer.
// The input parameter [q *ObservationQuery] define the filters to apply in the query.
func (r *Repository) FindOne(ctx context.Context, q *ObservationQuery) (*ObservationDoc, error) {
if q == nil {
return nil, ErrWrongQuery
return nil, errs.ErrMalformedQuery
}
var obs ObservationDoc
err := r.collections.observations.FindOne(ctx, q.toBSON()).Decode(&obs)
if err != nil {
return nil, err
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, errs.ErrNotFound
}
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute FindOne command to get observations",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return &obs, err
}
// ObservationQuery respresent a query for the observation mongodb document.
type ObservationQuery struct {
pagination.Pagination
chainId vaa.ChainID
@ -69,36 +88,43 @@ type ObservationQuery struct {
uint64
}
// Query create a new ObservationQuery with default pagination vaues.
func Query() *ObservationQuery {
page := pagination.FirstPage()
return &ObservationQuery{Pagination: *page}
}
// SetEmitter set the chainId field of the ObservationQuery struct.
func (q *ObservationQuery) SetChain(chainID vaa.ChainID) *ObservationQuery {
q.chainId = chainID
return q
}
// SetEmitter set the emitter field of the ObservationQuery struct.
func (q *ObservationQuery) SetEmitter(emitter string) *ObservationQuery {
q.emitter = emitter
return q
}
// SetSequence set the sequence field of the ObservationQuery struct.
func (q *ObservationQuery) SetSequence(seq uint64) *ObservationQuery {
q.sequence = seq
return q
}
// SetGuardianAddr set the guardianAddr field of the ObservationQuery struct.
func (q *ObservationQuery) SetGuardianAddr(guardianAddr string) *ObservationQuery {
q.guardianAddr = guardianAddr
return q
}
// SetHash set the hash field of the ObservationQuery struct.
func (q *ObservationQuery) SetHash(hash []byte) *ObservationQuery {
q.hash = hash
return q
}
// SetPagination set the pagination field of the ObservationQuery struct.
func (q *ObservationQuery) SetPagination(p *pagination.Pagination) *ObservationQuery {
q.Pagination = *p
return q

View File

@ -1,40 +1,49 @@
// Package observations handle the request of observations data from governor endpoint defined in the api.
package observations
import (
"context"
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
"go.uber.org/zap"
)
// Service definition.
type Service struct {
repo *Repository
logger *zap.Logger
}
// NewService create a new Service.
func NewService(dao *Repository, logger *zap.Logger) *Service {
return &Service{repo: dao, logger: logger.With(zap.String("module", "ObservationsService"))}
}
// FindAll get all the observations.
func (s *Service) FindAll(ctx context.Context, p *pagination.Pagination) ([]*ObservationDoc, error) {
return s.repo.Find(ctx, Query().SetPagination(p))
}
// FindByChain get all the observations by chainID.
func (s *Service) FindByChain(ctx context.Context, chain vaa.ChainID, p *pagination.Pagination) ([]*ObservationDoc, error) {
query := Query().SetChain(chain).SetPagination(p)
return s.repo.Find(ctx, query)
}
// FindByEmitter get all the observations by chainID and emitter address.
func (s *Service) FindByEmitter(ctx context.Context, chain vaa.ChainID, emitter *vaa.Address, p *pagination.Pagination) ([]*ObservationDoc, error) {
query := Query().SetChain(chain).SetEmitter(emitter.String()).SetPagination(p)
return s.repo.Find(ctx, query)
}
// FindByVAA get all the observations for a VAA (chainID, emitter addrress and sequence number).
func (s *Service) FindByVAA(ctx context.Context, chain vaa.ChainID, emitter *vaa.Address, seq uint64, p *pagination.Pagination) ([]*ObservationDoc, error) {
query := Query().SetChain(chain).SetEmitter(emitter.String()).SetSequence(seq).SetPagination(p)
return s.repo.Find(ctx, query)
}
// FindOne get a observation by chainID, emitter address, sequence, signer address and hash.
func (s *Service) FindOne(ctx context.Context, chainID vaa.ChainID, emitterAddr *vaa.Address, seq uint64, signerAddr *vaa.Address, hash []byte) (*ObservationDoc, error) {
query := Query().SetChain(chainID).SetEmitter(emitterAddr.String()).SetSequence(seq).SetGuardianAddr(signerAddr.String()).SetHash(hash)
return s.repo.FindOne(ctx, query)

View File

@ -1,83 +1,86 @@
// Package observations handle the request of VAA data from governor endpoint defined in the api.
package vaa
import (
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/wormhole-foundation/wormhole-explorer/api/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"go.uber.org/zap"
)
// Controller definition.
type Controller struct {
srv *Service
logger *zap.Logger
}
// NewController create a new controler.
func NewController(serv *Service, logger *zap.Logger) *Controller {
return &Controller{srv: serv, logger: logger.With(zap.String("module", "VaaController"))}
}
// FindAll handler for the endpoint /vaas/.
func (c *Controller) FindAll(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
p := middleware.GetPaginationFromContext(ctx)
vaas, err := c.srv.FindAll(ctx.Context(), p)
if err != nil {
fmt.Printf("error finding vaas: %v", err)
return err
}
return ctx.JSON(vaas)
}
// FindByChain handler for the endpoint /vaas/:chain.
func (c *Controller) FindByChain(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx)
fmt.Println(ctx.Locals("requestid"))
p := middleware.GetPaginationFromContext(ctx)
chainID, err := middleware.ExtractChainID(ctx, c.logger)
if err != nil {
return err
}
vaas, err := c.srv.FindByChain(ctx.Context(), chainID, p)
if err != nil {
fmt.Printf("error finding vaas: %v", err)
return err
}
return ctx.JSON(vaas)
}
// FindByEmitter handler for the endpoint /vaas/:chain/:emitter.
func (c *Controller) FindByEmitter(ctx *fiber.Ctx) error {
p := pagination.GetFromContext(ctx)
chainID, emitter, _, err := middleware.ExtractVAAParams(ctx)
if errors.IsOf(err, middleware.ErrMalformedChain, middleware.ErrMalformedAddr) {
p := middleware.GetPaginationFromContext(ctx)
chainID, emitter, err := middleware.ExtractVAAChainIDEmitter(ctx, c.logger)
if err != nil {
return err
}
vaas, err := c.srv.FindByEmitter(ctx.Context(), chainID, *emitter, p)
if err != nil {
//TODO logging
fmt.Printf("error finding vaas: %v", err)
return err
}
return ctx.JSON(vaas)
}
// FindById handler for the endpoint /vaas/:chain/:emitter/:sequence/:signer/:hash.
func (c *Controller) FindById(ctx *fiber.Ctx) error {
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx)
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
if err != nil {
return err
}
vaa, err := c.srv.FindById(ctx.Context(), chainID, *emitter, seq)
if err != nil {
//TODO logging
fmt.Printf("error finding vaa: %v", err)
return err
}
return ctx.JSON(vaa)
}
// FindSignedVAAByID get a signedVAA []byte from a chainID, emitter address and sequence.
func (c *Controller) FindSignedVAAByID(ctx *fiber.Ctx) error {
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx)
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
if err != nil {
// TODO: Handle InvalidArgument code (3) in the response.
return err
}
vaa, err := c.srv.FindById(ctx.Context(), chainID, *emitter, seq)
if err != nil {
// TODO: handle not found(5) and internal(13) response code.
return err
}
response := struct {

View File

@ -1,10 +1,12 @@
package vaa
import (
"github.com/certusone/wormhole/node/pkg/vaa"
"time"
"github.com/certusone/wormhole/node/pkg/vaa"
)
// VaaDoc represent an vaa document.
type VaaDoc struct {
ID string `bson:"_id" json:"id"`
Version uint8 `bson:"version" json:"version"`

View File

@ -2,15 +2,19 @@ package vaa
import (
"context"
"fmt"
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"github.com/pkg/errors"
errs "github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap"
)
// Repository definition
type Repository struct {
db *mongo.Database
logger *zap.Logger
@ -20,6 +24,7 @@ type Repository struct {
}
}
// NewRepository create a new Repository.
func NewRepository(db *mongo.Database, logger *zap.Logger) *Repository {
return &Repository{db: db,
logger: logger.With(zap.String("module", "VaaRepository")),
@ -29,6 +34,8 @@ func NewRepository(db *mongo.Database, logger *zap.Logger) *Repository {
}{vaas: db.Collection("vaas"), invalidVaas: db.Collection("invalid_vaas")}}
}
// Find get a list of *VaaDoc.
// The input parameter [q *VaaQuery] define the filters to apply in the query.
func (r *Repository) Find(ctx context.Context, q *VaaQuery) ([]*VaaDoc, error) {
if q == nil {
q = Query()
@ -36,22 +43,37 @@ func (r *Repository) Find(ctx context.Context, q *VaaQuery) ([]*VaaDoc, error) {
sort := bson.D{{q.SortBy, q.GetSortInt()}}
cur, err := r.collections.vaas.Find(ctx, q.toBSON(), options.Find().SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort))
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Find command to get vaas",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
var vaas []*VaaDoc
err = cur.All(ctx, &vaas)
if err != nil {
return nil, err
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed decoding cursor to []*VaaDoc", zap.Error(err), zap.Any("q", q),
zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return vaas, err
}
// FindOne get *VaaDoc.
// The input parameter [q *VaaQuery] define the filters to apply in the query.
func (r *Repository) FindOne(ctx context.Context, q *VaaQuery) (*VaaDoc, error) {
var vaaDoc VaaDoc
err := r.collections.vaas.FindOne(ctx, q.toBSON()).Decode(&vaaDoc)
if err != nil {
return nil, err
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, errs.ErrNotFound
}
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute FindOne command to get vaas",
zap.Error(err), zap.Any("q", q), zap.String("requestID", requestID))
return nil, errors.WithStack(err)
}
return &vaaDoc, err
}
@ -64,6 +86,9 @@ func (r *Repository) FindStats(ctx context.Context) ([]*VaaStats, error) {
}
c, err := r.collections.vaas.Aggregate(ctx, mongo.Pipeline{group})
if err != nil {
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
r.logger.Error("failed execute Aggregate command to get vaa stats",
zap.Error(err), zap.String("requestID", requestID))
return nil, err
}
var stats []*VaaStats
@ -71,6 +96,7 @@ func (r *Repository) FindStats(ctx context.Context) ([]*VaaStats, error) {
return stats, err
}
// VaaQuery respresent a query for the vaa mongodb document.
type VaaQuery struct {
pagination.Pagination
chainId vaa.ChainID
@ -78,26 +104,31 @@ type VaaQuery struct {
sequence uint64
}
// Query create a new VaaQuery with default pagination vaues.
func Query() *VaaQuery {
page := pagination.FirstPage()
return &VaaQuery{Pagination: *page}
}
// SetChain set the chainId field of the VaaQuery struct.
func (q *VaaQuery) SetChain(chainID vaa.ChainID) *VaaQuery {
q.chainId = chainID
return q
}
// SetEmitter set the emitter field of the VaaQuery struct.
func (q *VaaQuery) SetEmitter(emitter string) *VaaQuery {
q.emitter = emitter
return q
}
// SetSequence set the sequence field of the VaaQuery struct.
func (q *VaaQuery) SetSequence(seq uint64) *VaaQuery {
q.sequence = seq
return q
}
// SetPagination set the pagination field of the VaaQuery struct.
func (q *VaaQuery) SetPagination(p *pagination.Pagination) *VaaQuery {
q.Pagination = *p
return q

View File

@ -4,54 +4,60 @@ import (
"context"
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"github.com/wormhole-foundation/wormhole-explorer/api/services"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
"github.com/wormhole-foundation/wormhole-explorer/api/response"
"go.uber.org/zap"
)
// Service definition.
type Service struct {
repo *Repository
logger *zap.Logger
}
// NewService create a new Service.
func NewService(r *Repository, logger *zap.Logger) *Service {
return &Service{repo: r, logger: logger.With(zap.String("module", "VaaService"))}
}
func (s *Service) FindAll(ctx context.Context, p *pagination.Pagination) (*services.Response[[]*VaaDoc], error) {
// FindAll get all the the vaa.
func (s *Service) FindAll(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*VaaDoc], error) {
if p == nil {
p = pagination.FirstPage()
}
query := Query().SetPagination(p)
vaas, err := s.repo.Find(ctx, query)
res := services.Response[[]*VaaDoc]{Data: vaas, Error: err}
res := response.Response[[]*VaaDoc]{Data: vaas}
return &res, err
}
func (s *Service) FindByChain(ctx context.Context, chain vaa.ChainID, p *pagination.Pagination) (*services.Response[[]*VaaDoc], error) {
// FindByChain get all the vaa by chainID.
func (s *Service) FindByChain(ctx context.Context, chain vaa.ChainID, p *pagination.Pagination) (*response.Response[[]*VaaDoc], error) {
query := Query().SetChain(chain).SetPagination(p)
vaas, err := s.repo.Find(ctx, query)
res := services.Response[[]*VaaDoc]{Data: vaas, Error: err}
res := response.Response[[]*VaaDoc]{Data: vaas}
return &res, err
}
func (s *Service) FindByEmitter(ctx context.Context, chain vaa.ChainID, emitter vaa.Address, p *pagination.Pagination) (*services.Response[[]*VaaDoc], error) {
// FindByEmitter get all the vaa by chainID and emitter address.
func (s *Service) FindByEmitter(ctx context.Context, chain vaa.ChainID, emitter vaa.Address, p *pagination.Pagination) (*response.Response[[]*VaaDoc], error) {
query := Query().SetChain(chain).SetEmitter(emitter.String()).SetPagination(p)
vaas, err := s.repo.Find(ctx, query)
res := services.Response[[]*VaaDoc]{Data: vaas, Error: err}
res := response.Response[[]*VaaDoc]{Data: vaas}
return &res, err
}
func (s *Service) FindById(ctx context.Context, chain vaa.ChainID, emitter vaa.Address, seq uint64) (*services.Response[*VaaDoc], error) {
// FindById get a vaa by chainID, emitter address and sequence number.
func (s *Service) FindById(ctx context.Context, chain vaa.ChainID, emitter vaa.Address, seq uint64) (*response.Response[*VaaDoc], error) {
query := Query().SetChain(chain).SetEmitter(emitter.String()).SetSequence(seq)
vaas, err := s.repo.FindOne(ctx, query)
res := services.Response[*VaaDoc]{Data: vaas, Error: err}
res := response.Response[*VaaDoc]{Data: vaas}
return &res, err
}
func (s *Service) GetVAAStats(ctx context.Context) (*services.Response[[]*VaaStats], error) {
func (s *Service) GetVAAStats(ctx context.Context) (*response.Response[[]*VaaStats], error) {
stats, err := s.repo.FindStats(ctx)
res := services.Response[[]*VaaStats]{Data: stats, Error: err}
res := response.Response[[]*VaaStats]{Data: stats}
return &res, err
}

View File

@ -1,13 +1,23 @@
// Package config implement a simple configuration package.
// It define a type [AppConfig] that represent the aplication configuration and
// use viper [https://github.com/spf13/viper] to load the configuration.
package config
import (
"bytes"
"encoding/json"
"strings"
ipfslog "github.com/ipfs/go-log/v2"
"github.com/spf13/viper"
"strings"
)
const (
RunModeProduction = "PRODUCTION"
RunModeDevelopmernt = "DEVELOPMENT"
)
// AppConfig defines the configuration for the app.
type AppConfig struct {
DB struct {
URL string
@ -15,11 +25,12 @@ type AppConfig struct {
Name string
}
PORT int
PORT int
LogLevel string
RunMode string
}
// GetLogLevel get zapcore.Level define in the configuraion.
func (cfg *AppConfig) GetLogLevel() (ipfslog.LogLevel, error) {
return ipfslog.LevelFromString(cfg.LogLevel)
}
@ -27,6 +38,8 @@ func (cfg *AppConfig) GetLogLevel() (ipfslog.LogLevel, error) {
func init() {
viper.SetDefault("port", 8000)
viper.SetDefault("loglevel", "INFO")
viper.SetDefault("runmode", "PRODUCTION")
// Consider environment variables in unmarshall doesn't work unless doing this: https://github.com/spf13/viper/issues/188#issuecomment-1168898503
b, err := json.Marshal(AppConfig{})
if err != nil {
@ -50,6 +63,7 @@ func init() {
viper.AutomaticEnv()
}
// Get returns the app configuration.
func Get() (*AppConfig, error) {
var cfg AppConfig
err := viper.Unmarshal(&cfg)

View File

@ -1,11 +1,14 @@
// Package db handle mongodb connections.
package db
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// Connect create a new mongo db client for the options defined in the input param url.
func Connect(ctx context.Context, url string) (*mongo.Client, error) {
cli, err := mongo.NewClient(options.Client().ApplyURI(url))
if err != nil {

View File

@ -0,0 +1,10 @@
package errors
import "errors"
// Error definitions to use in service and repository layers.
var (
ErrMalformedQuery = errors.New("MALFORMED_QUERY")
ErrNotFound = errors.New("NOT FOUND")
ErrInternalError = errors.New("INTERNAL ERROR")
)

View File

@ -1,5 +1,6 @@
package pagination
// Pagination definition.
type Pagination struct {
Offset int64
PageSize int64
@ -7,41 +8,49 @@ type Pagination struct {
SortBy string
}
// FirstPage return a *Pagination with default values offset and page size.
func FirstPage() *Pagination {
return &Pagination{Offset: 0, PageSize: 50}
}
// BuildPagination create a new *Pagination.
func BuildPagination(page, pageSize int64, sortOrder, sortBy string) *Pagination {
p := Pagination{}
p.SetPage(page).SetPageSize(pageSize).SetSortOrder(sortOrder).SetSortBy(sortBy)
return &p
}
// SetPageSize set the PageSize field of the Pagination struct.
func (p *Pagination) SetPageSize(limit int64) *Pagination {
p.PageSize = limit
return p
}
// SetOffset set the Offset field of the Pagination struct.
func (p *Pagination) SetOffset(offset int64) *Pagination {
p.Offset = offset
return p
}
// SetPage set the Page field of the Pagination struct.
func (p *Pagination) SetPage(page int64) *Pagination {
p.Offset = page * p.PageSize
return p
}
// SetSortOrder set the SortOrder field of the Pagination struct.
func (p *Pagination) SetSortOrder(order string) *Pagination {
p.SortOrder = order
return p
}
// SetSortBy set the SortBy field of the Pagination struct.
func (p *Pagination) SetSortBy(by string) *Pagination {
p.SortBy = by
return p
}
// GetSortInt mapping to mongodb sort values.
func (p *Pagination) GetSortInt() int {
if p.SortOrder == "ASC" {
return 1

View File

@ -14,12 +14,13 @@ import (
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/requestid"
ipfslog "github.com/ipfs/go-log/v2"
"github.com/wormhole-foundation/wormhole-explorer/api/config"
"github.com/wormhole-foundation/wormhole-explorer/api/db"
"github.com/wormhole-foundation/wormhole-explorer/api/governor"
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/governor"
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/observations"
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/vaa"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/config"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/db"
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
"github.com/wormhole-foundation/wormhole-explorer/api/observations"
"github.com/wormhole-foundation/wormhole-explorer/api/vaa"
"github.com/wormhole-foundation/wormhole-explorer/api/response"
)
var cacheConfig = cache.Config{
@ -54,7 +55,7 @@ func main() {
panic(err)
}
rootLogger := ipfslog.Logger("wormhole-spy").Desugar()
rootLogger := ipfslog.Logger("wormhole-api").Desugar()
ipfslog.SetAllLoggers(lvl)
// Setup DB
@ -79,8 +80,9 @@ func main() {
observationsCtrl := observations.NewController(obsService, rootLogger)
governorCtrl := governor.NewController(governorService, rootLogger)
// Setup API
app := fiber.New()
// Setup app with custom error handling.
response.SetEnableStackTrace(*cfg)
app := fiber.New(fiber.Config{ErrorHandler: middleware.ErrorHandler})
// Middleware
prometheus := fiberprometheus.New("wormscan")

View File

@ -0,0 +1,32 @@
// package middleare contains all the middleware function to use in the API.
package middleware
import (
"github.com/pkg/errors"
errs "github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/response"
"github.com/gofiber/fiber/v2"
)
// ErrorHandler define a fiber custom error handler. This function process all errors
// returned from any handlers in the stack.
//
// To setup this function we must set the ErrorHandler field of the fiber.Config struct
// with this function and create a new fiber with this config.
//
// example: fiber.New(fiber.Config{ErrorHandler: errs.APIErrorHandler}
func ErrorHandler(ctx *fiber.Ctx, err error) error {
var apiError response.APIError
switch {
case errors.As(err, &apiError):
ctx.Status(apiError.StatusCode).JSON(apiError)
case errors.Is(err, errs.ErrNotFound):
apiError = response.NewNotFoundError(ctx)
ctx.Status(fiber.StatusNotFound).JSON(apiError)
default:
apiError = response.NewInternalError(ctx, err)
ctx.Status(fiber.StatusInternalServerError).JSON(apiError)
}
return nil
}

View File

@ -1,15 +1,50 @@
// package middleare contains all the middleware function to use in the API.
package middleware
import (
"github.com/gofiber/fiber/v2"
"github.com/wormhole-foundation/wormhole-explorer/api/pagination"
"net/http"
"strconv"
"github.com/gofiber/fiber/v2"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
)
// ExtractPagination middleware invoke pagination.ExtractPagination.
func ExtractPagination(c *fiber.Ctx) error {
if c.Method() != http.MethodGet {
return c.Next()
}
pagination.ExtractPagination(c)
extractPagination(c)
return c.Next()
}
// extractPagination get pagination query params and build a *Pagination.
func extractPagination(ctx *fiber.Ctx) (*pagination.Pagination, error) {
pageNumberStr := ctx.Query("page", "0")
pageNumber, err := strconv.ParseInt(pageNumberStr, 10, 64)
if err != nil {
return nil, err
}
pageSizeStr := ctx.Query("pageSize", "50")
pageSize, err := strconv.ParseInt(pageSizeStr, 10, 64)
if err != nil {
return nil, err
}
sortOrder := ctx.Query("sortOrder", "DESC")
sortBy := ctx.Query("sortBy", "indexedAt")
p := pagination.BuildPagination(pageNumber, pageSize, sortOrder, sortBy)
ctx.Locals("pagination", p)
return p, nil
}
// GetPaginationFromContext get pagination from context.
func GetPaginationFromContext(ctx *fiber.Ctx) *pagination.Pagination {
p := ctx.Locals("pagination")
if p == nil {
return nil
}
return p.(*pagination.Pagination)
}

View File

@ -1,67 +1,114 @@
// package middleare contains all the middleware function to use in the API.
package middleware
import (
"errors"
"fmt"
"strconv"
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/gofiber/fiber/v2"
"github.com/pkg/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/response"
"go.uber.org/zap"
)
var (
ErrMalformedChain = errors.New("WRONG_CHAIN_ID")
ErrMalformedAddr = errors.New("MALFORMED_EMITTER_ADDR")
ErrMalformedSequence = errors.New("MALFORMED_SEQUENCE_NUMBER")
ErrMalFormedGuardianAddress = errors.New("MALFORMED_GUARDIAN_ADDR")
)
func ExtractChainID(c *fiber.Ctx) (vaa.ChainID, error) {
// ExtractChainID get chain parameter from route path.
func ExtractChainID(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, error) {
chain, err := c.ParamsInt("chain")
if err != nil {
return vaa.ChainIDUnset, ErrMalformedChain
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
l.Error("failed to get chain parameter", zap.Error(err), zap.Int("chain", chain),
zap.String("requestID", requestID))
return vaa.ChainIDUnset, response.NewInvalidParamError(c, "WRONG CHAIN ID", errors.WithStack(err))
}
return vaa.ChainID(chain), nil
}
func ExtractEmitterAddr(c *fiber.Ctx) (*vaa.Address, error) {
// ExtractEmitterAddr get emitter parameter from route path.
func ExtractEmitterAddr(c *fiber.Ctx, l *zap.Logger) (*vaa.Address, error) {
emitterStr := c.Params("emitter")
emitter, err := vaa.StringToAddress(emitterStr)
if err != nil {
return nil, ErrMalformedAddr
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
l.Error("failed to covert emitter to address", zap.Error(err), zap.String("emitterStr", emitterStr),
zap.String("requestID", requestID))
return nil, response.NewInvalidParamError(c, "MALFORMED EMITTER_ADDR", errors.WithStack(err))
}
return &emitter, nil
}
func ExtractSequence(c *fiber.Ctx) (uint64, error) {
// ExtractSequence get sequence parameter from route path.
func ExtractSequence(c *fiber.Ctx, l *zap.Logger) (uint64, error) {
sequence := c.Params("sequence")
seq, err := strconv.ParseUint(sequence, 10, 64)
if err != nil {
return 0, err
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
l.Error("failed to get sequence parameter", zap.Error(err), zap.String("sequence", sequence),
zap.String("requestID", requestID))
return 0, response.NewInvalidParamError(c, "MALFORMED SEQUENCE NUMBER", errors.WithStack(err))
}
return seq, nil
}
func ExtractGuardianAddress(c *fiber.Ctx) (string, error) {
// ExtractGuardianAddress get guardian address from route path.
func ExtractGuardianAddress(c *fiber.Ctx, l *zap.Logger) (string, error) {
//TODO: check guardianAddress [vaa.StringToAddress(emitterStr)]
guardianAddress := c.Params("guardian_address")
if guardianAddress == "" {
return "", ErrMalFormedGuardianAddress
return "", response.NewInvalidParamError(c, "MALFORMED GUARDIAN ADDR", nil)
}
return guardianAddress, nil
}
func ExtractVAAParams(c *fiber.Ctx) (vaa.ChainID, *vaa.Address, uint64, error) {
chainID, err := ExtractChainID(c)
// ExtractVAAParams get VAA chain, address from route path.
func ExtractVAAChainIDEmitter(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, *vaa.Address, error) {
chainID, err := ExtractChainID(c, l)
if err != nil {
return vaa.ChainIDUnset, nil, err
}
address, err := ExtractEmitterAddr(c, l)
if err != nil {
return chainID, nil, err
}
return chainID, address, nil
}
// ExtractVAAParams get VAAA chain, address and sequence from route path.
func ExtractVAAParams(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, *vaa.Address, uint64, error) {
chainID, err := ExtractChainID(c, l)
if err != nil {
return vaa.ChainIDUnset, nil, 0, err
}
address, err := ExtractEmitterAddr(c)
address, err := ExtractEmitterAddr(c, l)
if err != nil {
return chainID, nil, 0, err
}
seq, err := ExtractSequence(c)
seq, err := ExtractSequence(c, l)
if err != nil {
return chainID, address, 0, err
}
return chainID, address, seq, nil
}
// ExtractObservationSigner get signer from route path.
func ExtractObservationSigner(c *fiber.Ctx, l *zap.Logger) (*vaa.Address, error) {
signer := c.Params("signer")
signerAddr, err := vaa.StringToAddress(signer)
if err != nil {
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
l.Error("failed to covert signer to address", zap.Error(err), zap.String("signer", signer),
zap.String("requestID", requestID))
return nil, response.NewInvalidParamError(c, "MALFORMED SIGNER", errors.WithStack(err))
}
return &signerAddr, nil
}
// ExtractObservationHash get a hash from route path.
func ExtractObservationHash(c *fiber.Ctx, l *zap.Logger) (string, error) {
hash := c.Params("hash")
if hash == "" {
return "", response.NewInvalidParamError(c, "MALFORMED HASH", nil)
}
return hash, nil
}

View File

@ -1,35 +0,0 @@
package pagination
import (
"github.com/gofiber/fiber/v2"
"strconv"
)
func ExtractPagination(ctx *fiber.Ctx) (*Pagination, error) {
pageNumberStr := ctx.Query("page", "0")
pageNumber, err := strconv.ParseInt(pageNumberStr, 10, 64)
if err != nil {
return nil, err
}
pageSizeStr := ctx.Query("pageSize", "50")
pageSize, err := strconv.ParseInt(pageSizeStr, 10, 64)
if err != nil {
return nil, err
}
sortOrder := ctx.Query("sortOrder", "DESC")
sortBy := ctx.Query("sortBy", "indexedAt")
p := BuildPagination(pageNumber, pageSize, sortOrder, sortBy)
ctx.Locals("pagination", p)
return p, nil
}
func GetFromContext(ctx *fiber.Ctx) *Pagination {
p := ctx.Locals("pagination")
if p == nil {
return nil
}
return p.(*Pagination)
}

113
api/response/error.go Normal file
View File

@ -0,0 +1,113 @@
// The response package defines the success and error response type.
// It define a type [AppError] that represent the api error response.
// Its define a custom error handling for the api.
package response
import (
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/config"
)
// API error codes. These error code are the same used in guardian API.
const (
InvalidParam = 3
NotFound = 5
Internal = 13
)
var enableStackTrace bool
// SetEnableStackTrace enable/disable send the stacktrace field in the response.
func SetEnableStackTrace(cfg config.AppConfig) {
if cfg.RunMode == config.RunModeDevelopmernt {
enableStackTrace = true
return
}
enableStackTrace = false
}
// APIError api error response.
// This structure is defined to be aligned with the way the guardian API handles the error response.
type APIError struct {
StatusCode int `json:"-"`
Code int `json:"code"` // support to guardian-api code.
Message string `json:"message"`
Details []ErrorDetail `json:"details"`
}
// ErrorDetail definition.
// This structure contains the requestID and the stacktrace of the error.
type ErrorDetail struct {
RequestID string `json:"request_id"`
StackTrace string `json:"stack_trace,omitempty"`
}
// Error interface implementation.
func (a APIError) Error() string {
return fmt.Sprintf("code: %d, message: %s, details: %v", a.Code, a.Message, a.Details)
}
// NewApiError create a new api response.
func NewApiError(ctx *fiber.Ctx, statusCode, code int, message string, err error) APIError {
detail := ErrorDetail{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}
if enableStackTrace && err != nil {
detail.StackTrace = fmt.Sprintf("%+v\n", err)
}
return APIError{
StatusCode: fiber.StatusBadRequest,
Code: InvalidParam,
Message: message,
Details: []ErrorDetail{detail},
}
}
// NewInvalidParamError create a invalid param Error.
func NewInvalidParamError(ctx *fiber.Ctx, message string, err error) APIError {
if message == "" {
message = "INVALID PARAM"
}
detail := ErrorDetail{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}
if enableStackTrace && err != nil {
detail.StackTrace = fmt.Sprintf("%+v\n", err)
}
return APIError{
StatusCode: fiber.StatusBadRequest,
Code: InvalidParam,
Message: message,
Details: []ErrorDetail{detail},
}
}
// NewInternalError create a new APIError for Internal Errors.
func NewInternalError(ctx *fiber.Ctx, err error) APIError {
detail := ErrorDetail{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}
if enableStackTrace && err != nil {
detail.StackTrace = fmt.Sprintf("%+v\n", err)
}
return APIError{
StatusCode: fiber.StatusInternalServerError,
Code: Internal,
Message: "INTERNAL ERROR",
Details: []ErrorDetail{detail},
}
}
// NewNotFoundError create a new APIError for Not Found errors.
func NewNotFoundError(ctx *fiber.Ctx) APIError {
return APIError{
StatusCode: fiber.StatusNotFound,
Code: NotFound,
Message: "NOT FOUND",
Details: []ErrorDetail{{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}},
}
}

View File

@ -1,11 +1,13 @@
package services
// The response package defines the success and error response type.
package response
// ResponsePagination definition.
type ResponsePagination struct {
Next string `json:"next"`
}
// Response represent a success API response.
type Response[T any] struct {
Data T `json:"data"`
Error error `json:"error"`
Pagination ResponsePagination `json:"pagination"`
}