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

     1  package handler
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"reflect"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	pb "github.com/tickoalcantara12/micro/v3/proto/store"
    13  	"github.com/tickoalcantara12/micro/v3/service/errors"
    14  	"github.com/tickoalcantara12/micro/v3/service/store"
    15  	"github.com/tickoalcantara12/micro/v3/util/auth/namespace"
    16  )
    17  
    18  const (
    19  	defaultDatabase = namespace.DefaultNamespace
    20  	defaultTable    = namespace.DefaultNamespace
    21  	internalTable   = "store"
    22  )
    23  
    24  type Store struct {
    25  	// local Stores cache
    26  	sync.RWMutex
    27  	Stores map[string]bool
    28  }
    29  
    30  // List all the keys in a table
    31  func (h *Store) List(ctx context.Context, req *pb.ListRequest, stream pb.Store_ListStream) error {
    32  	// set defaults
    33  	if req.Options == nil {
    34  		req.Options = &pb.ListOptions{}
    35  	}
    36  	if len(req.Options.Database) == 0 {
    37  		req.Options.Database = defaultDatabase
    38  	}
    39  	if len(req.Options.Table) == 0 {
    40  		req.Options.Table = defaultTable
    41  	}
    42  
    43  	// authorize the request
    44  	if err := namespace.AuthorizeAdmin(ctx, req.Options.Database, "store.Store.List"); err != nil {
    45  		return err
    46  	}
    47  
    48  	// setup the store
    49  	if err := h.setupTable(req.Options.Database, req.Options.Table); err != nil {
    50  		return errors.InternalServerError("store.Store.List", err.Error())
    51  	}
    52  
    53  	// setup the options
    54  	opts := []store.ListOption{
    55  		store.ListFrom(req.Options.Database, req.Options.Table),
    56  	}
    57  	if len(req.Options.Prefix) > 0 {
    58  		opts = append(opts, store.ListPrefix(req.Options.Prefix))
    59  	}
    60  	if len(req.Options.Suffix) > 0 {
    61  		opts = append(opts, store.ListSuffix(req.Options.Suffix))
    62  	}
    63  	if req.Options.Offset > 0 {
    64  		opts = append(opts, store.ListOffset(uint(req.Options.Offset)))
    65  	}
    66  	if req.Options.Limit > 0 {
    67  		opts = append(opts, store.ListLimit(uint(req.Options.Limit)))
    68  	}
    69  	if len(req.Options.Order) > 0 {
    70  		order := store.OrderAsc
    71  		if req.Options.Order == string(store.OrderDesc) {
    72  			order = store.OrderDesc
    73  		}
    74  		opts = append(opts, store.ListOrder(order))
    75  	}
    76  
    77  	// list from the store
    78  	vals, err := store.DefaultStore.List(opts...)
    79  	if err != nil && err == store.ErrNotFound {
    80  		return errors.NotFound("store.Store.List", err.Error())
    81  	} else if err != nil {
    82  		return errors.InternalServerError("store.Store.List", err.Error())
    83  	}
    84  
    85  	// serialize the response
    86  	// TODO: batch sync
    87  	rsp := new(pb.ListResponse)
    88  	for _, val := range vals {
    89  		rsp.Keys = append(rsp.Keys, val)
    90  	}
    91  
    92  	err = stream.Send(rsp)
    93  	if err == io.EOF {
    94  		return nil
    95  	}
    96  	if err != nil {
    97  		return errors.InternalServerError("store.Store.List", err.Error())
    98  	}
    99  	return nil
   100  }
   101  
   102  // Read records from the store
   103  func (h *Store) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error {
   104  	// set defaults
   105  	if req.Options == nil {
   106  		req.Options = &pb.ReadOptions{}
   107  	}
   108  	if len(req.Options.Database) == 0 {
   109  		req.Options.Database = defaultDatabase
   110  	}
   111  	if len(req.Options.Table) == 0 {
   112  		req.Options.Table = defaultTable
   113  	}
   114  
   115  	// authorize the request
   116  	if err := namespace.AuthorizeAdmin(ctx, req.Options.Database, "store.Store.Read"); err != nil {
   117  		return err
   118  	}
   119  
   120  	// setup the store
   121  	if err := h.setupTable(req.Options.Database, req.Options.Table); err != nil {
   122  		return errors.InternalServerError("store.Store.Read", err.Error())
   123  	}
   124  
   125  	// setup the options
   126  	opts := []store.ReadOption{
   127  		store.ReadFrom(req.Options.Database, req.Options.Table),
   128  	}
   129  	if req.Options.Prefix {
   130  		opts = append(opts, store.ReadPrefix())
   131  	}
   132  	if req.Options.Suffix {
   133  		opts = append(opts, store.ReadSuffix())
   134  	}
   135  	if req.Options.Limit > 0 {
   136  		opts = append(opts, store.ReadLimit(uint(req.Options.Limit)))
   137  	}
   138  	if req.Options.Offset > 0 {
   139  		opts = append(opts, store.ReadOffset(uint(req.Options.Offset)))
   140  	}
   141  	if len(req.Options.Order) > 0 {
   142  		order := store.OrderAsc
   143  		if req.Options.Order == string(store.OrderDesc) {
   144  			order = store.OrderDesc
   145  		}
   146  		opts = append(opts, store.ReadOrder(order))
   147  	}
   148  
   149  	// read from the database
   150  	vals, err := store.DefaultStore.Read(req.Key, opts...)
   151  	if err != nil && err == store.ErrNotFound {
   152  		return errors.NotFound("store.Store.Read", err.Error())
   153  	} else if err != nil {
   154  		return errors.InternalServerError("store.Store.Read", err.Error())
   155  	}
   156  
   157  	// serialize the result
   158  	for _, val := range vals {
   159  		metadata := make(map[string]*pb.Field)
   160  		for k, v := range val.Metadata {
   161  			metadata[k] = &pb.Field{
   162  				Type:  reflect.TypeOf(v).String(),
   163  				Value: fmt.Sprintf("%v", v),
   164  			}
   165  		}
   166  		rsp.Records = append(rsp.Records, &pb.Record{
   167  			Key:      val.Key,
   168  			Value:    val.Value,
   169  			Expiry:   int64(val.Expiry.Seconds()),
   170  			Metadata: metadata,
   171  		})
   172  	}
   173  	return nil
   174  }
   175  
   176  // Write to the store
   177  func (h *Store) Write(ctx context.Context, req *pb.WriteRequest, rsp *pb.WriteResponse) error {
   178  	// validate the request
   179  	if req.Record == nil {
   180  		return errors.BadRequest("store.Store.Write", "no record specified")
   181  	}
   182  
   183  	// set defaults
   184  	if req.Options == nil {
   185  		req.Options = &pb.WriteOptions{}
   186  	}
   187  	if len(req.Options.Database) == 0 {
   188  		req.Options.Database = defaultDatabase
   189  	}
   190  	if len(req.Options.Table) == 0 {
   191  		req.Options.Table = defaultTable
   192  	}
   193  
   194  	// authorize the request
   195  	if err := namespace.AuthorizeAdmin(ctx, req.Options.Database, "store.Store.Write"); err != nil {
   196  		return err
   197  	}
   198  
   199  	// setup the store
   200  	if err := h.setupTable(req.Options.Database, req.Options.Table); err != nil {
   201  		return errors.InternalServerError("store.Store.Write", err.Error())
   202  	}
   203  
   204  	// setup the options
   205  	opts := []store.WriteOption{
   206  		store.WriteTo(req.Options.Database, req.Options.Table),
   207  	}
   208  
   209  	// construct the record
   210  	metadata := make(map[string]interface{})
   211  	for k, v := range req.Record.Metadata {
   212  		metadata[k] = v.Value
   213  	}
   214  	record := &store.Record{
   215  		Key:      req.Record.Key,
   216  		Value:    req.Record.Value,
   217  		Expiry:   time.Duration(req.Record.Expiry) * time.Second,
   218  		Metadata: metadata,
   219  	}
   220  
   221  	// write to the store
   222  	err := store.DefaultStore.Write(record, opts...)
   223  	if err != nil && err == store.ErrNotFound {
   224  		return errors.NotFound("store.Store.Write", err.Error())
   225  	} else if err != nil {
   226  		return errors.InternalServerError("store.Store.Write", err.Error())
   227  	}
   228  
   229  	return nil
   230  }
   231  
   232  func (h *Store) Delete(ctx context.Context, req *pb.DeleteRequest, rsp *pb.DeleteResponse) error {
   233  	// set defaults
   234  	if req.Options == nil {
   235  		req.Options = &pb.DeleteOptions{}
   236  	}
   237  	if len(req.Options.Database) == 0 {
   238  		req.Options.Database = defaultDatabase
   239  	}
   240  	if len(req.Options.Table) == 0 {
   241  		req.Options.Table = defaultTable
   242  	}
   243  
   244  	// authorize the request
   245  	if err := namespace.AuthorizeAdmin(ctx, req.Options.Database, "store.Store.Delete"); err != nil {
   246  		return err
   247  	}
   248  
   249  	// setup the store
   250  	if err := h.setupTable(req.Options.Database, req.Options.Table); err != nil {
   251  		return errors.InternalServerError("store.Store.Delete", err.Error())
   252  	}
   253  
   254  	// setup the options
   255  	opts := []store.DeleteOption{
   256  		store.DeleteFrom(req.Options.Database, req.Options.Table),
   257  	}
   258  
   259  	// delete from the store
   260  	if err := store.DefaultStore.Delete(req.Key, opts...); err == store.ErrNotFound {
   261  		return errors.NotFound("store.Store.Delete", err.Error())
   262  	} else if err != nil {
   263  		return errors.InternalServerError("store.Store.Delete", err.Error())
   264  	}
   265  
   266  	return nil
   267  }
   268  
   269  // Databases lists all the databases
   270  func (h *Store) Databases(ctx context.Context, req *pb.DatabasesRequest, rsp *pb.DatabasesResponse) error {
   271  	// authorize the request
   272  	if err := namespace.AuthorizeAdmin(ctx, defaultDatabase, "store.Store.Database"); err != nil {
   273  		return err
   274  	}
   275  
   276  	// read the databases from the store
   277  	opts := []store.ReadOption{
   278  		store.ReadPrefix(),
   279  		store.ReadFrom(defaultDatabase, internalTable),
   280  	}
   281  	recs, err := store.DefaultStore.Read("databases/", opts...)
   282  	if err != nil {
   283  		return errors.InternalServerError("store.Store.Databases", err.Error())
   284  	}
   285  
   286  	// serialize the response
   287  	rsp.Databases = make([]string, len(recs))
   288  	for i, r := range recs {
   289  		rsp.Databases[i] = strings.TrimPrefix(r.Key, "databases/")
   290  	}
   291  	return nil
   292  }
   293  
   294  // Tables returns all the tables in a database
   295  func (h *Store) Tables(ctx context.Context, req *pb.TablesRequest, rsp *pb.TablesResponse) error {
   296  	// set defaults
   297  	if len(req.Database) == 0 {
   298  		req.Database = defaultDatabase
   299  	}
   300  
   301  	// authorize the request
   302  	if err := namespace.AuthorizeAdmin(ctx, req.Database, "store.Store.Tables"); err != nil {
   303  		return err
   304  	}
   305  
   306  	// construct the options
   307  	opts := []store.ReadOption{
   308  		store.ReadPrefix(),
   309  		store.ReadFrom(defaultDatabase, internalTable),
   310  	}
   311  
   312  	// perform the query
   313  	query := fmt.Sprintf("tables/%v/", req.Database)
   314  	recs, err := store.DefaultStore.Read(query, opts...)
   315  	if err != nil {
   316  		return errors.InternalServerError("store.Store.Tables", err.Error())
   317  	}
   318  
   319  	// serialize the response
   320  	rsp.Tables = make([]string, len(recs))
   321  	for i, r := range recs {
   322  		rsp.Tables[i] = strings.TrimPrefix(r.Key, "tables/"+req.Database+"/")
   323  	}
   324  	return nil
   325  }
   326  
   327  func (h *Store) setupTable(database, table string) error {
   328  	// lock (might be a race)
   329  	h.Lock()
   330  	defer h.Unlock()
   331  
   332  	// attempt to get the database
   333  	if _, ok := h.Stores[database+":"+table]; ok {
   334  		return nil
   335  	}
   336  
   337  	// record the new database in the internal store
   338  	opt := store.WriteTo(defaultDatabase, internalTable)
   339  	dbRecord := &store.Record{Key: "databases/" + database, Value: []byte{}}
   340  	if err := store.DefaultStore.Write(dbRecord, opt); err != nil {
   341  		return fmt.Errorf("Error writing new database to internal table: %v", err)
   342  	}
   343  
   344  	// record the new table in the internal store
   345  	tableRecord := &store.Record{Key: "tables/" + database + "/" + table, Value: []byte{}}
   346  	if err := store.DefaultStore.Write(tableRecord, opt); err != nil {
   347  		return fmt.Errorf("Error writing new table to internal table: %v", err)
   348  	}
   349  
   350  	// write to the cache
   351  	h.Stores[database+":"+table] = true
   352  	return nil
   353  }