gifs/api

Paddy 2014-08-27 Child:1bbbe113f599

0:08ec88016e2f Go to Latest

gifs/api/storage.go

Spike out functionality and tests. Create our interfaces around storing data and retrieving it. Create an in-memory implementation of our interfaces, for testing and rapid dev purposes. Begin sketching out what our unit tests look like. Create our Google Cloud Storage datastore implementation. Sketch out an idea for a usage collection process to keep track of which users are actually using stuff.

History
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/storage.go	Wed Aug 27 22:34:02 2014 -0400
     1.3 @@ -0,0 +1,142 @@
     1.4 +package api
     1.5 +
     1.6 +import (
     1.7 +	"errors"
     1.8 +	"io"
     1.9 +	"io/ioutil"
    1.10 +	"log"
    1.11 +	"net/http"
    1.12 +
    1.13 +	"code.google.com/p/google-api-go-client/googleapi"
    1.14 +	"code.google.com/p/google-api-go-client/storage/v1beta2"
    1.15 +)
    1.16 +
    1.17 +var (
    1.18 +	BucketNotFoundError = errors.New("bucket not found")
    1.19 +	BlobNotFoundError   = errors.New("blob not found")
    1.20 +)
    1.21 +
    1.22 +type Storage interface {
    1.23 +	Upload(bucket, tmp string, r io.Reader, errs chan error, done chan struct{})
    1.24 +	Delete(bucket, tmp string) error
    1.25 +	Move(srcBucket, src, dstBucket, dst string) error
    1.26 +	Download(bucket, id string, w io.Writer) (int64, error)
    1.27 +}
    1.28 +
    1.29 +type GoogleCloudStorage struct {
    1.30 +	*storage.Service
    1.31 +}
    1.32 +
    1.33 +func (gcs *GoogleCloudStorage) Upload(bucket, tmp string, r io.Reader, errs chan error, done chan struct{}) {
    1.34 +	if errs != nil {
    1.35 +		defer close(errs)
    1.36 +	}
    1.37 +	if done != nil {
    1.38 +		defer close(done)
    1.39 +	}
    1.40 +	object := &storage.Object{Name: tmp}
    1.41 +	_, err := gcs.Objects.Insert(bucket, object).Media(r).Do()
    1.42 +	if err != nil && errs != nil {
    1.43 +		errs <- err
    1.44 +	}
    1.45 +}
    1.46 +
    1.47 +func (gcs *GoogleCloudStorage) Delete(bucket, tmp string) error {
    1.48 +	return gcs.Objects.Delete(bucket, tmp).Do()
    1.49 +}
    1.50 +
    1.51 +func (gcs *GoogleCloudStorage) Move(srcBucket, src, dstBucket, dst string) error {
    1.52 +	_, err := gcs.Objects.Get(dstBucket, dst).Do()
    1.53 +	if err == nil {
    1.54 +		go gcs.del(srcBucket, src)
    1.55 +		return nil
    1.56 +	}
    1.57 +	if e, ok := err.(*googleapi.Error); !ok || e.Code != 404 {
    1.58 +		return err
    1.59 +	}
    1.60 +	_, err = gcs.Objects.Copy(srcBucket, src, dstBucket, dst, nil).Do()
    1.61 +	if err != nil {
    1.62 +		return err
    1.63 +	}
    1.64 +	objectAcl := &storage.ObjectAccessControl{
    1.65 +		Bucket: dstBucket, Entity: "allUsers", Object: dst, Role: "READER",
    1.66 +	}
    1.67 +	_, err = gcs.ObjectAccessControls.Insert(dstBucket, dst, objectAcl).Do()
    1.68 +	if err != nil {
    1.69 +		return err
    1.70 +	}
    1.71 +	go gcs.del(srcBucket, src)
    1.72 +	return nil
    1.73 +}
    1.74 +
    1.75 +func (gcs *GoogleCloudStorage) del(bucket, tmp string) {
    1.76 +	err := gcs.Delete(bucket, tmp)
    1.77 +	if err != nil {
    1.78 +		log.Printf("Error deleting temporary upload %s in %s: %s\n", tmp, bucket, err)
    1.79 +	}
    1.80 +}
    1.81 +
    1.82 +func (gcs *GoogleCloudStorage) Download(bucket, id string, w io.Writer) (int64, error) {
    1.83 +	res, err := gcs.Objects.Get(bucket, id).Do()
    1.84 +	if err != nil {
    1.85 +		return 0, err
    1.86 +	}
    1.87 +	resp, err := http.Get(res.MediaLink)
    1.88 +	if err != nil {
    1.89 +		return 0, err
    1.90 +	}
    1.91 +	defer resp.Body.Close()
    1.92 +	return io.Copy(w, resp.Body)
    1.93 +}
    1.94 +
    1.95 +type Memstorage map[string]Bucket
    1.96 +
    1.97 +type Bucket map[string][]byte
    1.98 +
    1.99 +func (m Memstorage) Upload(bucket, tmp string, r io.Reader, errs chan error, done chan struct{}) {
   1.100 +	if errs != nil {
   1.101 +		defer close(errs)
   1.102 +	}
   1.103 +	if done != nil {
   1.104 +		defer close(done)
   1.105 +	}
   1.106 +	if _, ok := m[bucket]; !ok {
   1.107 +		m[bucket] = make(Bucket)
   1.108 +	}
   1.109 +	bytes, err := ioutil.ReadAll(r)
   1.110 +	if err != nil {
   1.111 +		errs <- err
   1.112 +		return
   1.113 +	}
   1.114 +	m[bucket][tmp] = bytes
   1.115 +}
   1.116 +
   1.117 +func (m Memstorage) Delete(bucket, tmp string) error {
   1.118 +	delete(m[bucket], tmp)
   1.119 +	return nil
   1.120 +}
   1.121 +
   1.122 +func (m Memstorage) Move(srcBucket, src, dstBucket, dst string) error {
   1.123 +	if _, ok := m[srcBucket]; !ok {
   1.124 +		return BucketNotFoundError
   1.125 +	}
   1.126 +	if _, ok := m[srcBucket][src]; !ok {
   1.127 +		return BlobNotFoundError
   1.128 +	}
   1.129 +	if _, ok := m[dstBucket]; !ok {
   1.130 +		m[dstBucket] = make(Bucket)
   1.131 +	}
   1.132 +	m[dstBucket][dst] = m[srcBucket][src]
   1.133 +	return m.Delete(srcBucket, src)
   1.134 +}
   1.135 +
   1.136 +func (m Memstorage) Download(bucket, id string, w io.Writer) (int64, error) {
   1.137 +	if _, ok := m[bucket]; !ok {
   1.138 +		return 0, BucketNotFoundError
   1.139 +	}
   1.140 +	if _, ok := m[bucket][id]; !ok {
   1.141 +		return 0, BlobNotFoundError
   1.142 +	}
   1.143 +	n, err := w.Write(m[bucket][id])
   1.144 +	return int64(n), err
   1.145 +}