mirror of
https://github.com/Luzifer/badge-gen.git
synced 2024-12-20 16:41:16 +00:00
Add caching results of API calls
This commit is contained in:
parent
d2c7c6693e
commit
54c47998d7
5 changed files with 140 additions and 29 deletions
10
app.go
10
app.go
|
@ -5,6 +5,7 @@ import (
|
|||
"crypto/sha1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
|
@ -14,6 +15,7 @@ import (
|
|||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Luzifer/badge-gen/cache"
|
||||
"github.com/Luzifer/rconfig"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/tdewolff/minify"
|
||||
|
@ -29,9 +31,11 @@ var (
|
|||
cfg = struct {
|
||||
Port int64 `env:"PORT"`
|
||||
Listen string `flag:"listen" default:":3000" description:"Port/IP to listen on"`
|
||||
Cache string `flag:"cache" default:"mem://" description:"Where to cache query results from thirdparty APIs"`
|
||||
}{}
|
||||
serviceHandlers = map[string]serviceHandler{}
|
||||
version = "dev"
|
||||
cacheStore cache.Cache
|
||||
)
|
||||
|
||||
type serviceHandlerDocumentation struct {
|
||||
|
@ -72,6 +76,12 @@ func main() {
|
|||
cfg.Listen = fmt.Sprintf(":%d", cfg.Port)
|
||||
}
|
||||
|
||||
var err error
|
||||
cacheStore, err = cache.GetCacheByURI(cfg.Cache)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to open cache: %s", err)
|
||||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/v1/badge", generateBadge).Methods("GET")
|
||||
r.HandleFunc("/{service}/{parameters:.*}", generateServiceBadge).Methods("GET")
|
||||
|
|
33
cache/cache.go
vendored
Normal file
33
cache/cache.go
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type KeyNotFoundError struct{}
|
||||
|
||||
func (k KeyNotFoundError) Error() string {
|
||||
return "Requested key was not found in database"
|
||||
}
|
||||
|
||||
type Cache interface {
|
||||
Get(namespace, key string) (value string, err error)
|
||||
Set(namespace, key, value string, ttl time.Duration) (err error)
|
||||
Delete(namespace, key string) (err error)
|
||||
}
|
||||
|
||||
func GetCacheByURI(uri string) (Cache, error) {
|
||||
url, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch url.Scheme {
|
||||
case "mem":
|
||||
return NewInMemCache(), nil
|
||||
default:
|
||||
return nil, errors.New("Invalid cache scheme: " + url.Scheme)
|
||||
}
|
||||
}
|
53
cache/inMemCache.go
vendored
Normal file
53
cache/inMemCache.go
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type inMemCacheEntry struct {
|
||||
Value string
|
||||
Expires time.Time
|
||||
}
|
||||
|
||||
type InMemCache struct {
|
||||
cache map[string]inMemCacheEntry
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewInMemCache() *InMemCache {
|
||||
return &InMemCache{
|
||||
cache: map[string]inMemCacheEntry{},
|
||||
}
|
||||
}
|
||||
|
||||
func (i InMemCache) Get(namespace, key string) (value string, err error) {
|
||||
i.lock.RLock()
|
||||
defer i.lock.RUnlock()
|
||||
|
||||
e, ok := i.cache[namespace+"::"+key]
|
||||
if !ok || e.Expires.Before(time.Now()) {
|
||||
return "", KeyNotFoundError{}
|
||||
}
|
||||
return e.Value, nil
|
||||
}
|
||||
|
||||
func (i InMemCache) Set(namespace, key, value string, ttl time.Duration) (err error) {
|
||||
i.lock.Lock()
|
||||
defer i.lock.Unlock()
|
||||
|
||||
i.cache[namespace+"::"+key] = inMemCacheEntry{
|
||||
Value: value,
|
||||
Expires: time.Now().Add(ttl),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i InMemCache) Delete(namespace, key string) (err error) {
|
||||
i.lock.Lock()
|
||||
defer i.lock.Unlock()
|
||||
|
||||
delete(i.cache, namespace+"::"+key)
|
||||
return nil
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
|
@ -45,27 +46,33 @@ func (g githubServiceHandler) Handle(ctx context.Context, params []string) (titl
|
|||
func (g githubServiceHandler) handleLicense(ctx context.Context, params []string) (title, text, color string, err error) {
|
||||
path := strings.Join([]string{"repos", params[0], params[1], "license"}, "/")
|
||||
|
||||
req, _ := http.NewRequest("GET", "https://api.github.com/"+path, nil)
|
||||
req.Header.Set("Accept", "application/vnd.github.drax-preview+json")
|
||||
text, err = cacheStore.Get("github_license", path)
|
||||
|
||||
var resp *http.Response
|
||||
resp, err = ctxhttp.Do(ctx, http.DefaultClient, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
req, _ := http.NewRequest("GET", "https://api.github.com/"+path, nil)
|
||||
req.Header.Set("Accept", "application/vnd.github.drax-preview+json")
|
||||
|
||||
r := struct {
|
||||
License struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"license"`
|
||||
}{}
|
||||
if err = json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
||||
return
|
||||
var resp *http.Response
|
||||
resp, err = ctxhttp.Do(ctx, http.DefaultClient, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
r := struct {
|
||||
License struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"license"`
|
||||
}{}
|
||||
if err = json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
text = r.License.Name
|
||||
cacheStore.Set("github_license", path, text, 10*time.Minute)
|
||||
}
|
||||
|
||||
title = "license"
|
||||
text = r.License.Name
|
||||
color = "007ec6"
|
||||
|
||||
if text == "" {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
|
@ -36,26 +37,33 @@ func (t travisServiceHandler) Handle(ctx context.Context, params []string) (titl
|
|||
|
||||
path := strings.Join([]string{"repos", params[0], params[1], "branches", params[2]}, "/")
|
||||
|
||||
var resp *http.Response
|
||||
resp, err = ctxhttp.Get(ctx, http.DefaultClient, "https://api.travis-ci.org/"+path)
|
||||
var state string
|
||||
state, err = cacheStore.Get("travis", path)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var resp *http.Response
|
||||
resp, err = ctxhttp.Get(ctx, http.DefaultClient, "https://api.travis-ci.org/"+path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
r := struct {
|
||||
File string `json:"file"`
|
||||
Branch struct {
|
||||
State string `json:"state"`
|
||||
} `json:"branch"`
|
||||
}{}
|
||||
r := struct {
|
||||
File string `json:"file"`
|
||||
Branch struct {
|
||||
State string `json:"state"`
|
||||
} `json:"branch"`
|
||||
}{}
|
||||
|
||||
if err = json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
||||
return
|
||||
if err = json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
||||
return
|
||||
}
|
||||
state = r.Branch.State
|
||||
cacheStore.Set("travis", path, state, 5*time.Minute)
|
||||
}
|
||||
|
||||
title = "travis"
|
||||
text = r.Branch.State
|
||||
text = state
|
||||
if text == "" {
|
||||
text = "unknown"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue