aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cache/cache.go160
-rw-r--r--graphql/handler.go28
2 files changed, 177 insertions, 11 deletions
diff --git a/cache/cache.go b/cache/cache.go
new file mode 100644
index 00000000..a46922be
--- /dev/null
+++ b/cache/cache.go
@@ -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
+}
diff --git a/graphql/handler.go b/graphql/handler.go
index b49ca56b..89ef3801 100644
--- a/graphql/handler.go
+++ b/graphql/handler.go
@@ -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
}