Skip to content

Commit

Permalink
feat(instance): instroduce Instance, deprecate global config
Browse files Browse the repository at this point in the history
  • Loading branch information
b5 committed Apr 18, 2019
1 parent cf0caf6 commit 53a8e4b
Show file tree
Hide file tree
Showing 9 changed files with 615 additions and 202 deletions.
212 changes: 151 additions & 61 deletions lib/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,83 +3,168 @@ package lib
import (
"encoding/json"
"fmt"
"net/rpc"

"github.com/ghodss/yaml"
golog "github.com/ipfs/go-log"
"github.com/qri-io/ioes"
"github.com/qri-io/qri/config"
"github.com/qri-io/qri/config/migrate"
)

var (
// Config is the global configuration object
Config *config.Config
// ConfigFilepath is the default location for a config file
ConfigFilepath string
)

// SaveConfig is a function that updates the configuration file
var SaveConfig = func() error {
if err := Config.WriteToFile(ConfigFilepath); err != nil {
return fmt.Errorf("error saving profile: %s", err)
// var (
// // Config is the global configuration object
// Config *config.Config
// // ConfigFilepath is the default location for a config file
// ConfigFilepath string
// )

// // SaveConfig is a function that updates the configuration file
// var SaveConfig = func() error {
// if err := Config.WriteToFile(ConfigFilepath); err != nil {
// return fmt.Errorf("error saving profile: %s", err)
// }
// return nil
// }

// // LoadConfig loads the global default configuration
// func LoadConfig(streams ioes.IOStreams, path string) (err error) {
// cfg, err := config.ReadFromFile(path)
// if err != nil {
// return err
// }

// if cfg.Profile == nil {
// err = fmt.Errorf("missing profile")
// return err
// }

// // configure logging straight away
// if cfg.Logging != nil {
// for name, level := range cfg.Logging.Levels {
// golog.SetLogLevel(name, level)
// }
// }

// Config = cfg

// migrated, err := migrate.RunMigrations(streams, cfg)
// if err != nil {
// return err
// }

// if migrated {
// return SaveConfig()
// }

// return nil
// }

// // GetConfigParams are the params needed to format/specify the fields in bytes returned from the GetConfig function
// type GetConfigParams struct {
// WithPrivateKey bool
// Format string
// Concise bool
// Field string
// }

// // GetConfig returns the Config, or one of the specified fields of the Config, as a slice of bytes
// // the bytes can be formatted as json, concise json, or yaml
// func GetConfig(p *GetConfigParams, res *[]byte) error {
// var (
// err error
// cfg = &config.Config{}
// encode interface{}
// )

// if !p.WithPrivateKey {
// cfg = Config.WithoutPrivateValues()
// } else {
// cfg = Config.Copy()
// }

// encode = cfg

// if p.Field != "" {
// encode, err = cfg.Get(p.Field)
// if err != nil {
// return fmt.Errorf("error getting %s from config: %s", p.Field, err)
// }
// }

// switch p.Format {
// case "json":
// if p.Concise {
// *res, err = json.Marshal(encode)
// } else {
// *res, err = json.MarshalIndent(encode, "", " ")
// }
// case "yaml":
// *res, err = yaml.Marshal(encode)
// }
// if err != nil {
// return fmt.Errorf("error getting config: %s", err)
// }

// return nil
// }

// // SetConfig validates and saves the config (passed in as `res`)
// func SetConfig(res *config.Config) error {
// if err := res.Validate(); err != nil {
// return fmt.Errorf("error validating config: %s", err)
// }
// if err := SaveConfig(); err != nil {
// return fmt.Errorf("error saving config: %s", err)
// }

// Config = res.WithPrivateValues(Config)

// return nil
// }

// NewConfigRequests creates a configuration handle from an instance
func NewConfigRequests(cfg *config.Config, setCfg func(*config.Config) error, cli *rpc.Client) *ConfigRequests {
return &ConfigRequests{
cfg: cfg,
setCfg: setCfg,
cli: cli,
}
return nil
}

// LoadConfig loads the global default configuration
func LoadConfig(streams ioes.IOStreams, path string) (err error) {
cfg, err := config.ReadFromFile(path)
if err != nil {
return err
}

if cfg.Profile == nil {
err = fmt.Errorf("missing profile")
return err
}

// configure logging straight away
if cfg.Logging != nil {
for name, level := range cfg.Logging.Levels {
golog.SetLogLevel(name, level)
}
}

Config = cfg

migrated, err := migrate.RunMigrations(streams, cfg)
if err != nil {
return err
}

if migrated {
return SaveConfig()
}

return nil
// ConfigRequests encapsulates changes to a qri configuration
type ConfigRequests struct {
cfg *config.Config
setCfg func(*config.Config) error
cli *rpc.Client
}

// GetConfigParams are the params needed to format/specify the fields in bytes returned from the GetConfig function
// CoreRequestsName specifies this is a configuration handle
func (h ConfigRequests) CoreRequestsName() string { return "config" }

// GetConfigParams are the params needed to format/specify the fields in bytes
// returned from the GetConfig function
type GetConfigParams struct {
Field string
WithPrivateKey bool
Format string
Concise bool
Field string
}

// GetConfig returns the Config, or one of the specified fields of the Config, as a slice of bytes
// the bytes can be formatted as json, concise json, or yaml
func GetConfig(p *GetConfigParams, res *[]byte) error {
// GetConfig returns the Config, or one of the specified fields of the Config,
// as a slice of bytes the bytes can be formatted as json, concise json, or yaml
func (h ConfigRequests) GetConfig(p *GetConfigParams, res *[]byte) error {
if h.cli != nil {
return fmt.Errorf("GetConfig cannot be called over RPC")
}

var (
err error
cfg = &config.Config{}
cfg = h.cfg
encode interface{}
)

if !p.WithPrivateKey {
cfg = Config.WithoutPrivateValues()
cfg = cfg.WithoutPrivateValues()
} else {
cfg = Config.Copy()
cfg = cfg.Copy()
}

encode = cfg
Expand Down Expand Up @@ -108,16 +193,21 @@ func GetConfig(p *GetConfigParams, res *[]byte) error {
return nil
}

// SetConfig validates and saves the config (passed in as `res`)
func SetConfig(res *config.Config) error {
if err := res.Validate(); err != nil {
return fmt.Errorf("error validating config: %s", err)
// SetConfig validates, updates and saves the config
func (h ConfigRequests) SetConfig(update *config.Config, set *bool) (err error) {
if h.cli != nil {
return fmt.Errorf("SetConfig cannot be called over RPC")
}
if err := SaveConfig(); err != nil {
return fmt.Errorf("error saving config: %s", err)

if err = update.Validate(); err != nil {
return fmt.Errorf("error validating config: %s", err)
}

Config = res.WithPrivateValues(Config)
cfg := update.WithPrivateValues(h.cfg)
if err = h.setCfg(cfg); err != nil {
return
}

*set = true
return nil
}
86 changes: 44 additions & 42 deletions lib/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,89 +2,91 @@ package lib

import (
"bytes"
"fmt"
"io/ioutil"
"os"
"testing"

"github.com/qri-io/ioes"
"github.com/qri-io/qri/config"
)

func init() {
Config = config.DefaultConfigForTesting()
}
// func TestLoadConfig(t *testing.T) {
// path, err := ioutil.TempDir("", "config_tests")
// if err != nil {
// t.Fatal(err.Error())
// }
// defer os.RemoveAll(path)
// cfgPath := path + "/config.yaml"

func TestLoadConfig(t *testing.T) {
path, err := ioutil.TempDir("", "config_tests")
if err != nil {
t.Fatal(err.Error())
}
defer os.RemoveAll(path)
cfgPath := path + "/config.yaml"
// if err := config.DefaultConfigForTesting().WriteToFile(cfgPath); err != nil {
// t.Fatal(err.Error())
// }
// if err := LoadConfig(ioes.NewDiscardIOStreams(), cfgPath); err != nil {
// t.Error(err.Error())
// }
// }

if err := config.DefaultConfigForTesting().WriteToFile(cfgPath); err != nil {
t.Fatal(err.Error())
}
if err := LoadConfig(ioes.NewDiscardIOStreams(), cfgPath); err != nil {
t.Error(err.Error())
}
func testConfigAndSetter() (cfg *config.Config, setCfg func(*config.Config) error) {
cfg = config.DefaultConfigForTesting()
setCfg = func(*config.Config) error { return nil }
return
}
func TestGetConfig(t *testing.T) {
cfg, setCfg := testConfigAndSetter()
r := NewConfigRequests(cfg, setCfg, nil)

p := &GetConfigParams{Field: "profile.id", Format: "json"}
res := []byte{}
if err := GetConfig(p, &res); err != nil {
if err := r.GetConfig(p, &res); err != nil {
t.Error(err.Error())
}
if !bytes.Equal(res, []byte(`"QmeL2mdVka1eahKENjehK6tBxkkpk5dNQ1qMcgWi7Hrb4B"`)) {
t.Errorf("response mismatch. got %s", string(res))
}
}

func TestSaveConfig(t *testing.T) {
prevCFP := ConfigFilepath
defer func() {
ConfigFilepath = prevCFP
}()

func TestSaveConfigToFile(t *testing.T) {
path, err := ioutil.TempDir("", "save_config_test")
if err != nil {
t.Fatal(err.Error())
}
ConfigFilepath = ""
if err := SaveConfig(); err == nil {
t.Error("expected save to empty path to error")

cfgPath := path + "/config.yaml"
cfg := config.DefaultConfigForTesting()
setCfg := func(*config.Config) error {
return cfg.WriteToFile(cfgPath)
}
r := NewConfigRequests(cfg, setCfg, nil)

ConfigFilepath = path + "/config.yaml"
if err := SaveConfig(); err != nil {
var ok bool
if err := r.SetConfig(cfg, &ok); err != nil {
t.Error(err.Error())
}
}

func TestSetConfig(t *testing.T) {
prevSC := SaveConfig
defer func() { SaveConfig = prevSC }()
cfg, setCfg := testConfigAndSetter()
r := NewConfigRequests(cfg, setCfg, nil)

var set bool

if err := SetConfig(&config.Config{}); err == nil {
if err := r.SetConfig(&config.Config{}, &set); err == nil {
t.Errorf("expected saving empty config to be invalid")
}

cfg := config.DefaultConfigForTesting()
SaveConfig = func() error { return fmt.Errorf("bad") }
if err := SetConfig(cfg); err == nil {
t.Errorf("expected saving error to return")
}
// cfg := config.DefaultConfigForTesting()
// SaveConfig = func() error { return fmt.Errorf("bad") }
// if err := SetConfig(cfg); err == nil {
// t.Errorf("expected saving error to return")
// }

SaveConfig = func() error { return nil }
// SaveConfig = func() error { return nil }

cfg.Profile.Twitter = "@qri_io"
if err := SetConfig(cfg); err != nil {
if err := r.SetConfig(cfg, &set); err != nil {
t.Error(err.Error())
}
p := &GetConfigParams{Field: "profile.twitter", Format: "json"}
res := []byte{}
if err := GetConfig(p, &res); err != nil {
if err := r.GetConfig(p, &res); err != nil {
t.Error(err.Error())
}
if !bytes.Equal(res, []byte(`"@qri_io"`)) {
Expand Down
Loading

0 comments on commit 53a8e4b

Please sign in to comment.