add a cache to support the graphql API and the future interactive CLI UI

This commit is contained in:
Michael Muré 2018-07-25 21:26:25 +02:00
parent 6706fa2beb
commit 074156634b
No known key found for this signature in database
GPG Key ID: A4457C029293126F
2 changed files with 177 additions and 11 deletions

160
cache/cache.go vendored Normal file
View File

@ -0,0 +1,160 @@
package cache
import (
"fmt"
"strings"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/repository"
)
type Cache interface {
RegisterRepository(ref string, repo repository.Repo)
RegisterDefaultRepository(repo repository.Repo)
ResolveRepo(ref string) (CachedRepo, error)
DefaultRepo() (CachedRepo, error)
}
type CachedRepo interface {
ResolveBug(id string) (CachedBug, error)
ResolveBugPrefix(prefix string) (CachedBug, error)
ClearAllBugs()
}
type CachedBug interface {
Snapshot() bug.Snapshot
ClearSnapshot()
}
// Cache ------------------------
type DefaultCache struct {
repos map[string]CachedRepo
}
func NewDefaultCache() Cache {
return &DefaultCache{
repos: make(map[string]CachedRepo),
}
}
func (c *DefaultCache) RegisterRepository(ref string, repo repository.Repo) {
c.repos[ref] = NewCachedRepo(repo)
}
func (c *DefaultCache) RegisterDefaultRepository(repo repository.Repo) {
c.repos[""] = NewCachedRepo(repo)
}
func (c *DefaultCache) DefaultRepo() (CachedRepo, error) {
if len(c.repos) != 1 {
return nil, fmt.Errorf("repository is not unique")
}
for _, r := range c.repos {
return r, nil
}
panic("unreachable")
}
func (c *DefaultCache) ResolveRepo(ref string) (CachedRepo, error) {
r, ok := c.repos[ref]
if !ok {
return nil, fmt.Errorf("unknown repo")
}
return r, nil
}
// Repo ------------------------
type CachedRepoImpl struct {
repo repository.Repo
bugs map[string]CachedBug
}
func NewCachedRepo(r repository.Repo) CachedRepo {
return &CachedRepoImpl{
repo: r,
bugs: make(map[string]CachedBug),
}
}
func (c CachedRepoImpl) ResolveBug(id string) (CachedBug, error) {
cached, ok := c.bugs[id]
if ok {
return cached, nil
}
b, err := bug.ReadLocalBug(c.repo, id)
if err != nil {
return nil, err
}
cached = NewCachedBug(b)
c.bugs[id] = cached
return cached, nil
}
func (c CachedRepoImpl) ResolveBugPrefix(prefix string) (CachedBug, error) {
// preallocate but empty
matching := make([]string, 0, 5)
for id := range c.bugs {
if strings.HasPrefix(id, prefix) {
matching = append(matching, id)
}
}
// TODO: should check matching bug in the repo as well
if len(matching) > 1 {
return nil, fmt.Errorf("Multiple matching bug found:\n%s", strings.Join(matching, "\n"))
}
if len(matching) == 1 {
b := c.bugs[matching[0]]
return b, nil
}
b, err := bug.FindLocalBug(c.repo, prefix)
if err != nil {
return nil, err
}
cached := NewCachedBug(b)
c.bugs[b.Id()] = cached
return cached, nil
}
func (c CachedRepoImpl) ClearAllBugs() {
c.bugs = make(map[string]CachedBug)
}
// Bug ------------------------
type CachedBugImpl struct {
bug *bug.Bug
snap *bug.Snapshot
}
func NewCachedBug(b *bug.Bug) CachedBug {
return &CachedBugImpl{
bug: b,
}
}
func (c CachedBugImpl) Snapshot() bug.Snapshot {
if c.snap == nil {
snap := c.bug.Compile()
c.snap = &snap
}
return *c.snap
}
func (c CachedBugImpl) ClearSnapshot() {
c.snap = nil
}

View File

@ -4,18 +4,19 @@ import (
"context"
"net/http"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/repository"
"github.com/graphql-go/handler"
graphqlHandler "github.com/graphql-go/handler"
)
type Handler struct {
Handler *handler.Handler
Repo repository.Repo
handler *graphqlHandler.Handler
cache cache.Cache
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "repo", h.Repo)
h.Handler.ContextHandler(ctx, w, r)
ctx := context.WithValue(r.Context(), "cache", h.cache)
h.handler.ContextHandler(ctx, w, r)
}
func NewHandler(repo repository.Repo) (*Handler, error) {
@ -25,12 +26,17 @@ func NewHandler(repo repository.Repo) (*Handler, error) {
return nil, err
}
h := graphqlHandler.New(&graphqlHandler.Config{
Schema: &schema,
Pretty: true,
GraphiQL: true,
})
c := cache.NewDefaultCache()
c.RegisterDefaultRepository(repo)
return &Handler{
Handler: handler.New(&handler.Config{
Schema: &schema,
Pretty: true,
GraphiQL: true,
}),
Repo: repo,
handler: h,
cache: c,
}, nil
}