objst

package module
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2023 License: Apache-2.0 Imports: 21 Imported by: 0

README

objst

objst is an emmedable object storage written in golang. It can also be used to serve public http traffic. It is based upon BadgerDB, a highly performant key-value store.

Bucket

A bucket is representing an object storage. It allows you to interact with the underlying object storage in an easy manner. To create a simple bucket you can use the following code snippet:

func main() {
  opts := objst.NewDefaultBucketOptions()
  bucket, err := objst.NewBucket(opts)
  if err != nil {
    panic(err)
  }
  // use the bucket for different operations
}

Object

An object is the main abstraction in objst to represent different payload with some metadata.

An object can be created using the NewObject function:

func main() {
  obj, err := objst.NewObject("name", "owner")
  if err != nil {
    panic(err)
  }
}

NOTE: The owner of an object has to be a valid uuid-v4. The uuid can be created using the google/uuid package. It is used internally for testing and promises the most resilient results in production use. If you don't differentiate between different owners you can use objst.SystemOwner which will assign a one time calculated uuid to the object. This should be only used if the different objects are all managed by the same owner and the object storage will only be consumed by the same owner over http. Otherwise you will risk some security issues on your end (no authorization).

The object struct has implemented many useful interfaces which allow you to use it as a usual file. For example an object can be passed to any function which accepts an io.Reader, io.Writer, io.WriterTo or io.ReaderFrom.

func main() {
  obj, err := objst.NewObject("name", "owner")
  if err != nil {
    panic(err)
  }
  buf := new(bytes.Buffer)
  if _, err := buf.ReadFrom(obj); err != nil {
    panic(err)
  }
  if _, err := buf.WriteTo(obj); err != nil {
    return err
  }
}

Metadata

The most powerful feature of objst is the use of meta data. Meta data are custom key-value pairs which will be associated with object and used for querying purposes. Setting a key-value on an object can be done using the obj.SetMetaKey function. Some meta data is managed directly by objst and cannot be set by you. For example objst.MetaKeyID or objst.MetaKeyCreatedAt can not be set using obj.SetMetaKey. The key of the meta data is of type objst.MetaKey and the value is a string.

func main() {
  obj, err := objst.NewObject("name", "owner")
  if err != nil {
    panic(err)
  }
  // set the meta data contentType
  obj.SetMetaKey(objst.MetaKeyContentType, "text/plain")

  // get the meta data id
  id := obj.GetMetaKey(objst.MetaKeyID)
}

There are some helper function implemented for the object struct e.g ID() or Owner() which will return the meta data in a convenient way. Calling ID() is the same as obj.GetMetaKey(objst.MetaKeyID).

objst.MetaKeyContentType

One of the most important meta data is the content-type of the object. This will be used as the content-type to serve the object over http and is required for every object. objst is making best effort assumptions to get the official mime-type of the uploaded file using the specified file extension. If the file extension cannot be found it will fallback to the user defined multipart form key objst.MetaKeyContentType. If none is provided an error will be returned. If one is found it will automatically be registered to the runtime and the content-type meta data will be specified. Every new object created after the extension is registered to the runtime using objst.NewObject will automatically have the MetaKeyContentType set so you don't have to worry about it. To make your life as easy as possible you can implement any kind of init function which will register your unofficial mime types using mime.AddExtensionType:

func main() {
  if err := run(); err != nil {
    log.Fatal(err)
  }
}

// run can also be named `init` so the go
// runtime will be calling it on your behalf.
// But be aware that using init is sometimes
// not considered best practice (uber-styleguide/google-styleguid)
func run() error {
  // every extension has to begin with a leading `.`
  unofficialTypes := map[string]string{
    ".test": "text/plain",
  }

  for ext, mimeType := range unofficialTypes {
    objst.AddExtensionType(ext, mimeType)
  }
}

An example is provided at examples.

Queries

objst.NewQuery allows you to get multiple or one object at once in a convenient way. For example you can get all the objects which have the meta data foo=bar in the following way:

