store: add Wrappers support, create Namespace wrapper

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2021-07-14 17:11:37 +03:00
parent 0d1ef31764
commit c3def24bf4
4 changed files with 262 additions and 155 deletions

View File

@ -36,16 +36,6 @@ func (m *memoryStore) key(prefix, key string) string {
return filepath.Join(prefix, key) return filepath.Join(prefix, key)
} }
func (m *memoryStore) prefix(database, table string) string {
if len(database) == 0 {
database = m.opts.Database
}
if len(table) == 0 {
table = m.opts.Table
}
return filepath.Join(database, table)
}
func (m *memoryStore) exists(prefix, key string) error { func (m *memoryStore) exists(prefix, key string) error {
key = m.key(prefix, key) key = m.key(prefix, key)
@ -127,37 +117,45 @@ func (m *memoryStore) Name() string {
} }
func (m *memoryStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error { func (m *memoryStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error {
prefix := m.prefix(m.opts.Database, m.opts.Table) options := NewExistsOptions(opts...)
return m.exists(prefix, key) if options.Namespace == "" {
options.Namespace = m.opts.Namespace
}
return m.exists(options.Namespace, key)
} }
func (m *memoryStore) Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error { func (m *memoryStore) Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error {
readOpts := NewReadOptions(opts...) options := NewReadOptions(opts...)
prefix := m.prefix(readOpts.Database, readOpts.Table) if options.Namespace == "" {
return m.get(prefix, key, val) options.Namespace = m.opts.Namespace
}
return m.get(options.Namespace, key, val)
} }
func (m *memoryStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error { func (m *memoryStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error {
writeOpts := NewWriteOptions(opts...) options := NewWriteOptions(opts...)
if options.Namespace == "" {
options.Namespace = m.opts.Namespace
}
prefix := m.prefix(writeOpts.Database, writeOpts.Table) key = m.key(options.Namespace, key)
key = m.key(prefix, key)
buf, err := m.opts.Codec.Marshal(val) buf, err := m.opts.Codec.Marshal(val)
if err != nil { if err != nil {
return err return err
} }
m.store.Set(key, buf, writeOpts.TTL) m.store.Set(key, buf, options.TTL)
return nil return nil
} }
func (m *memoryStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error { func (m *memoryStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error {
deleteOptions := NewDeleteOptions(opts...) options := NewDeleteOptions(opts...)
if options.Namespace == "" {
options.Namespace = m.opts.Namespace
}
prefix := m.prefix(deleteOptions.Database, deleteOptions.Table) m.delete(options.Namespace, key)
m.delete(prefix, key)
return nil return nil
} }
@ -166,25 +164,27 @@ func (m *memoryStore) Options() Options {
} }
func (m *memoryStore) List(ctx context.Context, opts ...ListOption) ([]string, error) { func (m *memoryStore) List(ctx context.Context, opts ...ListOption) ([]string, error) {
listOptions := NewListOptions(opts...) options := NewListOptions(opts...)
if options.Namespace == "" {
options.Namespace = m.opts.Namespace
}
prefix := m.prefix(listOptions.Database, listOptions.Table) keys := m.list(options.Namespace, options.Limit, options.Offset)
keys := m.list(prefix, listOptions.Limit, listOptions.Offset)
if len(listOptions.Prefix) > 0 { if len(options.Prefix) > 0 {
var prefixKeys []string var prefixKeys []string
for _, k := range keys { for _, k := range keys {
if strings.HasPrefix(k, listOptions.Prefix) { if strings.HasPrefix(k, options.Prefix) {
prefixKeys = append(prefixKeys, k) prefixKeys = append(prefixKeys, k)
} }
} }
keys = prefixKeys keys = prefixKeys
} }
if len(listOptions.Suffix) > 0 { if len(options.Suffix) > 0 {
var suffixKeys []string var suffixKeys []string
for _, k := range keys { for _, k := range keys {
if strings.HasSuffix(k, listOptions.Suffix) { if strings.HasSuffix(k, options.Suffix) {
suffixKeys = append(suffixKeys, k) suffixKeys = append(suffixKeys, k)
} }
} }

View File

@ -9,11 +9,11 @@ import (
) )
func TestMemoryReInit(t *testing.T) { func TestMemoryReInit(t *testing.T) {
s := store.NewStore(store.Table("aaa")) s := store.NewStore(store.Namespace("aaa"))
if err := s.Init(store.Table("")); err != nil { if err := s.Init(store.Namespace("")); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(s.Options().Table) > 0 { if len(s.Options().Namespace) > 0 {
t.Error("Init didn't reinitialise the store") t.Error("Init didn't reinitialise the store")
} }
} }
@ -28,7 +28,7 @@ func TestMemoryBasic(t *testing.T) {
func TestMemoryPrefix(t *testing.T) { func TestMemoryPrefix(t *testing.T) {
s := store.NewStore() s := store.NewStore()
if err := s.Init(store.Table("some-prefix")); err != nil { if err := s.Init(store.Namespace("some-prefix")); err != nil {
t.Fatal(err) t.Fatal(err)
} }
basictest(s, t) basictest(s, t)
@ -36,7 +36,7 @@ func TestMemoryPrefix(t *testing.T) {
func TestMemoryNamespace(t *testing.T) { func TestMemoryNamespace(t *testing.T) {
s := store.NewStore() s := store.NewStore()
if err := s.Init(store.Database("some-namespace")); err != nil { if err := s.Init(store.Namespace("some-namespace")); err != nil {
t.Fatal(err) t.Fatal(err)
} }
basictest(s, t) basictest(s, t)
@ -44,7 +44,7 @@ func TestMemoryNamespace(t *testing.T) {
func TestMemoryNamespacePrefix(t *testing.T) { func TestMemoryNamespacePrefix(t *testing.T) {
s := store.NewStore() s := store.NewStore()
if err := s.Init(store.Table("some-prefix"), store.Database("some-namespace")); err != nil { if err := s.Init(store.Namespace("some-namespace")); err != nil {
t.Fatal(err) t.Fatal(err)
} }
basictest(s, t) basictest(s, t)

View File

@ -28,13 +28,12 @@ type Options struct {
TLSConfig *tls.Config TLSConfig *tls.Config
// Name specifies store name // Name specifies store name
Name string Name string
// Database specifies store database // Namespace of the records
Database string Namespace string
// Table specifies store table // Addrs contains store address
Table string Addrs []string
// Nodes contains store address //Wrappers store wrapper that called before actual functions
// TODO: replace with Addrs //Wrappers []Wrapper
Nodes []string
} }
// NewOptions creates options struct // NewOptions creates options struct
@ -90,13 +89,20 @@ func Meter(m meter.Meter) Option {
} }
} }
// Name the name // Name the name of the store
func Name(n string) Option { func Name(n string) Option {
return func(o *Options) { return func(o *Options) {
o.Name = n o.Name = n
} }
} }
// Namespace sets namespace of the store
func Namespace(ns string) Option {
return func(o *Options) {
o.Namespace = ns
}
}
// Tracer sets the tracer // Tracer sets the tracer
func Tracer(t tracer.Tracer) Option { func Tracer(t tracer.Tracer) Option {
return func(o *Options) { return func(o *Options) {
@ -104,27 +110,21 @@ func Tracer(t tracer.Tracer) Option {
} }
} }
// Nodes contains the addresses or other connection information of the backing storage. // Addrs contains the addresses or other connection information of the backing storage.
// For example, an etcd implementation would contain the nodes of the cluster. // For example, an etcd implementation would contain the nodes of the cluster.
// A SQL implementation could contain one or more connection strings. // A SQL implementation could contain one or more connection strings.
func Nodes(a ...string) Option { func Addrs(addrs ...string) Option {
return func(o *Options) { return func(o *Options) {
o.Nodes = a o.Addrs = addrs
} }
} }
// Database allows multiple isolated stores to be kept in one backend, if supported. // ReadOptions configures an individual Read operation
func Database(db string) Option { type ReadOptions struct {
return func(o *Options) { // Context holds external options
o.Database = db Context context.Context
} // Namespace holds namespace
} Namespace string
// Table is analag for a table in database backends or a key prefix in KV backends
func Table(t string) Option {
return func(o *Options) {
o.Table = t
}
} }
// NewReadOptions fills ReadOptions struct with opts slice // NewReadOptions fills ReadOptions struct with opts slice
@ -136,29 +136,35 @@ func NewReadOptions(opts ...ReadOption) ReadOptions {
return options return options
} }
// ReadOptions configures an individual Read operation
type ReadOptions struct {
// Context holds external options
Context context.Context
// Database holds the database name
Database string
// Table holds table name
Table string
// Namespace holds namespace
Namespace string
}
// ReadOption sets values in ReadOptions // ReadOption sets values in ReadOptions
type ReadOption func(r *ReadOptions) type ReadOption func(r *ReadOptions)
// ReadFrom the database and table // ReadContext pass context.Context to ReadOptions
func ReadFrom(database, table string) ReadOption { func ReadContext(ctx context.Context) ReadOption {
return func(r *ReadOptions) { return func(o *ReadOptions) {
r.Database = database o.Context = ctx
r.Table = table
} }
} }
// ReadNamespace pass namespace to ReadOptions
func ReadNamespace(ns string) ReadOption {
return func(o *ReadOptions) {
o.Namespace = ns
}
}
// WriteOptions configures an individual Write operation
type WriteOptions struct {
// Context holds external options
Context context.Context
// Metadata contains additional metadata
Metadata metadata.Metadata
// Namespace holds namespace
Namespace string
// TTL specifies key TTL
TTL time.Duration
}
// NewWriteOptions fills WriteOptions struct with opts slice // NewWriteOptions fills WriteOptions struct with opts slice
func NewWriteOptions(opts ...WriteOption) WriteOptions { func NewWriteOptions(opts ...WriteOption) WriteOptions {
options := WriteOptions{} options := WriteOptions{}
@ -168,47 +174,45 @@ func NewWriteOptions(opts ...WriteOption) WriteOptions {
return options return options
} }
// WriteOptions configures an individual Write operation
type WriteOptions struct {
// Context holds external options
Context context.Context
// Metadata contains additional metadata
Metadata metadata.Metadata
// Database holds database name
Database string
// Table holds table name
Table string
// Namespace holds namespace
Namespace string
// TTL specifies key TTL
TTL time.Duration
}
// WriteOption sets values in WriteOptions // WriteOption sets values in WriteOptions
type WriteOption func(w *WriteOptions) type WriteOption func(w *WriteOptions)
// WriteTo the database and table // WriteContext pass context.Context to wirte options
func WriteTo(database, table string) WriteOption { func WriteContext(ctx context.Context) WriteOption {
return func(w *WriteOptions) { return func(o *WriteOptions) {
w.Database = database o.Context = ctx
w.Table = table
}
}
// WriteTTL is the time the record expires
func WriteTTL(d time.Duration) WriteOption {
return func(w *WriteOptions) {
w.TTL = d
} }
} }
// WriteMetadata add metadata.Metadata // WriteMetadata add metadata.Metadata
func WriteMetadata(md metadata.Metadata) WriteOption { func WriteMetadata(md metadata.Metadata) WriteOption {
return func(w *WriteOptions) { return func(o *WriteOptions) {
w.Metadata = metadata.Copy(md) o.Metadata = metadata.Copy(md)
} }
} }
// WriteTTL is the time the record expires
func WriteTTL(d time.Duration) WriteOption {
return func(o *WriteOptions) {
o.TTL = d
}
}
// WriteNamespace pass namespace to write options
func WriteNamespace(ns string) WriteOption {
return func(o *WriteOptions) {
o.Namespace = ns
}
}
// DeleteOptions configures an individual Delete operation
type DeleteOptions struct {
// Context holds external options
Context context.Context
// Namespace holds namespace
Namespace string
}
// NewDeleteOptions fills DeleteOptions struct with opts slice // NewDeleteOptions fills DeleteOptions struct with opts slice
func NewDeleteOptions(opts ...DeleteOption) DeleteOptions { func NewDeleteOptions(opts ...DeleteOption) DeleteOptions {
options := DeleteOptions{} options := DeleteOptions{}
@ -218,29 +222,33 @@ func NewDeleteOptions(opts ...DeleteOption) DeleteOptions {
return options return options
} }
// DeleteOptions configures an individual Delete operation
type DeleteOptions struct {
// Context holds external options
Context context.Context
// Database holds database name
Database string
// Table holds table name
Table string
// Namespace holds namespace
Namespace string
}
// DeleteOption sets values in DeleteOptions // DeleteOption sets values in DeleteOptions
type DeleteOption func(d *DeleteOptions) type DeleteOption func(d *DeleteOptions)
// DeleteFrom the database and table // DeleteContext pass context.Context to delete options
func DeleteFrom(database, table string) DeleteOption { func DeleteContext(ctx context.Context) DeleteOption {
return func(d *DeleteOptions) { return func(o *DeleteOptions) {
d.Database = database o.Context = ctx
d.Table = table
} }
} }
// DeleteNamespace pass namespace to delete options
func DeleteNamespace(ns string) DeleteOption {
return func(o *DeleteOptions) {
o.Namespace = ns
}
}
// ListOptions configures an individual List operation
type ListOptions struct {
Context context.Context
Prefix string
Suffix string
Namespace string
Limit uint
Offset uint
}
// NewListOptions fills ListOptions struct with opts slice // NewListOptions fills ListOptions struct with opts slice
func NewListOptions(opts ...ListOption) ListOptions { func NewListOptions(opts ...ListOption) ListOptions {
options := ListOptions{} options := ListOptions{}
@ -250,59 +258,50 @@ func NewListOptions(opts ...ListOption) ListOptions {
return options return options
} }
// ListOptions configures an individual List operation
type ListOptions struct {
Context context.Context
Database string
Prefix string
Suffix string
Namespace string
Table string
Limit uint
Offset uint
}
// ListOption sets values in ListOptions // ListOption sets values in ListOptions
type ListOption func(l *ListOptions) type ListOption func(l *ListOptions)
// ListFrom the database and table // ListContext pass context.Context to list options
func ListFrom(database, table string) ListOption { func ListContext(ctx context.Context) ListOption {
return func(l *ListOptions) { return func(o *ListOptions) {
l.Database = database o.Context = ctx
l.Table = table
} }
} }
// ListPrefix returns all keys that are prefixed with key // ListPrefix returns all keys that are prefixed with key
func ListPrefix(p string) ListOption { func ListPrefix(s string) ListOption {
return func(l *ListOptions) { return func(o *ListOptions) {
l.Prefix = p o.Prefix = s
} }
} }
// ListSuffix returns all keys that end with key // ListSuffix returns all keys that end with key
func ListSuffix(s string) ListOption { func ListSuffix(s string) ListOption {
return func(l *ListOptions) { return func(o *ListOptions) {
l.Suffix = s o.Suffix = s
} }
} }
// ListLimit limits the number of returned keys to l // ListLimit limits the number of returned keys
func ListLimit(l uint) ListOption { func ListLimit(n uint) ListOption {
return func(lo *ListOptions) { return func(o *ListOptions) {
lo.Limit = l o.Limit = n
} }
} }
// ListOffset starts returning responses from o. Use in conjunction with Limit for pagination. // ListOffset use with Limit for pagination
func ListOffset(o uint) ListOption { func ListOffset(n uint) ListOption {
return func(l *ListOptions) { return func(o *ListOptions) {
l.Offset = o o.Offset = n
} }
} }
// ExistsOption specifies Exists call options // ListNamespace pass namespace to list options
type ExistsOption func(*ExistsOptions) func ListNamespace(ns string) ListOption {
return func(o *ListOptions) {
o.Namespace = ns
}
}
// ExistsOptions holds options for Exists method // ExistsOptions holds options for Exists method
type ExistsOptions struct { type ExistsOptions struct {
@ -312,6 +311,9 @@ type ExistsOptions struct {
Namespace string Namespace string
} }
// ExistsOption specifies Exists call options
type ExistsOption func(*ExistsOptions)
// NewExistsOptions helper for Exists method // NewExistsOptions helper for Exists method
func NewExistsOptions(opts ...ExistsOption) ExistsOptions { func NewExistsOptions(opts ...ExistsOption) ExistsOptions {
options := ExistsOptions{ options := ExistsOptions{
@ -322,3 +324,24 @@ func NewExistsOptions(opts ...ExistsOption) ExistsOptions {
} }
return options return options
} }
// ExistsContext pass context.Context to exist options
func ExistsContext(ctx context.Context) ExistsOption {
return func(o *ExistsOptions) {
o.Context = ctx
}
}
// ExistsNamespace pass namespace to exist options
func ExistsNamespace(ns string) ExistsOption {
return func(o *ExistsOptions) {
o.Namespace = ns
}
}
// WrapStore adds a store Wrapper to a list of options passed into the store
//func WrapStore(w Wrapper) Option {
// return func(o *Options) {
// o.Wrappers = append(o.Wrappers, w)
// }
//}

84
store/wrapper.go Normal file
View File

@ -0,0 +1,84 @@
package store
import (
"context"
)
// LogfFunc function used for Logf method
//type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{})
//type Wrapper interface {
// Logf logs message with needed level
//Logf(LogfFunc) LogfFunc
//}
type NamespaceStore struct {
s Store
ns string
}
var (
_ Store = &NamespaceStore{}
)
func NewNamespaceStore(s Store, ns string) Store {
return &NamespaceStore{s: s, ns: ns}
}
func (w *NamespaceStore) Init(opts ...Option) error {
return w.s.Init(opts...)
}
func (w *NamespaceStore) Connect(ctx context.Context) error {
return w.s.Connect(ctx)
}
func (w *NamespaceStore) Disconnect(ctx context.Context) error {
return w.s.Disconnect(ctx)
}
func (w *NamespaceStore) Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error {
return w.s.Read(ctx, key, val, append(opts, ReadNamespace(w.ns))...)
}
func (w *NamespaceStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error {
return w.s.Write(ctx, key, val, append(opts, WriteNamespace(w.ns))...)
}
func (w *NamespaceStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error {
return w.s.Delete(ctx, key, append(opts, DeleteNamespace(w.ns))...)
}
func (w *NamespaceStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error {
return w.s.Exists(ctx, key, append(opts, ExistsNamespace(w.ns))...)
}
func (w *NamespaceStore) List(ctx context.Context, opts ...ListOption) ([]string, error) {
return w.s.List(ctx, append(opts, ListNamespace(w.ns))...)
}
func (w *NamespaceStore) Options() Options {
return w.s.Options()
}
func (w *NamespaceStore) Name() string {
return w.s.Name()
}
func (w *NamespaceStore) String() string {
return w.s.String()
}
//type NamespaceWrapper struct{}
//func NewNamespaceWrapper() Wrapper {
// return &NamespaceWrapper{}
//}
/*
func (w *OmitWrapper) Logf(fn LogfFunc) LogfFunc {
return func(ctx context.Context, level Level, msg string, args ...interface{}) {
fn(ctx, level, msg, getArgs(args)...)
}
}
*/