Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Unified Diff: server/config/format.go

Issue 2580713002: Implement a server-side config service interface. (Closed)
Patch Set: Update MultiResolver interface, add test for MultiError. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: server/config/format.go
diff --git a/server/config/format.go b/server/config/format.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c83db4a0c693a646b354f80364d22ec1be34a7f
--- /dev/null
+++ b/server/config/format.go
@@ -0,0 +1,156 @@
+// Copyright 2016 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package config
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/luci/luci-go/common/errors"
+
+ "golang.org/x/net/context"
+)
+
+// Formatter applies a transformation to the data's cached representation.
+//
+// The Formatter is supplied with the content to format, and any additional
+// formatter data
iannucci 2017/01/07 20:12:16 This is the format data that comes with the Params
dnj 2017/01/10 03:25:58 It's the data in Item's Content, implemented by th
+type Formatter interface {
+ FormatItem(c, fd string) (string, error)
+}
+
+// FormatterRegistry is a registry of Resolver key mapped to the Formatter to
+// use to format that key.
+type FormatterRegistry struct {
+ registryMu sync.RWMutex
+ registry map[string]Formatter
+}
+
+// Register registers a Formatter with the FormatBackend.
+//
+// If the supplied key is already registered, Register will panic.
+func (r *FormatterRegistry) Register(rk string, f Formatter) {
+ if rk == "" {
+ panic("cannot register empty key")
+ }
+
+ r.registryMu.Lock()
+ defer r.registryMu.Unlock()
+
+ if _, ok := r.registry[rk]; ok {
+ panic(fmt.Errorf("key %q is already registered", rk))
+ }
+ if r.registry == nil {
+ r.registry = make(map[string]Formatter)
iannucci 2017/01/07 20:12:16 nit: `map[string]Formatter{}` is shorter
dnj 2017/01/10 03:25:58 Done.
+ }
+ r.registry[rk] = f
+}
+
+// Get returns the Formatter associated with the provided Format.
+func (r *FormatterRegistry) Get(f string) Formatter {
+ r.registryMu.RLock()
+ defer r.registryMu.RUnlock()
+ return r.registry[f]
+}
+
+// FormatBackend is a Backend implementation that applies Formatter
+// transformations to the various Items that pass through it.
+//
+// Formatter transformations are registered explicitly to the Resolver
+// descriptions of the types that they operate on. If an Item is already
+// formatted, no further transformations will be applied.
+type FormatBackend struct {
+ // Backend is the underlying Backend that this FormatBackend will pull data
+ // from.
+ Backend
+
+ // GetRegistry returns the FormatterRegistry to use. If it returns nil,
+ // no formatting will be done.
+ GetRegistry func(context.Context) *FormatterRegistry
+}
+
+// Get implements Backend.
+func (b *FormatBackend) Get(c context.Context, configSet, path string, p Params) (*Item, error) {
+ item, err := b.Backend.Get(c, configSet, path, p)
+ if err != nil {
+ return nil, err
+ }
+
+ if p.Format != "" {
+ formatter, err := b.getFormatter(c, p.Format)
+ if err != nil {
+ return nil, errors.Annotate(err).Reason("failed to get formatter for %(format)q, data %(data)q").
+ D("format", p.Format).D("data", p.FormatData).Err()
+ }
+
+ if err := b.formatItem(item, formatter, p.Format, p.FormatData); err != nil {
+ return nil, errors.Annotate(err).Reason("failed to format item to %(format)q, data %(data)q").
+ D("format", p.Format).D("data", p.FormatData).Err()
+ }
+ }
+ return item, nil
+}
+
+// GetAll implements Backend.
+func (b *FormatBackend) GetAll(c context.Context, t GetAllType, path string, p Params) ([]*Item, error) {
+ items, err := b.Backend.GetAll(c, t, path, p)
+ if err != nil {
+ return nil, err
+ }
+
+ if p.Format != "" {
+ formatter, err := b.getFormatter(c, p.Format)
+ if err != nil {
+ return nil, errors.Annotate(err).Reason("failed to get formatter for %(format)q, data %(data)q").
+ D("format", p.Format).D("data", p.FormatData).Err()
+ }
+
+ lme := errors.NewLazyMultiError(len(items))
+ for i, item := range items {
+ if err := b.formatItem(item, formatter, p.Format, p.FormatData); err != nil {
+ lme.Assign(i, err)
+ }
+ }
+
+ if err := lme.Get(); err != nil {
+ return nil, errors.Annotate(err).Reason("failed to format items to %(format)q, data %(data)q").
+ D("format", p.Format).D("data", p.FormatData).Err()
+ }
+ }
+ return items, nil
+}
+
+func (b *FormatBackend) getFormatter(c context.Context, f string) (Formatter, error) {
+ if b.GetRegistry == nil {
+ return nil, errors.New("no formatter registry function installed")
+ }
+
+ reg := b.GetRegistry(c)
+ if reg == nil {
+ return nil, errors.New("formatter registry function returned nil registry")
+ }
+ formatter := reg.Get(f)
+ if formatter == nil {
+ return nil, errors.Reason("unknown formatter: %(formatter)q").D("formatter", f).Err()
+ }
+ return formatter, nil
+}
+
+func (b *FormatBackend) formatItem(it *Item, formatter Formatter, f, fd string) error {
+ if it.Format != "" {
+ // Item is already formatted.
+ return nil
+ }
+
+ // Item is not formatted, so format it.
+ content, err := formatter.FormatItem(it.Content, fd)
+ if err != nil {
+ return errors.Annotate(err).Reason("failed to format item").Err()
+ }
+ it.Content = content
+ it.Format = f
+ it.FormatData = fd
+ return nil
+}

Powered by Google App Engine
This is Rietveld 408576698