func main() {
  opts := objst.NewDefaultBucketOptions()
  bucket, err := objst.NewBucket(opts)
  if err != nil {
    panic(err)
  }
  // Create a query with the parameter foo=bar and the `Get` operation
  // which is the default.
  q := objst.NewQuery().Param("foo", "bar")

  objs, err := bucket.Execute(q)
  if err != nil {
    panic(err)
  }
}

The query is smart engough to figure out if only one record will be fetched or multiple. This allows you to use queries to fetch one record in an efficient manner:

func main() {
  opts := objst.NewDefaultBucketOptions()
  bucket, err := objst.NewBucket(opts)
  if err != nil {
    panic(err)
  }

  // Get one object for the owner `owner` and name `name`.
  q := objst.NewQuery().Owner("owner").Name("name")

  objs, err := bucket.Execute(q)
  if err != nil {
    panic(err)
  }

  // or fetch by id
  q = objst.NewQuery().ID("id")
  objs, err := bucket.Execute(q)
  if err != nil {
    panic(err)
  }
}

HTTP Handler

objst delivers a default HTTPHandler to serve objects over http.

func main() {
  opts := objst.NewDefaultBucketOptions()
  bucket, err := objst.NewBucket(opts)
  if err != nil {
    panic(err)
  }

  // the handler options allow you to set different
  // parameters to modify the behavior of the handler.
  // For the different options available see:
  // https://pkg.go.dev/github.com/naivary/objst#HTTPHandlerOptions
  handlerOpts := objst.DefaultHTTPHandlerOptions()
  hl := objst.NewHTTPHandler(handlerOpts)
}

All endpoints require authentication some require authorization. You can specify how authorization or authentication is implemented by setting the IsAuthorized and IsAuthenticated middleware in the handler's options. By default IsAuthenticated and IsAuthorized will allow all incoming request.

The endpoints are as follow:

  1. GET /objst/{id}: Get the object as a model without the payload. The model includes the name, owner, id and the user defined meta data.
  2. GET /objst/read/{id}: Read the payload of the object
  3. DELETE /objst/{id}: Delete the object
  4. POST /objst/upload: Upload a file to the object storage. The file will be retrived using opts.FormKey. The Content-Type of the object can be specified using the contentType key in the multipart form.

The first three endpoints require authentication and authorization the last one only requires authentication and the objst.CtxKeyOwner set in the request context.

Examples

Some examples are being provided in the examples directory. Use these as a starting point and tailor the behavior to your liking and needed requirements.

Documentation

Index

Constants

View Source
const (
	// logical `Or` relationship
	Or action = iota + 1

	// logical `And` relationship
	And
)
View Source
const (
	OperationDelete = iota + 1

	OperationGet
)

Variables

View Source
var (
	ErrContentTypeNotExist     = errors.New("missing content type metadata")
	ErrEmptyPayload            = errors.New("object doesn't contain any payload")
	ErrObjectIsImmutable       = errors.New("object is immutable. Create a new object")
	ErrMustIncludeOwnerAndName = errors.New("object is immutable. Create a new object")
	ErrInvalidNamePattern      = fmt.Errorf("object name must match the following regex pattern: %s", objectNamePattern)
)

Object errors

View Source
var (
	ErrMissingOwner      = errors.New("missing owner in the request context")
	ErrUknownContentType = errors.New("content type of the file is not an official mime-type and no contentType key could be found in the form")
)

HTTP errors

View Source
var (
	ErrEmptyQuery          = errors.New("empty query")
	ErrNameOwnerCtxMissing = errors.New("name is set but missing owner")
)

Query errors

View Source
var (
	SystemOwner = uuid.NewString()
)

Functions

func DefaultOptions added in v0.10.0

func DefaultOptions() badger.Options

Types

type Bucket

type Bucket struct {
	// contains filtered or unexported fields
}

func NewBucket

func NewBucket(opts BucketOptions) (*Bucket, error)

NewBucket will create a new object storage with the provided options. The `Dir` option will be overwritten by the application to have a gurantee about the data path.

func (Bucket) BatchCreate

func (b Bucket) BatchCreate(objs []*Object) error

BatchCreate inserts multiple objects in an efficient way.

func (Bucket) Create

