mirror of
https://github.com/luxfi/indexer.git
synced 2026-09-24 00:25:37 +00:00
Co-authored-by: dhrubabasu <[email protected]> Co-authored-by: Patrick O'Grady <[email protected]> Co-authored-by: Stephen <[email protected]>
144 lines
3.9 KiB
Go
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)
|
|
}
|
|
}
|