github.com/SaurabhDubey-Groww/go-cloud@v0.0.0-20221124105541-b26c29285fd8/blob/driver/driver.go (about)

     1  // Copyright 2018 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package driver defines interfaces to be implemented by blob drivers, which
    16  // will be used by the blob package to interact with the underlying services.
    17  // Application code should use package blob.
    18  package driver // import "gocloud.dev/blob/driver"
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"io"
    24  	"strings"
    25  	"time"
    26  
    27  	"gocloud.dev/gcerrors"
    28  )
    29  
    30  // ReaderOptions controls Reader behaviors.
    31  type ReaderOptions struct {
    32  	// BeforeRead is a callback that must be called exactly once before
    33  	// any data is read, unless NewRangeReader returns an error before then, in
    34  	// which case it should not be called at all.
    35  	// asFunc allows drivers to expose driver-specific types;
    36  	// see Bucket.As for more details.
    37  	BeforeRead func(asFunc func(interface{}) bool) error
    38  }
    39  
    40  // Reader reads an object from the blob.
    41  type Reader interface {
    42  	io.ReadCloser
    43  
    44  	// Attributes returns a subset of attributes about the blob.
    45  	// The portable type will not modify the returned ReaderAttributes.
    46  	Attributes() *ReaderAttributes
    47  
    48  	// As allows drivers to expose driver-specific types;
    49  	// see Bucket.As for more details.
    50  	As(interface{}) bool
    51  }
    52  
    53  // Writer writes an object to the blob.
    54  type Writer interface {
    55  	io.WriteCloser
    56  }
    57  
    58  // WriterOptions controls behaviors of Writer.
    59  type WriterOptions struct {
    60  	// BufferSize changes the default size in byte of the maximum part Writer can
    61  	// write in a single request, if supported. Larger objects will be split into
    62  	// multiple requests.
    63  	BufferSize int
    64  	// MaxConcurrency changes the default concurrency for uploading parts.
    65  	MaxConcurrency int
    66  	// CacheControl specifies caching attributes that services may use
    67  	// when serving the blob.
    68  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
    69  	CacheControl string
    70  	// ContentDisposition specifies whether the blob content is expected to be
    71  	// displayed inline or as an attachment.
    72  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
    73  	ContentDisposition string
    74  	// ContentEncoding specifies the encoding used for the blob's content, if any.
    75  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
    76  	ContentEncoding string
    77  	// ContentLanguage specifies the language used in the blob's content, if any.
    78  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language
    79  	ContentLanguage string
    80  	// ContentMD5 is used as a message integrity check.
    81  	// The portable type checks that the MD5 hash of the bytes written matches
    82  	// ContentMD5.
    83  	// If len(ContentMD5) > 0, driver implementations may pass it to their
    84  	// underlying network service to guarantee the integrity of the bytes in
    85  	// transit.
    86  	ContentMD5 []byte
    87  	// Metadata holds key/value strings to be associated with the blob.
    88  	// Keys are guaranteed to be non-empty and lowercased.
    89  	Metadata map[string]string
    90  	// BeforeWrite is a callback that must be called exactly once before
    91  	// any data is written, unless NewTypedWriter returns an error, in
    92  	// which case it should not be called.
    93  	// asFunc allows drivers to expose driver-specific types;
    94  	// see Bucket.As for more details.
    95  	BeforeWrite func(asFunc func(interface{}) bool) error
    96  }
    97  
    98  // CopyOptions controls options for Copy.
    99  type CopyOptions struct {
   100  	// BeforeCopy is a callback that must be called before initiating the Copy.
   101  	// asFunc allows drivers to expose driver-specific types;
   102  	// see Bucket.As for more details.
   103  	BeforeCopy func(asFunc func(interface{}) bool) error
   104  }
   105  
   106  // ReaderAttributes contains a subset of attributes about a blob that are
   107  // accessible from Reader.
   108  type ReaderAttributes struct {
   109  	// ContentType is the MIME type of the blob object. It must not be empty.
   110  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
   111  	ContentType string
   112  	// ModTime is the time the blob object was last modified.
   113  	ModTime time.Time
   114  	// Size is the size of the object in bytes.
   115  	Size int64
   116  }
   117  
   118  // Attributes contains attributes about a blob.
   119  type Attributes struct {
   120  	// CacheControl specifies caching attributes that services may use
   121  	// when serving the blob.
   122  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
   123  	CacheControl string
   124  	// ContentDisposition specifies whether the blob content is expected to be
   125  	// displayed inline or as an attachment.
   126  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
   127  	ContentDisposition string
   128  	// ContentEncoding specifies the encoding used for the blob's content, if any.
   129  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
   130  	ContentEncoding string
   131  	// ContentLanguage specifies the language used in the blob's content, if any.
   132  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language
   133  	ContentLanguage string
   134  	// ContentType is the MIME type of the blob object. It must not be empty.
   135  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
   136  	ContentType string
   137  	// Metadata holds key/value pairs associated with the blob.
   138  	// Keys will be lowercased by the portable type before being returned
   139  	// to the user. If there are duplicate case-insensitive keys (e.g.,
   140  	// "foo" and "FOO"), only one value will be kept, and it is undefined
   141  	// which one.
   142  	Metadata map[string]string
   143  	// CreateTime is the time the blob object was created. If not available,
   144  	// leave as the zero time.
   145  	CreateTime time.Time
   146  	// ModTime is the time the blob object was last modified.
   147  	ModTime time.Time
   148  	// Size is the size of the object in bytes.
   149  	Size int64
   150  	// MD5 is an MD5 hash of the blob contents or nil if not available.
   151  	MD5 []byte
   152  	// ETag for the blob; see https://en.wikipedia.org/wiki/HTTP_ETag.
   153  	ETag string
   154  	// AsFunc allows drivers to expose driver-specific types;
   155  	// see Bucket.As for more details.
   156  	// If not set, no driver-specific types are supported.
   157  	AsFunc func(interface{}) bool
   158  }
   159  
   160  // ListOptions sets options for listing objects in the bucket.
   161  type ListOptions struct {
   162  	// Prefix indicates that only results with the given prefix should be
   163  	// returned.
   164  	Prefix string
   165  	// Delimiter sets the delimiter used to define a hierarchical namespace,
   166  	// like a filesystem with "directories".
   167  	//
   168  	// An empty delimiter means that the bucket is treated as a single flat
   169  	// namespace.
   170  	//
   171  	// A non-empty delimiter means that any result with the delimiter in its key
   172  	// after Prefix is stripped will be returned with ListObject.IsDir = true,
   173  	// ListObject.Key truncated after the delimiter, and zero values for other
   174  	// ListObject fields. These results represent "directories". Multiple results
   175  	// in a "directory" are returned as a single result.
   176  	Delimiter string
   177  	// PageSize sets the maximum number of objects to be returned.
   178  	// 0 means no maximum; driver implementations should choose a reasonable
   179  	// max. It is guaranteed to be >= 0.
   180  	PageSize int
   181  	// PageToken may be filled in with the NextPageToken from a previous
   182  	// ListPaged call.
   183  	PageToken []byte
   184  	// BeforeList is a callback that must be called exactly once during ListPaged,
   185  	// before the underlying service's list is executed.
   186  	// asFunc allows drivers to expose driver-specific types;
   187  	// see Bucket.As for more details.
   188  	BeforeList func(asFunc func(interface{}) bool) error
   189  }
   190  
   191  // ListObject represents a specific blob object returned from ListPaged.
   192  type ListObject struct {
   193  	// Key is the key for this blob.
   194  	Key string
   195  	// ModTime is the time the blob object was last modified.
   196  	ModTime time.Time
   197  	// Size is the size of the object in bytes.
   198  	Size int64
   199  	// MD5 is an MD5 hash of the blob contents or nil if not available.
   200  	MD5 []byte
   201  	// IsDir indicates that this result represents a "directory" in the
   202  	// hierarchical namespace, ending in ListOptions.Delimiter. Key can be
   203  	// passed as ListOptions.Prefix to list items in the "directory".
   204  	// Fields other than Key and IsDir will not be set if IsDir is true.
   205  	IsDir bool
   206  	// AsFunc allows drivers to expose driver-specific types;
   207  	// see Bucket.As for more details.
   208  	// If not set, no driver-specific types are supported.
   209  	AsFunc func(interface{}) bool
   210  }
   211  
   212  // ListPage represents a page of results return from ListPaged.
   213  type ListPage struct {
   214  	// Objects is the slice of objects found. If ListOptions.PageSize > 0,
   215  	// it should have at most ListOptions.PageSize entries.
   216  	//
   217  	// Objects should be returned in lexicographical order of UTF-8 encoded keys,
   218  	// including across pages. I.e., all objects returned from a ListPage request
   219  	// made using a PageToken from a previous ListPage request's NextPageToken
   220  	// should have Key >= the Key for all objects from the previous request.
   221  	Objects []*ListObject
   222  	// NextPageToken should be left empty unless there are more objects
   223  	// to return. The value may be returned as ListOptions.PageToken on a
   224  	// subsequent ListPaged call, to fetch the next page of results.
   225  	// It can be an arbitrary []byte; it need not be a valid key.
   226  	NextPageToken []byte
   227  }
   228  
   229  // Bucket provides read, write and delete operations on objects within it on the
   230  // blob service.
   231  type Bucket interface {
   232  	// ErrorCode should return a code that describes the error, which was returned by
   233  	// one of the other methods in this interface.
   234  	ErrorCode(error) gcerrors.ErrorCode
   235  
   236  	// As converts i to driver-specific types.
   237  	// See https://gocloud.dev/concepts/as/ for background information.
   238  	As(i interface{}) bool
   239  
   240  	// ErrorAs allows drivers to expose driver-specific types for returned
   241  	// errors.
   242  	// See https://gocloud.dev/concepts/as/ for background information.
   243  	ErrorAs(error, interface{}) bool
   244  
   245  	// Attributes returns attributes for the blob. If the specified object does
   246  	// not exist, Attributes must return an error for which ErrorCode returns
   247  	// gcerrors.NotFound.
   248  	// The portable type will not modify the returned Attributes.
   249  	Attributes(ctx context.Context, key string) (*Attributes, error)
   250  
   251  	// ListPaged lists objects in the bucket, in lexicographical order by
   252  	// UTF-8-encoded key, returning pages of objects at a time.
   253  	// Services are only required to be eventually consistent with respect
   254  	// to recently written or deleted objects. That is to say, there is no
   255  	// guarantee that an object that's been written will immediately be returned
   256  	// from ListPaged.
   257  	// opts is guaranteed to be non-nil.
   258  	ListPaged(ctx context.Context, opts *ListOptions) (*ListPage, error)
   259  
   260  	// NewRangeReader returns a Reader that reads part of an object, reading at
   261  	// most length bytes starting at the given offset. If length is negative, it
   262  	// will read until the end of the object. If the specified object does not
   263  	// exist, NewRangeReader must return an error for which ErrorCode returns
   264  	// gcerrors.NotFound.
   265  	// opts is guaranteed to be non-nil.
   266  	NewRangeReader(ctx context.Context, key string, offset, length int64, opts *ReaderOptions) (Reader, error)
   267  
   268  	// NewTypedWriter returns Writer that writes to an object associated with key.
   269  	//
   270  	// A new object will be created unless an object with this key already exists.
   271  	// Otherwise any previous object with the same key will be replaced.
   272  	// The object may not be available (and any previous object will remain)
   273  	// until Close has been called.
   274  	//
   275  	// contentType sets the MIME type of the object to be written. It must not be
   276  	// empty. opts is guaranteed to be non-nil.
   277  	//
   278  	// The caller must call Close on the returned Writer when done writing.
   279  	//
   280  	// Implementations should abort an ongoing write if ctx is later canceled,
   281  	// and do any necessary cleanup in Close. Close should then return ctx.Err().
   282  	NewTypedWriter(ctx context.Context, key, contentType string, opts *WriterOptions) (Writer, error)
   283  
   284  	// Copy copies the object associated with srcKey to dstKey.
   285  	//
   286  	// If the source object does not exist, Copy must return an error for which
   287  	// ErrorCode returns gcerrors.NotFound.
   288  	//
   289  	// If the destination object already exists, it should be overwritten.
   290  	//
   291  	// opts is guaranteed to be non-nil.
   292  	Copy(ctx context.Context, dstKey, srcKey string, opts *CopyOptions) error
   293  
   294  	// Delete deletes the object associated with key. If the specified object does
   295  	// not exist, Delete must return an error for which ErrorCode returns
   296  	// gcerrors.NotFound.
   297  	Delete(ctx context.Context, key string) error
   298  
   299  	// SignedURL returns a URL that can be used to GET the blob for the duration
   300  	// specified in opts.Expiry. opts is guaranteed to be non-nil.
   301  	// If not supported, return an error for which ErrorCode returns
   302  	// gcerrors.Unimplemented.
   303  	SignedURL(ctx context.Context, key string, opts *SignedURLOptions) (string, error)
   304  
   305  	// Close cleans up any resources used by the Bucket. Once Close is called,
   306  	// there will be no method calls to the Bucket other than As, ErrorAs, and
   307  	// ErrorCode. There may be open readers or writers that will receive calls.
   308  	// It is up to the driver as to how these will be handled.
   309  	Close() error
   310  }
   311  
   312  // SignedURLOptions sets options for SignedURL.
   313  type SignedURLOptions struct {
   314  	// Expiry sets how long the returned URL is valid for. It is guaranteed to be > 0.
   315  	Expiry time.Duration
   316  
   317  	// Method is the HTTP method that can be used on the URL; one of "GET", "PUT",
   318  	// or "DELETE". Drivers must implement all 3.
   319  	Method string
   320  
   321  	// ContentType specifies the Content-Type HTTP header the user agent is
   322  	// permitted to use in the PUT request. It must match exactly. See
   323  	// EnforceAbsentContentType for behavior when ContentType is the empty string.
   324  	// If this field is not empty and the bucket cannot enforce the Content-Type
   325  	// header, it must return an Unimplemented error.
   326  	//
   327  	// This field will not be set for any non-PUT requests.
   328  	ContentType string
   329  
   330  	// If EnforceAbsentContentType is true and ContentType is the empty string,
   331  	// then PUTing to the signed URL must fail if the Content-Type header is
   332  	// present or the implementation must return an error if it cannot enforce
   333  	// this. If EnforceAbsentContentType is false and ContentType is the empty
   334  	// string, implementations should validate the Content-Type header if possible.
   335  	// If EnforceAbsentContentType is true and the bucket cannot enforce the
   336  	// Content-Type header, it must return an Unimplemented error.
   337  	//
   338  	// This field will always be false for non-PUT requests.
   339  	EnforceAbsentContentType bool
   340  
   341  	// BeforeSign is a callback that will be called before each call to the
   342  	// the underlying service's sign functionality.
   343  	// asFunc converts its argument to driver-specific types.
   344  	// See https://gocloud.dev/concepts/as/ for background information.
   345  	BeforeSign func(asFunc func(interface{}) bool) error
   346  }
   347  
   348  // prefixedBucket implements Bucket by prepending prefix to all keys.
   349  type prefixedBucket struct {
   350  	base   Bucket
   351  	prefix string
   352  }
   353  
   354  // NewPrefixedBucket returns a Bucket based on b with all keys modified to have
   355  // prefix.
   356  func NewPrefixedBucket(b Bucket, prefix string) Bucket {
   357  	return &prefixedBucket{base: b, prefix: prefix}
   358  }
   359  
   360  func (b *prefixedBucket) ErrorCode(err error) gcerrors.ErrorCode { return b.base.ErrorCode(err) }
   361  func (b *prefixedBucket) As(i interface{}) bool                  { return b.base.As(i) }
   362  func (b *prefixedBucket) ErrorAs(err error, i interface{}) bool  { return b.base.ErrorAs(err, i) }
   363  func (b *prefixedBucket) Attributes(ctx context.Context, key string) (*Attributes, error) {
   364  	return b.base.Attributes(ctx, b.prefix+key)
   365  }
   366  func (b *prefixedBucket) ListPaged(ctx context.Context, opts *ListOptions) (*ListPage, error) {
   367  	var myopts ListOptions
   368  	if opts != nil {
   369  		myopts = *opts
   370  	}
   371  	myopts.Prefix = b.prefix + myopts.Prefix
   372  	page, err := b.base.ListPaged(ctx, &myopts)
   373  	if err != nil {
   374  		return nil, err
   375  	}
   376  	for _, p := range page.Objects {
   377  		p.Key = strings.TrimPrefix(p.Key, b.prefix)
   378  	}
   379  	return page, nil
   380  }
   381  func (b *prefixedBucket) NewRangeReader(ctx context.Context, key string, offset, length int64, opts *ReaderOptions) (Reader, error) {
   382  	return b.base.NewRangeReader(ctx, b.prefix+key, offset, length, opts)
   383  }
   384  func (b *prefixedBucket) NewTypedWriter(ctx context.Context, key, contentType string, opts *WriterOptions) (Writer, error) {
   385  	if key == "" {
   386  		return nil, errors.New("invalid key (empty string)")
   387  	}
   388  	return b.base.NewTypedWriter(ctx, b.prefix+key, contentType, opts)
   389  }
   390  func (b *prefixedBucket) Copy(ctx context.Context, dstKey, srcKey string, opts *CopyOptions) error {
   391  	return b.base.Copy(ctx, b.prefix+dstKey, b.prefix+srcKey, opts)
   392  }
   393  func (b *prefixedBucket) Delete(ctx context.Context, key string) error {
   394  	return b.base.Delete(ctx, b.prefix+key)
   395  }
   396  func (b *prefixedBucket) SignedURL(ctx context.Context, key string, opts *SignedURLOptions) (string, error) {
   397  	return b.base.SignedURL(ctx, b.prefix+key, opts)
   398  }
   399  func (b *prefixedBucket) Close() error { return b.base.Close() }
   400  
   401  // singleKeyBucket implements Bucket by hardwiring a specific key.
   402  type singleKeyBucket struct {
   403  	base Bucket
   404  	key  string
   405  }
   406  
   407  // NewSingleKeyBucket returns a Bucket based on b that always references key.
   408  func NewSingleKeyBucket(b Bucket, key string) Bucket {
   409  	return &singleKeyBucket{base: b, key: key}
   410  }
   411  
   412  func (b *singleKeyBucket) ErrorCode(err error) gcerrors.ErrorCode { return b.base.ErrorCode(err) }
   413  func (b *singleKeyBucket) As(i interface{}) bool                  { return b.base.As(i) }
   414  func (b *singleKeyBucket) ErrorAs(err error, i interface{}) bool  { return b.base.ErrorAs(err, i) }
   415  func (b *singleKeyBucket) Attributes(ctx context.Context, _ string) (*Attributes, error) {
   416  	return b.base.Attributes(ctx, b.key)
   417  }
   418  func (b *singleKeyBucket) ListPaged(ctx context.Context, opts *ListOptions) (*ListPage, error) {
   419  	return nil, errors.New("List not supported for SingleKey buckets")
   420  }
   421  func (b *singleKeyBucket) NewRangeReader(ctx context.Context, _ string, offset, length int64, opts *ReaderOptions) (Reader, error) {
   422  	return b.base.NewRangeReader(ctx, b.key, offset, length, opts)
   423  }
   424  func (b *singleKeyBucket) NewTypedWriter(ctx context.Context, _, contentType string, opts *WriterOptions) (Writer, error) {
   425  	return b.base.NewTypedWriter(ctx, b.key, contentType, opts)
   426  }
   427  func (b *singleKeyBucket) Copy(ctx context.Context, dstKey, _ string, opts *CopyOptions) error {
   428  	return b.base.Copy(ctx, dstKey, b.key, opts)
   429  }
   430  func (b *singleKeyBucket) Delete(ctx context.Context, _ string) error {
   431  	return b.base.Delete(ctx, b.key)
   432  }
   433  func (b *singleKeyBucket) SignedURL(ctx context.Context, _ string, opts *SignedURLOptions) (string, error) {
   434  	return b.base.SignedURL(ctx, b.key, opts)
   435  }
   436  func (b *singleKeyBucket) Close() error { return b.base.Close() }