func (b Bucket) Create(obj *Object) error

Create inserts the given object into the storage. If you have to create multiple objects use `BatchCreate` which is more performant than multiple calls to Create.

func (Bucket) Delete added in v0.10.0

func (b Bucket) Delete(q *Query) error

func (Bucket) DeleteByID

func (b Bucket) DeleteByID(id string) error

func (Bucket) DeleteByName

func (b Bucket) DeleteByName(name, owner string) error

func (Bucket) Execute added in v0.10.0

func (b Bucket) Execute(q *Query) ([]*Object, error)

func (Bucket) Get added in v0.10.0

func (b Bucket) Get(q *Query) ([]*Object, error)

func (Bucket) GetByID

func (b Bucket) GetByID(id string) (*Object, error)

func (Bucket) GetByName

func (b Bucket) GetByName(name, owner string) (*Object, error)

func (Bucket) GetMeta added in v0.10.0

func (b Bucket) GetMeta(id string) (*Metadata, error)

func (Bucket) Read added in v0.10.0

func (b Bucket) Read(id string, w io.Writer) error

func (Bucket) Shutdown

func (b Bucket) Shutdown() error

type BucketOptions added in v0.11.0

type BucketOptions badger.Options

func NewDefaultBucketOptions added in v0.11.0

func NewDefaultBucketOptions() BucketOptions

type CtxKey added in v0.10.0

type CtxKey string
const (
	CtxKeyOwner CtxKey = "owner"
	CtxKeyReqID CtxKey = "reqid"
)

type HTTPHandler added in v0.10.0

type HTTPHandler struct {
	// contains filtered or unexported fields
}

func NewHTTPHandler added in v0.10.0

func NewHTTPHandler(bucket *Bucket, opts HTTPHandlerOptions) *HTTPHandler

func (*HTTPHandler) Get added in v0.10.0

func (h *HTTPHandler) Get(w http.ResponseWriter, r *http.Request)

Get will return the object model witht he given payload iff any object is found.

func (*HTTPHandler) Read added in v0.10.0

func (h *HTTPHandler) Read(w http.ResponseWriter, r *http.Request)

func (*HTTPHandler) Remove added in v0.10.0

func (h *HTTPHandler) Remove(w http.ResponseWriter, r *http.Request)

func (*HTTPHandler) ServeHTTP added in v0.10.0

