github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/store/client/client.go (about)

     1  package client
     2  
     3  import (
     4  	goctx "context"
     5  	"fmt"
     6  	"io"
     7  	"reflect"
     8  	"time"
     9  
    10  	pb "github.com/tickoalcantara12/micro/v3/proto/store"
    11  	"github.com/tickoalcantara12/micro/v3/service/client"
    12  	"github.com/tickoalcantara12/micro/v3/service/context"
    13  	"github.com/tickoalcantara12/micro/v3/service/context/metadata"
    14  	"github.com/tickoalcantara12/micro/v3/service/errors"
    15  	"github.com/tickoalcantara12/micro/v3/service/store"
    16  )
    17  
    18  type srv struct {
    19  	options store.Options
    20  
    21  	// The database to use
    22  	Database string
    23  
    24  	// The table to use
    25  	Table string
    26  
    27  	// Addresses of the nodes
    28  	Nodes []string
    29  
    30  	// store service client
    31  	Client pb.StoreService
    32  }
    33  
    34  func (s *srv) Close() error {
    35  	return nil
    36  }
    37  
    38  func (s *srv) Init(opts ...store.Option) error {
    39  	for _, o := range opts {
    40  		o(&s.options)
    41  	}
    42  	s.Database = s.options.Database
    43  	s.Table = s.options.Table
    44  	s.Nodes = s.options.Nodes
    45  	s.Client = pb.NewStoreService("store", client.DefaultClient)
    46  	return nil
    47  }
    48  
    49  func (s *srv) Context() goctx.Context {
    50  	ctx := context.DefaultContext
    51  	md := make(metadata.Metadata)
    52  	if len(s.Database) > 0 {
    53  		md["Micro-Database"] = s.Database
    54  	}
    55  
    56  	if len(s.Table) > 0 {
    57  		md["Micro-Table"] = s.Table
    58  	}
    59  	return metadata.MergeContext(ctx, md, true)
    60  }
    61  
    62  // Sync all the known records
    63  func (s *srv) List(opts ...store.ListOption) ([]string, error) {
    64  	options := store.ListOptions{
    65  		Database: s.Database,
    66  		Table:    s.Table,
    67  	}
    68  
    69  	for _, o := range opts {
    70  		o(&options)
    71  	}
    72  
    73  	listOpts := &pb.ListOptions{
    74  		Database: options.Database,
    75  		Table:    options.Table,
    76  		Prefix:   options.Prefix,
    77  		Suffix:   options.Suffix,
    78  		Limit:    uint64(options.Limit),
    79  		Offset:   uint64(options.Offset),
    80  		Order:    string(options.Order),
    81  	}
    82  
    83  	stream, err := s.Client.List(s.Context(), &pb.ListRequest{Options: listOpts}, client.WithAddress(s.Nodes...), client.WithAuthToken())
    84  	if err != nil && errors.Equal(err, errors.NotFound("", "")) {
    85  		return nil, store.ErrNotFound
    86  	} else if err != nil {
    87  		return nil, err
    88  	}
    89  	defer stream.Close()
    90  
    91  	var keys []string
    92  
    93  	for {
    94  		rsp, err := stream.Recv()
    95  		if err == io.EOF {
    96  			break
    97  		}
    98  		if err != nil {
    99  			return keys, err
   100  		}
   101  
   102  		for _, key := range rsp.Keys {
   103  			keys = append(keys, key)
   104  		}
   105  	}
   106  
   107  	return keys, nil
   108  }
   109  
   110  // Read a record with key
   111  func (s *srv) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
   112  	options := store.ReadOptions{
   113  		Database: s.Database,
   114  		Table:    s.Table,
   115  	}
   116  
   117  	for _, o := range opts {
   118  		o(&options)
   119  	}
   120  
   121  	readOpts := &pb.ReadOptions{
   122  		Database: options.Database,
   123  		Table:    options.Table,
   124  		Prefix:   options.Prefix,
   125  		Suffix:   options.Suffix,
   126  		Limit:    uint64(options.Limit),
   127  		Offset:   uint64(options.Offset),
   128  		Order:    string(options.Order),
   129  	}
   130  
   131  	rsp, err := s.Client.Read(s.Context(), &pb.ReadRequest{
   132  		Key:     key,
   133  		Options: readOpts,
   134  	}, client.WithAddress(s.Nodes...), client.WithAuthToken())
   135  	if err != nil && errors.Equal(err, errors.NotFound("", "")) {
   136  		return nil, store.ErrNotFound
   137  	} else if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	records := make([]*store.Record, 0, len(rsp.Records))
   142  
   143  	for _, val := range rsp.Records {
   144  		metadata := make(map[string]interface{})
   145  
   146  		for k, v := range val.Metadata {
   147  			switch v.Type {
   148  			// TODO: parse all types
   149  			default:
   150  				metadata[k] = v
   151  			}
   152  		}
   153  
   154  		records = append(records, &store.Record{
   155  			Key:      val.Key,
   156  			Value:    val.Value,
   157  			Expiry:   time.Duration(val.Expiry) * time.Second,
   158  			Metadata: metadata,
   159  		})
   160  	}
   161  
   162  	return records, nil
   163  }
   164  
   165  // Write a record
   166  func (s *srv) Write(record *store.Record, opts ...store.WriteOption) error {
   167  	options := store.WriteOptions{
   168  		Database: s.Database,
   169  		Table:    s.Table,
   170  	}
   171  
   172  	for _, o := range opts {
   173  		o(&options)
   174  	}
   175  
   176  	writeOpts := &pb.WriteOptions{
   177  		Database: options.Database,
   178  		Table:    options.Table,
   179  	}
   180  
   181  	metadata := make(map[string]*pb.Field)
   182  
   183  	for k, v := range record.Metadata {
   184  		metadata[k] = &pb.Field{
   185  			Type:  reflect.TypeOf(v).String(),
   186  			Value: fmt.Sprintf("%v", v),
   187  		}
   188  	}
   189  
   190  	_, err := s.Client.Write(s.Context(), &pb.WriteRequest{
   191  		Record: &pb.Record{
   192  			Key:      record.Key,
   193  			Value:    record.Value,
   194  			Expiry:   int64(record.Expiry.Seconds()),
   195  			Metadata: metadata,
   196  		},
   197  		Options: writeOpts}, client.WithAddress(s.Nodes...), client.WithAuthToken())
   198  	if err != nil && errors.Equal(err, errors.NotFound("", "")) {
   199  		return store.ErrNotFound
   200  	}
   201  
   202  	return err
   203  }
   204  
   205  // Delete a record with key
   206  func (s *srv) Delete(key string, opts ...store.DeleteOption) error {
   207  	options := store.DeleteOptions{
   208  		Database: s.Database,
   209  		Table:    s.Table,
   210  	}
   211  
   212  	for _, o := range opts {
   213  		o(&options)
   214  	}
   215  
   216  	deleteOpts := &pb.DeleteOptions{
   217  		Database: options.Database,
   218  		Table:    options.Table,
   219  	}
   220  
   221  	_, err := s.Client.Delete(s.Context(), &pb.DeleteRequest{
   222  		Key:     key,
   223  		Options: deleteOpts,
   224  	}, client.WithAddress(s.Nodes...), client.WithAuthToken())
   225  	if err != nil && errors.Equal(err, errors.NotFound("", "")) {
   226  		return store.ErrNotFound
   227  	}
   228  
   229  	return err
   230  }
   231  
   232  func (s *srv) String() string {
   233  	return "service"
   234  }
   235  
   236  func (s *srv) Options() store.Options {
   237  	return s.options
   238  }
   239  
   240  // NewStore returns a new store service implementation
   241  func NewStore(opts ...store.Option) store.Store {
   242  	var options store.Options
   243  	for _, o := range opts {
   244  		o(&options)
   245  	}
   246  
   247  	return &srv{
   248  		options:  options,
   249  		Database: options.Database,
   250  		Table:    options.Table,
   251  		Nodes:    options.Nodes,
   252  		Client:   pb.NewStoreService("store", client.DefaultClient),
   253  	}
   254  }