6
1
mirror of https://git.mills.io/saltyim/saltyim.git synced 2024-06-25 16:28:20 +00:00
prologic-saltyim/client_blob_service.go
James Mills f3b7f14e5e Add .Blobs() service object to the client for easier usage of the Blob API (#184)
Co-authored-by: James Mills <1290234+prologic@users.noreply.github.com>
Reviewed-on: https://git.mills.io/saltyim/saltyim/pulls/184
2023-01-27 23:20:49 +00:00

111 lines
3.0 KiB
Go

package saltyim
import (
"encoding/base64"
"fmt"
"hash/fnv"
"io"
"net/http"
)
// BlobOptions are various options that define a blob such its type, filename, whether it is
// public or not (private by default) and any optional properties that clients can interpret.
type BlobOptions struct {
// Key is the locaiton of the blob option, if omitted a hash of the contents is used as the key
Key string
// Type is the mimetype of the blob, if omitted the type is auto detected
Type string
// Public sets whether the blob is public or pviate (default)
Public bool
// Filename sets the blob's friendly filename (if omitted, no filename is used)
Filename string
// Properties sets optional properties of a blob which are up to clients to interpet
Properties map[string]string
}
// BlobOption is a function type that is used to configure blob options
type BlobOption func(opts *BlobOptions) error
// WithKey sets an explicit key (location) for the blob
func WithKey(key string) BlobOption {
return func(opts *BlobOptions) error {
opts.Key = key
return nil
}
}
// BlobService allows a client to store, retrieve and delete blobs of data as well as store
// properites for a blob that are useful to other clients or users receiving the blob.
type BlobService interface {
Write(r io.Reader, opts ...BlobOption) error
Read(key string) ([]byte, error)
}
// Blobs returns an object that implements the BlobService which uses this client's identity
// and the broker this client is connected to (normally an instance of `saltyd`).
func (cli *Client) Blobs() BlobService {
return &blobService{cli: cli}
}
type blobService struct {
cli *Client
}
func (svc *blobService) Read(key string) ([]byte, error) {
cli := svc.cli
if cli.me.Endpoint() == nil {
if err := cli.me.Refresh(); err != nil {
return nil, fmt.Errorf("unable to find your endpoint for %s: %w", cli.me.String(), err)
}
}
endpoint := cli.me.Endpoint()
endpoint.Path = fmt.Sprintf("/api/v1/blob/%s", key)
data, err := cli.Request(http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, fmt.Errorf("error retrieving blob: %w", err)
}
return data, nil
}
func (svc *blobService) Write(r io.Reader, options ...BlobOption) error {
opts := &BlobOptions{}
for _, option := range options {
if err := option(opts); err != nil {
return fmt.Errorf("error configuring blob options: %w", err)
}
}
cli := svc.cli
if cli.me.Endpoint() == nil {
if err := cli.me.Refresh(); err != nil {
return fmt.Errorf("unable to find your endpoint for %s: %w", cli.me.String(), err)
}
}
endpoint := cli.me.Endpoint()
data, err := io.ReadAll(r)
if err != nil {
return fmt.Errorf("error reading blob contents: %w", err)
}
key := opts.Key
if key == "" {
key = base64.StdEncoding.EncodeToString(fnv.New128().Sum(data))
}
endpoint.Path = fmt.Sprintf("/api/v1/blob/%s", key)
if _, err := cli.Request(http.MethodPut, endpoint.String(), data); err != nil {
return fmt.Errorf("error creating/updating blob: %w", err)
}
return nil
}