Files
3a9331169b Update avalanchego to v1.9.0-fuji (#472)
Co-authored-by: dhrubabasu <[email protected]>
Co-authored-by: Patrick O'Grady <[email protected]>
Co-authored-by: Stephen <[email protected]>
2022-10-02 20:20:49 -04:00

144 lines
3.9 KiB
Go

// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package api
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/ortelius/cfg"
"github.com/ava-labs/ortelius/services/indexes/avax"
"github.com/ava-labs/ortelius/services/indexes/params"
"github.com/ava-labs/ortelius/servicesctrl"
"github.com/ava-labs/ortelius/utils"
"github.com/gocraft/web"
"go.uber.org/zap"
)
var (
// ErrCacheableFnFailed is returned when the execution of a CacheableFn
// fails.
ErrCacheableFnFailed = errors.New("failed to load resource")
)
// Context is the base context for APIs in the ortelius systems
type Context struct {
sc *servicesctrl.Control
networkID uint32
avaxAssetID ids.ID
delayCache *utils.DelayCache
avaxReader *avax.Reader
connections *utils.Connections
}
// NetworkID returns the networkID this request is for
func (c *Context) NetworkID() uint32 {
return c.networkID
}
func (c *Context) cacheGet(key string) ([]byte, error) {
ctxget, cancelFnGet := context.WithTimeout(context.Background(), cfg.CacheTimeout)
defer cancelFnGet()
// Get from cache or, if there is a cache miss, from the cacheablefn
return c.delayCache.Cache.Get(ctxget, key)
}
func (c *Context) cacheRun(reqTime time.Duration, cacheable utils.Cacheable) (interface{}, error) {
ctxreq, cancelFnReq := context.WithTimeout(context.Background(), reqTime)
defer cancelFnReq()
return cacheable.CacheableFn(ctxreq)
}
// WriteCacheable writes to the http response the output of the given Cacheable's
// function, either from the cache or from a new execution of the function
func (c *Context) WriteCacheable(w http.ResponseWriter, cacheable utils.Cacheable) {
key := utils.CacheKey(c.NetworkID(), cacheable.Key...)
// Get from cache or, if there is a cache miss, from the cacheablefn
resp, err := c.cacheGet(key)
switch err {
case nil:
default:
var obj interface{}
obj, err = c.cacheRun(cfg.RequestTimeout, cacheable)
if err == nil {
resp, err = json.Marshal(obj)
if err == nil {
c.delayCache.Worker.TryEnque(&utils.CacheJob{Key: key, Body: &resp, TTL: cacheable.TTL})
}
}
}
// Write error or response
if err != nil {
c.sc.Log.Warn("server error",
zap.Error(err),
)
c.WriteErr(w, 500, ErrCacheableFnFailed)
return
}
WriteJSON(w, resp)
}
// WriteErr writes an error response to the http response
func (c *Context) WriteErr(w http.ResponseWriter, code int, err error) {
errBytes, err := json.Marshal(&ErrorResponse{
Code: code,
Message: err.Error(),
})
if err != nil {
w.WriteHeader(500)
c.sc.Log.Warn("marshal error",
zap.Error(err),
)
return
}
w.WriteHeader(code)
fmt.Fprint(w, string(errBytes))
}
func (*Context) setHeaders(w web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) {
h := w.Header()
h.Add("access-control-allow-headers", "Accept, Content-Type, Content-Length, Accept-Encoding")
h.Add("access-control-allow-methods", "GET")
h.Add("access-control-allow-origin", "*")
h.Add("Content-Type", "application/json")
next(w, r)
}
func (*Context) notFoundHandler(w web.ResponseWriter, r *web.Request) {
WriteErr(w, 404, "Not Found")
}
func (c *Context) cacheKeyForID(name string, id string) []string {
return []string{"avax", name, params.CacheKey("id", id)}
}
func (c *Context) cacheKeyForParams(name string, p params.Param) []string {
return append([]string{"avax", name}, p.CacheKey()...)
}
func newContextSetter(sc *servicesctrl.Control, networkID uint32, connections *utils.Connections, delayCache *utils.DelayCache) func(*Context, web.ResponseWriter, *web.Request, web.NextMiddlewareFunc) {
return func(c *Context, w web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) {
c.sc = sc
c.connections = connections
c.delayCache = delayCache
c.networkID = networkID
// Execute handler
next(w, r)
}
}