func (h *HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler

func (*HTTPHandler) Upload added in v0.10.0

func (h *HTTPHandler) Upload(w http.ResponseWriter, r *http.Request)

type HTTPHandlerOptions added in v0.10.0

type HTTPHandlerOptions struct {
	// MaxUploadSize is limiting the size of a file
	// which can be uploaded using the /objst/upload
	// endpoint. Default: 32 MB.
	MaxUploadSize int64

	// FormKey is the key to access the file
	// in the multipart form. Default: "file"
	FormKey string

	// IsAuthorized is the middleware used to validate
	// if the incoming request is considered authorized.
	// By default all request will be considered authorized.
	// This middleware is further usedj to inject the `CtxKeyOwner`
	// key into the request context to set the owner.
	IsAuthorized func(http.Handler) http.Handler

	// IsAuthenticated is the middleware used to validate
	// if the incoming request is considered authenticated.
	// By default all request will be considered authenticated.
	IsAuthenticated func(http.Handler) http.Handler

	// Handler is used to serve public http traffic.
	// By default if the handler is nil it will be replaced
	// by the default handler.
	Handler http.Handler

	// Logger is the default logger. By default slog.Logger
	// with the text handler will be used.
	Logger *slog.Logger
}

func DefaultHTTPHandlerOptions added in v0.10.0

func DefaultHTTPHandlerOptions() HTTPHandlerOptions

type MetaKey added in v0.10.0

type MetaKey string
const (
	MetaKeyCreatedAt   MetaKey = "createdAt"
	MetaKeyContentType MetaKey = "contentType"
	MetaKeyName        MetaKey = "name"
	MetaKeyID          MetaKey = "id"
	MetaKeyOwner       MetaKey = "owner"
)

func (MetaKey) String added in v0.10.0

func (m MetaKey) String() string

type Metadata added in v0.10.0

type Metadata struct {
	// contains filtered or unexported fields
}

func NewMetadata added in v0.10.0

func NewMetadata() *Metadata

func (*Metadata) Compare added in v0.10.0

func (m *Metadata) Compare(md *Metadata, act action) bool

func (Metadata) Del added in v0.10.0

func (m Metadata) Del(k MetaKey)

func (Metadata) Encode added in v0.10.0

func (m Metadata) Encode() string

func (Metadata) Get added in v0.10.0

func (m Metadata) Get(k MetaKey) string

func (Metadata) Has added in v0.10.0

func (m Metadata) Has(k MetaKey) bool

func (Metadata) Marshal added in v0.10.0

func (m Metadata) Marshal() ([]byte, error)

func (Metadata) Merge added in v0.10.0

func (m Metadata) Merge(mp map[MetaKey]string)

func (Metadata) Set added in v0.10.0

func (m Metadata) Set(k MetaKey, v string)

Set will insert the given key value pair iff it isn't a systemKey like MetaKeyID or MetaKeyCreatedAt.

func (*Metadata) Unmarshal added in v0.10.0

func (m *Metadata) Unmarshal(data []byte) error

func (Metadata) UserDefinedPairs added in v0.10.0

func (m Metadata) UserDefinedPairs() map[MetaKey]string

type Object

type Object struct {
	// contains filtered or unexported fields
}

func NewObject

func NewObject(name, owner string) (*Object, error)

func (*Object) BinaryMarshaler

func (o *Object) BinaryMarshaler() ([]byte, error)

func (*Object) BinaryUnmarshaler

func (o *Object) BinaryUnmarshaler(data []byte) error

func (*Object) GetMetaKey added in v0.10.0

func (o *Object) GetMetaKey(k MetaKey) string

GetMeta returns the corresponding value of the provided key. The bool is indicating if the value was retrieved successfully.

func (*Object) HasMetaKey

func (o *Object) HasMetaKey(k MetaKey) bool

HasMetaKey check if the meta data of the object contains the given key.

func (Object) ID

func (o Object) ID() string

func (*Object) Marshal

func (o *Object) Marshal() ([]byte, error)

func (Object) Name

func (o Object) Name() string

func (Object) Owner

func (o Object) Owner() string

func (Object) Payload

func (o Object) Payload() []byte

func (*Object) Read

func (o *Object) Read(b []byte) (int, error)

func (*Object) ReadFrom

func (o *Object) ReadFrom(r io.Reader) (int64, error)

func (*Object) Reset

func (o *Object) Reset()

Reset resets the payload

func (*Object) SetMetaKey added in v0.10.0

func (o *Object) SetMetaKey(k MetaKey, v string)

SetMeta will set the given key and value as a meta data key-pair, over- writing any key-pair which has been set before.

func (*Object) ToModel

func (o *Object) ToModel() *objectModel

func (*Object) Unmarshal

func (o *Object) Unmarshal(data []byte) error

func (*Object) Write

func (o *Object) Write(p []byte) (int, error)

Write will write the data iff the object is mutable. Otherwise an ErrObjectIsImmutable will be returned. An object is mutable if it isn't inserted or retrieved from the store.

func (*Object) WriteTo

func (o *Object) WriteTo(w io.Writer) (int64, error)

type Query

type Query struct {
	// contains filtered or unexported fields
}

func NewQuery

func NewQuery() *Query

func (*Query) Action added in v0.10.0

func (q *Query) Action(act action) *Query

Action sets the logical connection between the params and the meta data of all compared objects.

func (*Query) ID added in v0.10.0

func (q *Query) ID(id string) *Query

func (*Query) Name added in v0.10.0

func (q *Query) Name(name string) *Query

func (*Query) Operation added in v0.10.0

func (q *Query) Operation(op operation) *Query

func (*Query) Owner added in v0.10.0

func (q *Query) Owner(owner string) *Query

func (*Query) Param added in v0.10.0

func (q *Query) Param(k MetaKey, v string) *Query

Param sets a given key value pair as a parameter of the query.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL