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

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	"net/http"
     8  
     9  	pb "github.com/tickoalcantara12/micro/v3/proto/store"
    10  	"github.com/tickoalcantara12/micro/v3/service/client"
    11  	"github.com/tickoalcantara12/micro/v3/service/errors"
    12  	"github.com/tickoalcantara12/micro/v3/service/store"
    13  )
    14  
    15  const bufferSize = 1024
    16  
    17  // NewBlobStore returns a new store service implementation
    18  func NewBlobStore() store.BlobStore {
    19  	return &blob{}
    20  }
    21  
    22  type blob struct {
    23  	client pb.BlobStoreService
    24  }
    25  
    26  func (b *blob) Read(key string, opts ...store.BlobOption) (io.Reader, error) {
    27  	// validate the key
    28  	if len(key) == 0 {
    29  		return nil, store.ErrMissingKey
    30  	}
    31  
    32  	// parse the options
    33  	var options store.BlobOptions
    34  	for _, o := range opts {
    35  		o(&options)
    36  	}
    37  
    38  	// execute the rpc
    39  	stream, err := b.cli().Read(context.TODO(), &pb.BlobReadRequest{
    40  		Key: key,
    41  		Options: &pb.BlobOptions{
    42  			Namespace: options.Namespace,
    43  		},
    44  	}, client.WithAuthToken())
    45  
    46  	// handle the error
    47  	if verr := errors.FromError(err); verr != nil && verr.Code == http.StatusNotFound {
    48  		return nil, store.ErrNotFound
    49  	} else if verr != nil {
    50  		return nil, verr
    51  	} else if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	// create a buffer to store the bytes in
    56  	buf := bytes.NewBuffer(nil)
    57  
    58  	// keep recieving bytes from the stream until it's closed by the server
    59  	for {
    60  		res, err := stream.Recv()
    61  		if err == io.EOF {
    62  			break
    63  		} else if err != nil {
    64  			return nil, err
    65  		}
    66  
    67  		buf.Write(res.Blob)
    68  	}
    69  
    70  	// return the bytes
    71  	return buf, nil
    72  }
    73  
    74  func (b *blob) Write(key string, blob io.Reader, opts ...store.BlobOption) error {
    75  	// validate the key
    76  	if len(key) == 0 {
    77  		return store.ErrMissingKey
    78  	}
    79  
    80  	// parse the options
    81  	var options store.BlobOptions
    82  	for _, o := range opts {
    83  		o(&options)
    84  	}
    85  
    86  	// setup a context
    87  	ctx, cancel := context.WithCancel(context.TODO())
    88  	defer cancel()
    89  
    90  	// open the stream
    91  	stream, err := b.cli().Write(ctx, client.WithAuthToken())
    92  	if verr := errors.FromError(err); verr != nil {
    93  		return verr
    94  	} else if err != nil {
    95  		return err
    96  	}
    97  
    98  	// read from the blob and stream it to the server
    99  	buffer := make([]byte, bufferSize)
   100  	for {
   101  		num, err := blob.Read(buffer)
   102  		if err == io.EOF {
   103  			break
   104  		} else if err != nil {
   105  			return err
   106  		}
   107  
   108  		req := &pb.BlobWriteRequest{
   109  			Key: key,
   110  			Options: &pb.BlobOptions{
   111  				Namespace:   options.Namespace,
   112  				Public:      options.Public,
   113  				ContentType: options.ContentType,
   114  			},
   115  			Blob: buffer[:num],
   116  		}
   117  
   118  		if err := stream.Send(req); err != nil {
   119  			return err
   120  		}
   121  	}
   122  
   123  	// wait for the server to process the blob
   124  	_, err = stream.CloseAndRecv()
   125  	return err
   126  }
   127  
   128  func (b *blob) Delete(key string, opts ...store.BlobOption) error {
   129  	// validate the key
   130  	if len(key) == 0 {
   131  		return store.ErrMissingKey
   132  	}
   133  
   134  	// parse the options
   135  	var options store.BlobOptions
   136  	for _, o := range opts {
   137  		o(&options)
   138  	}
   139  
   140  	// execute the rpc
   141  	_, err := b.cli().Delete(context.TODO(), &pb.BlobDeleteRequest{
   142  		Key: key,
   143  		Options: &pb.BlobOptions{
   144  			Namespace: options.Namespace,
   145  		},
   146  	}, client.WithAuthToken())
   147  
   148  	// handle the error
   149  	if verr := errors.FromError(err); verr != nil && verr.Code == http.StatusNotFound {
   150  		return store.ErrNotFound
   151  	} else if verr != nil {
   152  		return verr
   153  	}
   154  
   155  	return err
   156  }
   157  
   158  func (b *blob) cli() pb.BlobStoreService {
   159  	if b.client == nil {
   160  		b.client = pb.NewBlobStoreService("store", client.DefaultClient)
   161  	}
   162  	return b.client
   163  }
   164  
   165  func (b *blob) List(opts ...store.BlobListOption) ([]string, error) {
   166  
   167  	// parse the options
   168  	var options store.BlobListOptions
   169  	for _, o := range opts {
   170  		o(&options)
   171  	}
   172  
   173  	// execute the rpc
   174  	rsp, err := b.cli().List(context.TODO(), &pb.BlobListRequest{
   175  		Options: &pb.BlobListOptions{
   176  			Namespace: options.Namespace,
   177  			Prefix:    options.Prefix,
   178  		},
   179  	}, client.WithAuthToken())
   180  
   181  	// handle the error
   182  	if verr := errors.FromError(err); verr != nil && verr.Code == http.StatusNotFound {
   183  		return nil, store.ErrNotFound
   184  	} else if verr != nil {
   185  		return nil, verr
   186  	} else if err != nil {
   187  		return nil, err
   188  	}
   189  
   190  	return rsp.Keys, nil
   191  }