| @@ -17,7 +17,6 @@ import ( | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/go-micro/config/options" | ||||
| 	"github.com/micro/go-micro/store" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| @@ -27,7 +26,7 @@ const ( | ||||
| ) | ||||
|  | ||||
| type workersKV struct { | ||||
| 	options.Options | ||||
| 	options store.Options | ||||
| 	// cf account id | ||||
| 	account string | ||||
| 	// cf api token | ||||
| @@ -291,38 +290,25 @@ func (w *workersKV) request(ctx context.Context, method, path string, body inter | ||||
| // CF_API_TOKEN to a cloudflare API token scoped to Workers KV. | ||||
| // CF_ACCOUNT_ID to contain a string with your cloudflare account ID. | ||||
| // KV_NAMESPACE_ID to contain the namespace UUID for your KV storage. | ||||
| func NewStore(opts ...options.Option) store.Store { | ||||
| 	// create new Options | ||||
| 	options := options.NewOptions(opts...) | ||||
| func NewStore(opts ...store.Option) store.Store { | ||||
| 	var options store.Options | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
|  | ||||
| 	// get values from the environment | ||||
| 	// get options from environment | ||||
| 	account, token, namespace := getOptions() | ||||
|  | ||||
| 	// set api token from options if exists | ||||
| 	if apiToken, ok := options.Values().Get("CF_API_TOKEN"); ok { | ||||
| 		tk, ok := apiToken.(string) | ||||
| 		if !ok { | ||||
| 			log.Fatal("Store: Option CF_API_TOKEN contains a non-string") | ||||
| 		} | ||||
| 		token = tk | ||||
| 	if len(account) == 0 { | ||||
| 		account = getAccount(options.Context) | ||||
| 	} | ||||
|  | ||||
| 	// set account id from options if exists | ||||
| 	if accountID, ok := options.Values().Get("CF_ACCOUNT_ID"); ok { | ||||
| 		id, ok := accountID.(string) | ||||
| 		if !ok { | ||||
| 			log.Fatal("Store: Option CF_ACCOUNT_ID contains a non-string") | ||||
| 		} | ||||
| 		account = id | ||||
| 	if len(token) == 0 { | ||||
| 		token = getToken(options.Context) | ||||
| 	} | ||||
|  | ||||
| 	// set namespace from options if exists | ||||
| 	if uuid, ok := options.Values().Get("KV_NAMESPACE_ID"); ok { | ||||
| 		ns, ok := uuid.(string) | ||||
| 		if !ok { | ||||
| 			log.Fatal("Store: Option KV_NAMESPACE_ID contains a non-string") | ||||
| 		} | ||||
| 		namespace = ns | ||||
| 	if len(namespace) == 0 { | ||||
| 		namespace = getNamespace(options.Context) | ||||
| 	} | ||||
|  | ||||
| 	// validate options are not blank or log.Fatal | ||||
| @@ -332,7 +318,7 @@ func NewStore(opts ...options.Option) store.Store { | ||||
| 		account:    account, | ||||
| 		namespace:  namespace, | ||||
| 		token:      token, | ||||
| 		Options:    options, | ||||
| 		options:    options, | ||||
| 		httpClient: &http.Client{}, | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,23 +1,60 @@ | ||||
| package cloudflare | ||||
|  | ||||
| import ( | ||||
| 	"github.com/micro/go-micro/config/options" | ||||
| 	"context" | ||||
|  | ||||
| 	"github.com/micro/go-micro/store" | ||||
| ) | ||||
|  | ||||
| func getOption(ctx context.Context, key string) string { | ||||
| 	if ctx == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	val, ok := ctx.Value(key).(string) | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return val | ||||
| } | ||||
|  | ||||
| func getToken(ctx context.Context) string { | ||||
| 	return getOption(ctx, "CF_API_TOKEN") | ||||
| } | ||||
|  | ||||
| func getAccount(ctx context.Context) string { | ||||
| 	return getOption(ctx, "CF_ACCOUNT_ID") | ||||
| } | ||||
|  | ||||
| func getNamespace(ctx context.Context) string { | ||||
| 	return getOption(ctx, "KV_NAMESPACE_ID") | ||||
| } | ||||
|  | ||||
| // Token sets the cloudflare api token | ||||
| func Token(t string) options.Option { | ||||
| 	// TODO: change to store.cf.api_token | ||||
| 	return options.WithValue("CF_API_TOKEN", t) | ||||
| func Token(t string) store.Option { | ||||
| 	return func(o *store.Options) { | ||||
| 		if o.Context == nil { | ||||
| 			o.Context = context.Background() | ||||
| 		} | ||||
| 		o.Context = context.WithValue(o.Context, "CF_API_TOKEN", t) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Account sets the cloudflare account id | ||||
| func Account(id string) options.Option { | ||||
| 	// TODO: change to store.cf.account_id | ||||
| 	return options.WithValue("CF_ACCOUNT_ID", id) | ||||
| func Account(id string) store.Option { | ||||
| 	return func(o *store.Options) { | ||||
| 		if o.Context == nil { | ||||
| 			o.Context = context.Background() | ||||
| 		} | ||||
| 		o.Context = context.WithValue(o.Context, "CF_ACCOUNT_ID", id) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Namespace sets the KV namespace | ||||
| func Namespace(ns string) options.Option { | ||||
| 	// TODO: change to store.cf.namespace | ||||
| 	return options.WithValue("KV_NAMESPACE_ID", ns) | ||||
| func Namespace(ns string) store.Option { | ||||
| 	return func(o *store.Options) { | ||||
| 		if o.Context == nil { | ||||
| 			o.Context = context.Background() | ||||
| 		} | ||||
| 		o.Context = context.WithValue(o.Context, "KV_NAMESPACE_ID", ns) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -7,13 +7,12 @@ import ( | ||||
|  | ||||
| 	client "github.com/coreos/etcd/clientv3" | ||||
| 	"github.com/coreos/etcd/mvcc/mvccpb" | ||||
| 	"github.com/micro/go-micro/config/options" | ||||
| 	"github.com/micro/go-micro/store" | ||||
| ) | ||||
|  | ||||
| type ekv struct { | ||||
| 	options.Options | ||||
| 	kv client.KV | ||||
| 	options store.Options | ||||
| 	kv      client.KV | ||||
| } | ||||
|  | ||||
| func (e *ekv) Read(keys ...string) ([]*store.Record, error) { | ||||
| @@ -91,15 +90,15 @@ func (e *ekv) String() string { | ||||
| 	return "etcd" | ||||
| } | ||||
|  | ||||
| func NewStore(opts ...options.Option) store.Store { | ||||
| 	options := options.NewOptions(opts...) | ||||
|  | ||||
| 	var endpoints []string | ||||
|  | ||||
| 	if e, ok := options.Values().Get("store.nodes"); ok { | ||||
| 		endpoints = e.([]string) | ||||
| func NewStore(opts ...store.Option) store.Store { | ||||
| 	var options store.Options | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
|  | ||||
| 	// get the endpoints | ||||
| 	endpoints := options.Nodes | ||||
|  | ||||
| 	if len(endpoints) == 0 { | ||||
| 		endpoints = []string{"http://127.0.0.1:2379"} | ||||
| 	} | ||||
| @@ -113,7 +112,7 @@ func NewStore(opts ...options.Option) store.Store { | ||||
| 	} | ||||
|  | ||||
| 	return &ekv{ | ||||
| 		Options: options, | ||||
| 		options: options, | ||||
| 		kv:      client.NewKV(c), | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -5,12 +5,11 @@ import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/go-micro/config/options" | ||||
| 	"github.com/micro/go-micro/store" | ||||
| ) | ||||
|  | ||||
| type memoryStore struct { | ||||
| 	options.Options | ||||
| 	options store.Options | ||||
|  | ||||
| 	sync.RWMutex | ||||
| 	values map[string]*memoryRecord | ||||
| @@ -110,11 +109,14 @@ func (m *memoryStore) Delete(keys ...string) error { | ||||
| } | ||||
|  | ||||
| // NewStore returns a new store.Store | ||||
| func NewStore(opts ...options.Option) store.Store { | ||||
| 	options := options.NewOptions(opts...) | ||||
| func NewStore(opts ...store.Option) store.Store { | ||||
| 	var options store.Options | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
|  | ||||
| 	return &memoryStore{ | ||||
| 		Options: options, | ||||
| 		options: options, | ||||
| 		values:  make(map[string]*memoryRecord), | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package store | ||||
|  | ||||
| import ( | ||||
| 	"github.com/micro/go-micro/config/options" | ||||
| 	"context" | ||||
| ) | ||||
|  | ||||
| type Options struct { | ||||
| @@ -11,20 +11,30 @@ type Options struct { | ||||
| 	Namespace string | ||||
| 	// Prefix of the keys used | ||||
| 	Prefix string | ||||
| 	// Alternative options | ||||
| 	Context context.Context | ||||
| } | ||||
|  | ||||
| type Option func(o *Options) | ||||
|  | ||||
| // Nodes is a list of nodes used to back the store | ||||
| func Nodes(a ...string) options.Option { | ||||
| 	return options.WithValue("store.nodes", a) | ||||
| func Nodes(a ...string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Nodes = a | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Prefix sets a prefix to any key ids used | ||||
| func Prefix(p string) options.Option { | ||||
| 	return options.WithValue("store.prefix", p) | ||||
| func Prefix(p string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Prefix = p | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Namespace offers a way to have multiple isolated | ||||
| // stores in the same backend, if supported. | ||||
| func Namespace(n string) options.Option { | ||||
| 	return options.WithValue("store.namespace", n) | ||||
| func Namespace(ns string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Namespace = ns | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -4,15 +4,13 @@ package postgresql | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unicode" | ||||
|  | ||||
| 	"github.com/lib/pq" | ||||
| 	"github.com/pkg/errors" | ||||
|  | ||||
| 	"github.com/micro/go-micro/config/options" | ||||
| 	"github.com/micro/go-micro/store" | ||||
| 	"github.com/micro/go-micro/util/log" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| // DefaultNamespace is the namespace that the sql store | ||||
| @@ -27,7 +25,8 @@ type sqlStore struct { | ||||
|  | ||||
| 	database string | ||||
| 	table    string | ||||
| 	options.Options | ||||
|  | ||||
| 	options store.Options | ||||
| } | ||||
|  | ||||
| // List all the known records | ||||
| @@ -151,38 +150,16 @@ func (s *sqlStore) Delete(keys ...string) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *sqlStore) initDB(options options.Options) error { | ||||
| 	// Get the store.namespace option, or use sql.DefaultNamespace | ||||
| 	namespaceOpt, found := options.Values().Get("store.namespace") | ||||
| 	if !found { | ||||
| 		s.database = DefaultNamespace | ||||
| 	} else { | ||||
| 		if namespace, ok := namespaceOpt.(string); ok { | ||||
| 			s.database = namespace | ||||
| 		} else { | ||||
| 			return errors.New("store.namespace option must be a string") | ||||
| 		} | ||||
| 	} | ||||
| 	// Get the store.namespace option, or use sql.DefaultNamespace | ||||
| 	prefixOpt, found := options.Values().Get("store.prefix") | ||||
| 	if !found { | ||||
| 		s.table = DefaultPrefix | ||||
| 	} else { | ||||
| 		if prefix, ok := prefixOpt.(string); ok { | ||||
| 			s.table = prefix | ||||
| 		} else { | ||||
| 			return errors.New("store.namespace option must be a string") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| func (s *sqlStore) initDB() { | ||||
| 	// Create "micro" schema | ||||
| 	schema, err := s.db.Prepare(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s ;", s.database)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	_, err = schema.Exec() | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, "Couldn't create database") | ||||
| 		log.Fatal(errors.Wrap(err, "Couldn't create database")) | ||||
| 	} | ||||
|  | ||||
| 	// Create a table for the Store namespace | ||||
| @@ -194,73 +171,59 @@ func (s *sqlStore) initDB(options options.Options) error { | ||||
| 		CONSTRAINT %s_pkey PRIMARY KEY (key) | ||||
| 	);`, s.database, s.table, s.table)) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, "SQL statement preparation failed") | ||||
| 	} | ||||
| 	_, err = tableq.Exec() | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, "Couldn't create table") | ||||
| 		log.Fatal(errors.Wrap(err, "SQL statement preparation failed")) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| 	_, err = tableq.Exec() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(errors.Wrap(err, "Couldn't create table")) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // New returns a new micro Store backed by sql | ||||
| func New(opts ...options.Option) (store.Store, error) { | ||||
| 	options := options.NewOptions(opts...) | ||||
| 	driver, dataSourceName, err := validateOptions(options) | ||||
| func New(opts ...store.Option) store.Store { | ||||
| 	var options store.Options | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
|  | ||||
| 	nodes := options.Nodes | ||||
| 	if len(nodes) == 0 { | ||||
| 		nodes = []string{"localhost:26257"} | ||||
| 	} | ||||
|  | ||||
| 	namespace := options.Namespace | ||||
| 	if len(namespace) == 0 { | ||||
| 		namespace = DefaultNamespace | ||||
| 	} | ||||
|  | ||||
| 	prefix := options.Prefix | ||||
| 	if len(prefix) == 0 { | ||||
| 		prefix = DefaultPrefix | ||||
| 	} | ||||
|  | ||||
| 	for _, r := range namespace { | ||||
| 		if !unicode.IsLetter(r) { | ||||
| 			log.Fatal("store.namespace must only contain letters") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// create source from first node | ||||
| 	source := fmt.Sprintf("host=%s", nodes[0]) | ||||
| 	db, err := sql.Open("pq", source) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !strings.Contains(dataSourceName, " ") { | ||||
| 		dataSourceName = fmt.Sprintf("host=%s", dataSourceName) | ||||
| 	} | ||||
| 	db, err := sql.Open(driver, dataSourceName) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if err := db.Ping(); err != nil { | ||||
| 		return nil, err | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	s := &sqlStore{ | ||||
| 		db: db, | ||||
| 		db:       db, | ||||
| 		database: namespace, | ||||
| 		table:    prefix, | ||||
| 	} | ||||
|  | ||||
| 	return s, s.initDB(options) | ||||
| } | ||||
|  | ||||
| // validateOptions checks whether the provided options are valid, then returns the driver | ||||
| // and data source name. | ||||
| func validateOptions(options options.Options) (driver, dataSourceName string, err error) { | ||||
| 	driverOpt, found := options.Values().Get("store.sql.driver") | ||||
| 	if !found { | ||||
| 		return "", "", errors.New("No store.sql.driver option specified") | ||||
| 	} | ||||
| 	nodesOpt, found := options.Values().Get("store.nodes") | ||||
| 	if !found { | ||||
| 		return "", "", errors.New("No store.nodes option specified (expected a database connection string)") | ||||
| 	} | ||||
| 	driver, ok := driverOpt.(string) | ||||
| 	if !ok { | ||||
| 		return "", "", errors.New("store.sql.driver option must be a string") | ||||
| 	} | ||||
| 	nodes, ok := nodesOpt.([]string) | ||||
| 	if !ok { | ||||
| 		return "", "", errors.New("store.nodes option must be a []string") | ||||
| 	} | ||||
| 	if len(nodes) != 1 { | ||||
| 		return "", "", errors.New("expected only 1 store.nodes option") | ||||
| 	} | ||||
| 	namespaceOpt, found := options.Values().Get("store.namespace") | ||||
| 	if found { | ||||
| 		namespace, ok := namespaceOpt.(string) | ||||
| 		if !ok { | ||||
| 			return "", "", errors.New("store.namespace must me a string") | ||||
| 		} | ||||
| 		for _, r := range namespace { | ||||
| 			if !unicode.IsLetter(r) { | ||||
| 				return "", "", errors.New("store.namespace must only contain letters") | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return driver, nodes[0], nil | ||||
| 	return s | ||||
| } | ||||
|   | ||||
| @@ -27,13 +27,10 @@ func TestSQL(t *testing.T) { | ||||
| 	} | ||||
| 	db.Close() | ||||
|  | ||||
| 	sqlStore, err := New( | ||||
| 	sqlStore := New( | ||||
| 		store.Namespace("testsql"), | ||||
| 		store.Nodes(connection), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	records, err := sqlStore.List() | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -7,14 +7,13 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/go-micro/client" | ||||
| 	"github.com/micro/go-micro/config/options" | ||||
| 	"github.com/micro/go-micro/metadata" | ||||
| 	"github.com/micro/go-micro/store" | ||||
| 	pb "github.com/micro/go-micro/store/service/proto" | ||||
| ) | ||||
|  | ||||
| type serviceStore struct { | ||||
| 	options.Options | ||||
| 	options store.Options | ||||
|  | ||||
| 	// Namespace to use | ||||
| 	Namespace string | ||||
| @@ -123,33 +122,17 @@ func (s *serviceStore) Delete(keys ...string) error { | ||||
| } | ||||
|  | ||||
| // NewStore returns a new store service implementation | ||||
| func NewStore(opts ...options.Option) store.Store { | ||||
| 	options := options.NewOptions(opts...) | ||||
|  | ||||
| 	var nodes []string | ||||
| 	var namespace string | ||||
| 	var prefix string | ||||
|  | ||||
| 	n, ok := options.Values().Get("store.nodes") | ||||
| 	if ok { | ||||
| 		nodes = n.([]string) | ||||
| 	} | ||||
|  | ||||
| 	ns, ok := options.Values().Get("store.namespace") | ||||
| 	if ok { | ||||
| 		namespace = ns.(string) | ||||
| 	} | ||||
|  | ||||
| 	prx, ok := options.Values().Get("store.prefix") | ||||
| 	if ok { | ||||
| 		prefix = prx.(string) | ||||
| func NewStore(opts ...store.Option) store.Store { | ||||
| 	var options store.Options | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
|  | ||||
| 	service := &serviceStore{ | ||||
| 		Options:   options, | ||||
| 		Namespace: namespace, | ||||
| 		Prefix:    prefix, | ||||
| 		Nodes:     nodes, | ||||
| 		options:   options, | ||||
| 		Namespace: options.Namespace, | ||||
| 		Prefix:    options.Prefix, | ||||
| 		Nodes:     options.Nodes, | ||||
| 		Client:    pb.NewStoreService("go.micro.store", client.DefaultClient), | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user