Skip to content

Commit

Permalink
DataFile implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
algebraic-brain committed Oct 25, 2016
1 parent 7bd2071 commit d921014
Show file tree
Hide file tree
Showing 9 changed files with 490 additions and 7 deletions.
10 changes: 5 additions & 5 deletions algorithm.go
Expand Up @@ -20,7 +20,7 @@ type algoClient interface {
}

type Algorithm struct {
Client algoClient
client algoClient
Path string
Url string
QueryParameters url.Values
Expand All @@ -46,7 +46,7 @@ func NewAlgorithm(client algoClient, ref string) (*Algorithm, error) {
}

return &Algorithm{
Client: client,
client: client,
Path: path,
Url: "/v1/algo/" + path,
QueryParameters: url.Values{},
Expand All @@ -62,7 +62,7 @@ func (algo *Algorithm) SetOptions(opt AlgoOptions) {

func (algo *Algorithm) postRawOutput(input1 interface{}) ([]byte, error) {
algo.QueryParameters.Add("output", "raw")
resp, err := algo.Client.postJsonHelper(algo.Url, input1, algo.QueryParameters)
resp, err := algo.client.postJsonHelper(algo.Url, input1, algo.QueryParameters)
if err != nil {
return nil, err
}
Expand All @@ -72,7 +72,7 @@ func (algo *Algorithm) postRawOutput(input1 interface{}) ([]byte, error) {

func (algo *Algorithm) postVoidOutput(input1 interface{}) (*AsyncResponse, error) {
algo.QueryParameters.Add("output", "void")
resp, err := algo.Client.postJsonHelper(algo.Url, input1, algo.QueryParameters)
resp, err := algo.client.postJsonHelper(algo.Url, input1, algo.QueryParameters)
if err != nil {
return nil, err
}
Expand All @@ -87,7 +87,7 @@ func (algo *Algorithm) Pipe(input1 interface{}) (interface{}, error) {
case Void:
return algo.postVoidOutput(input1)
default:
resp, err := algo.Client.postJsonHelper(algo.Url, input1, algo.QueryParameters)
resp, err := algo.client.postJsonHelper(algo.Url, input1, algo.QueryParameters)
if err != nil {
return nil, err
}
Expand Down
40 changes: 40 additions & 0 deletions client.go
Expand Up @@ -26,6 +26,10 @@ func (c *Client) Algo(ref string) (*Algorithm, error) {
return NewAlgorithm(c, ref)
}

func (c *Client) File(dataUrl string) *DataFile {
return NewDataFile(c, dataUrl)
}

func (c *Client) postJsonHelper(url string, input interface{}, params url.Values) (*http.Response, error) {
headers := http.Header{}
if c.ApiKey != "" {
Expand Down Expand Up @@ -61,3 +65,39 @@ func (c *Client) postJsonHelper(url string, input interface{}, params url.Values

return Request{Url: c.ApiAddress + url, Data: inputJson, Headers: headers, Params: params}.Post()
}

func (c *Client) getHelper(url string, params url.Values) (*http.Response, error) {
headers := http.Header{}
if c.ApiKey != "" {
headers.Add("Authorization", c.ApiKey)
}

return Request{Url: c.ApiAddress + url, Headers: headers, Params: params}.Get()
}

func (c *Client) headHelper(url string) (*http.Response, error) {
headers := http.Header{}
if c.ApiKey != "" {
headers.Add("Authorization", c.ApiKey)
}

return Request{Url: c.ApiAddress + url, Headers: headers}.Head()
}

func (c *Client) putHelper(url string, data []byte) (*http.Response, error) {
headers := http.Header{}
if c.ApiKey != "" {
headers.Add("Authorization", c.ApiKey)
}

return Request{Url: c.ApiAddress + url, Headers: headers, Data: data}.Put()
}

func (c *Client) deleteHelper(url string) (*http.Response, error) {
headers := http.Header{}
if c.ApiKey != "" {
headers.Add("Authorization", c.ApiKey)
}

return Request{Url: c.ApiAddress + url, Headers: headers}.Delete()
}
16 changes: 16 additions & 0 deletions data.go
@@ -0,0 +1,16 @@
package algorithmia

type DataObject int

const (
File DataObject = iota
Directory
)

func (obj DataObject) IsFile() bool {
return obj == File
}

func (obj DataObject) IsDir() bool {
return obj == Directory
}
225 changes: 225 additions & 0 deletions datafile.go
@@ -0,0 +1,225 @@
package algorithmia

import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
)

type datafileClient interface {
getHelper(url string, params url.Values) (*http.Response, error)
headHelper(url string) (*http.Response, error)
putHelper(url string, data []byte) (*http.Response, error)
deleteHelper(url string) (*http.Response, error)
}

type FileAttributes struct {
FileName string `json:"filename"`
LastModified string `json:"last_modified"`
Size int64 `json:"size"`
}

type DataFile struct {
client datafileClient

Path string
Url string
LastModified time.Time
Size int64
}

func NewDataFile(client datafileClient, dataUrl string) *DataFile {
p := strings.TrimSpace(dataUrl)
if strings.HasPrefix(p, "data://") {
p = p[len("data://"):]
} else if strings.HasPrefix(p, "/") {
p = p[1:]
}
return &DataFile{
client: client,
Path: p,
Url: "/v1/data/" + p,
}
}

func (f *DataFile) SetAttributes(attr *FileAttributes) error {
//%Y-%m-%dT%H:%M:%S.000Z
t, err := time.Parse("2006-01-02T15:04:05.000Z", attr.LastModified)
if err != nil {
return err
}
f.LastModified = t
f.Size = attr.Size
return nil
}

/*
def getFile(self):
if not self.exists():
raise Exception('file does not exist - {}'.format(self.path))
# Make HTTP get request
response = self.client.getHelper(self.url)
with tempfile.NamedTemporaryFile(delete = False) as f:
for block in response.iter_content(1024):
if not block:
break;
f.write(block)
f.flush()
return open(f.name)
*/
func (f *DataFile) File() (*os.File, error) {
if exists, err := f.Exists(); err != nil {
return nil, err
} else if !exists {
return nil, errors.New(fmt.Sprint("file does not exist -", f.Path))
}

resp, err := f.client.getHelper(f.Url, url.Values{})
if err != nil {
return nil, err
}

rf, err := ioutil.TempFile(os.TempDir(), "algorithmia")
if err != nil {
return nil, err
}
_, err = io.Copy(rf, resp.Body)
if err != nil {
rf.Close()
return nil, err
}
return rf, nil
}

/*
def exists(self):
response = self.client.headHelper(self.url)
return (response.status_code == 200)
*/
func (f *DataFile) Exists() (bool, error) {
resp, err := f.client.headHelper(f.Url)
return resp.StatusCode == http.StatusOK, err
}

func (f *DataFile) Name() (string, error) {
_, name, err := getParentAndBase(f.Path)
return name, err
}

func (f *DataFile) Bytes() ([]byte, error) {
if exists, err := f.Exists(); err != nil {
return nil, err
} else if !exists {
return nil, errors.New(fmt.Sprint("file does not exist -", f.Path))
}

resp, err := f.client.getHelper(f.Url, url.Values{})
if err != nil {
return nil, err
}

return ioutil.ReadAll(resp.Body)
}

func (f *DataFile) StringContents() (string, error) {
if b, err := f.Bytes(); err != nil {
return "", err
} else {
return string(b), nil
}
}

func (f *DataFile) Json(x interface{}) error {
if exists, err := f.Exists(); err != nil {
return err
} else if !exists {
return errors.New(fmt.Sprint("file does not exist -", f.Path))
}

resp, err := f.client.getHelper(f.Url, url.Values{})
if err != nil {
return err
}

return getJson(resp, x)
}

/*
def put(self, data):
# Post to data api
# First turn the data to bytes if we can
if isinstance(data, six.string_types) and not isinstance(data, six.binary_type):
data = bytes(data.encode())
if isinstance(data, six.binary_type):
result = self.client.putHelper(self.url, data)
if 'error' in result:
raise Exception(result['error']['message'])
else:
return self
else:
raise Exception("Must put strings or binary data. Use putJson instead")
*/

func (f *DataFile) Put(data []byte) error {
resp, err := f.client.putHelper(f.Url, data)
if err != nil {
return err
}

b, err := getRaw(resp)
if err != nil {
return err
}

err = ErrorFromJsonData(b)
if err != nil {
return err
}

return nil
}

func (f *DataFile) PutJson(data interface{}) error {
b, err := json.Marshal(data)
if err != nil {
return err
}
return f.Put(b)
}

func (f *DataFile) PutFile(fpath string) error {
b, err := ioutil.ReadFile(fpath)
if err != nil {
return err
}
return f.Put(b)
}

func (f *DataFile) Delete() error {
resp, err := f.client.deleteHelper(f.Url)
if err != nil {
return err
}

b, err := getRaw(resp)
if err != nil {
return err
}

err = ErrorFromJsonData(b)
if err != nil {
return err
}

return nil
}
45 changes: 45 additions & 0 deletions datafile_test.go
@@ -0,0 +1,45 @@
package algorithmia

/*
IMPORTANT! For testing DataFile data interface use "test/datafile_test.go"
instead of this.
*/

import (
"testing"
)

func TestDataFileSetAttributes(t *testing.T) {
f := NewDataFile(NewClient("", ""), "data://a/b.txt")

err := f.SetAttributes(&FileAttributes{
LastModified: "2016-01-06T00:52:34.000Z",
Size: 1,
})

if err != nil {
t.Fatal(err)
}

ft := f.LastModified.Format("2006-01-02T15:04:05.000Z")
if ft != "2016-01-06T00:52:34.000Z" {
t.Fatal("got", ft, "after Format")
}
}

func TestReadingErrorMessage(t *testing.T) {
msg1 := []byte(`{"error":{"message":"everything is lost"}}`)

e1, e2 := ErrFromJsonData(msg1)
if e2 != nil {
t.Fatal(e2)
}

if e1 == nil {
t.Fatal("non-nil *Err expected")
}

if e1.Error() != "everything is lost" {
t.Fatal("wrong error message")
}
}

0 comments on commit d921014

Please sign in to comment.