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)
}
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 {
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 {
prefix := m.prefix(m.opts.Database, m.opts.Table)
return m.exists(prefix, key)
options := NewExistsOptions(opts...)
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 {
readOpts := NewReadOptions(opts...)
prefix := m.prefix(readOpts.Database, readOpts.Table)
return m.get(prefix, key, val)
options := NewReadOptions(opts...)
if options.Namespace == "" {
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 {
writeOpts := NewWriteOptions(opts...)
options := NewWriteOptions(opts...)
if options.Namespace == "" {
options.Namespace = m.opts.Namespace
}
prefix := m.prefix(writeOpts.Database, writeOpts.Table)
key = m.key(prefix, key)
key = m.key(options.Namespace, key)
buf, err := m.opts.Codec.Marshal(val)
if err != nil {
return err
}
m.store.Set(key, buf, writeOpts.TTL)
m.store.Set(key, buf, options.TTL)
return nil
}
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(prefix, key)
m.delete(options.Namespace, key)
return nil
}
@ -166,25 +164,27 @@ func (m *memoryStore) Options() Options {
}
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(prefix, listOptions.Limit, listOptions.Offset)
keys := m.list(options.Namespace, options.Limit, options.Offset)
if len(listOptions.Prefix) > 0 {
if len(options.Prefix) > 0 {
var prefixKeys []string
for _, k := range keys {
if strings.HasPrefix(k, listOptions.Prefix) {
if strings.HasPrefix(k, options.Prefix) {
prefixKeys = append(prefixKeys, k)
}
}
keys = prefixKeys
}
if len(listOptions.Suffix) > 0 {
if len(options.Suffix) > 0 {
var suffixKeys []string
for _, k := range keys {
if strings.HasSuffix(k, listOptions.Suffix) {
if strings.HasSuffix(k, options.Suffix) {
suffixKeys = append(suffixKeys, k)
}
}

View File

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

View File

@ -28,13 +28,12 @@ type Options struct {
TLSConfig *tls.Config
// Name specifies store name
Name string
// Database specifies store database
Database string
// Table specifies store table
Table string
// Nodes contains store address
// TODO: replace with Addrs
Nodes []string
// Namespace of the records
Namespace string
// Addrs contains store address
Addrs []string
//Wrappers store wrapper that called before actual functions
//Wrappers []Wrapper
}
// 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 {
return func(o *Options) {
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
func Tracer(t tracer.Tracer) Option {
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.
// A SQL implementation could contain one or more connection strings.
func Nodes(a ...string) Option {
func Addrs(addrs ...string) Option {
return func(o *Options) {
o.Nodes = a
o.Addrs = addrs
}
}
// Database allows multiple isolated stores to be kept in one backend, if supported.
func Database(db string) Option {
return func(o *Options) {
o.Database = db
}
}
// 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
}
// ReadOptions configures an individual Read operation
type ReadOptions struct {
// Context holds external options
Context context.Context
// Namespace holds namespace
Namespace string
}
// NewReadOptions fills ReadOptions struct with opts slice
@ -136,29 +136,35 @@ func NewReadOptions(opts ...ReadOption) ReadOptions {
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
type ReadOption func(r *ReadOptions)
// ReadFrom the database and table
func ReadFrom(database, table string) ReadOption {
return func(r *ReadOptions) {
r.Database = database
r.Table = table
// ReadContext pass context.Context to ReadOptions
func ReadContext(ctx context.Context) ReadOption {
return func(o *ReadOptions) {
o.Context = ctx
}
}
// 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
func NewWriteOptions(opts ...WriteOption) WriteOptions {
options := WriteOptions{}
@ -168,47 +174,45 @@ func NewWriteOptions(opts ...WriteOption) WriteOptions {
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
type WriteOption func(w *WriteOptions)
// WriteTo the database and table
func WriteTo(database, table string) WriteOption {
return func(w *WriteOptions) {
w.Database = database
w.Table = table
}
}
// WriteTTL is the time the record expires
func WriteTTL(d time.Duration) WriteOption {
return func(w *WriteOptions) {
w.TTL = d
// WriteContext pass context.Context to wirte options
func WriteContext(ctx context.Context) WriteOption {
return func(o *WriteOptions) {
o.Context = ctx
}
}
// WriteMetadata add metadata.Metadata
func WriteMetadata(md metadata.Metadata) WriteOption {
return func(w *WriteOptions) {
w.Metadata = metadata.Copy(md)
return func(o *WriteOptions) {
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
func NewDeleteOptions(opts ...DeleteOption) DeleteOptions {
options := DeleteOptions{}
@ -218,29 +222,33 @@ func NewDeleteOptions(opts ...DeleteOption) DeleteOptions {
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
type DeleteOption func(d *DeleteOptions)
// DeleteFrom the database and table
func DeleteFrom(database, table string) DeleteOption {
return func(d *DeleteOptions) {
d.Database = database
d.Table = table
// DeleteContext pass context.Context to delete options
func DeleteContext(ctx context.Context) DeleteOption {
return func(o *DeleteOptions) {
o.Context = ctx
}
}
// 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
func NewListOptions(opts ...ListOption) ListOptions {
options := ListOptions{}
@ -250,59 +258,50 @@ func NewListOptions(opts ...ListOption) ListOptions {
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
type ListOption func(l *ListOptions)
// ListFrom the database and table
func ListFrom(database, table string) ListOption {
return func(l *ListOptions) {
l.Database = database
l.Table = table
// ListContext pass context.Context to list options
func ListContext(ctx context.Context) ListOption {
return func(o *ListOptions) {
o.Context = ctx
}
}
// ListPrefix returns all keys that are prefixed with key
func ListPrefix(p string) ListOption {
return func(l *ListOptions) {
l.Prefix = p
func ListPrefix(s string) ListOption {
return func(o *ListOptions) {
o.Prefix = s
}
}
// ListSuffix returns all keys that end with key
func ListSuffix(s string) ListOption {
return func(l *ListOptions) {
l.Suffix = s
return func(o *ListOptions) {
o.Suffix = s
}
}
// ListLimit limits the number of returned keys to l
func ListLimit(l uint) ListOption {
return func(lo *ListOptions) {
lo.Limit = l
// ListLimit limits the number of returned keys
func ListLimit(n uint) ListOption {
return func(o *ListOptions) {
o.Limit = n
}
}
// ListOffset starts returning responses from o. Use in conjunction with Limit for pagination.
func ListOffset(o uint) ListOption {
return func(l *ListOptions) {
l.Offset = o
// ListOffset use with Limit for pagination
func ListOffset(n uint) ListOption {
return func(o *ListOptions) {
o.Offset = n
}
}
// ExistsOption specifies Exists call options
type ExistsOption func(*ExistsOptions)
// ListNamespace pass namespace to list options
func ListNamespace(ns string) ListOption {
return func(o *ListOptions) {
o.Namespace = ns
}
}
// ExistsOptions holds options for Exists method
type ExistsOptions struct {
@ -312,6 +311,9 @@ type ExistsOptions struct {
Namespace string
}
// ExistsOption specifies Exists call options
type ExistsOption func(*ExistsOptions)
// NewExistsOptions helper for Exists method
func NewExistsOptions(opts ...ExistsOption) ExistsOptions {
options := ExistsOptions{
@ -322,3 +324,24 @@ func NewExistsOptions(opts ...ExistsOption) ExistsOptions {
}
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)...)
}
}
*/