diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc35ec8 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# go-snapas + +[![godoc](https://godoc.org/github.com/snapas/go-snapas?status.svg)](https://godoc.org/github.com/snapas/go-snapas) + +Official Snap.as Go client library. + +## Installation + +```bash +go get github.com/snapas/go-snapas +``` + +## Documentation + +See all functionality and usages in the [API documentation](https://developers.snap.as/docs/api/). + +### Example usage + +```go +import ( + "github.com/snapas/go-snapas" + "github.com/writeas/go-writeas/v2" + "log" +) + +func main() { + // Authenticate with Write.as + wc := writeas.NewClient() + u, err := wc.LogIn("demo", "demo") + if err != nil { + log.Fatal(err) + } + + // Upload to Snap.as + sc := snapas.NewClient(u.AccessToken) + p, err := sc.UploadPhoto(&snapas.PhotoParams{ + FileName: "image.jpg", + }) + if err != nil { + wc.LogOut() + log.Fatal(err) + } + + // Output final domain + log.Println(p.URL) + + // Clean up + wc.LogOut() +} +``` + +## License + +MIT diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fdf434e --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/snapas/go-snapas + +go 1.13 + +require ( + code.as/core/api v0.0.0-20180910161400-1dd2503197ed + github.com/writeas/impart v1.1.1 +) diff --git a/photo.go b/photo.go index 656248b..8260df0 100644 --- a/photo.go +++ b/photo.go @@ -1,14 +1,87 @@ package snapas -import "time" - -// Photo represents a photo on Snap.as. -type Photo struct { - ID string `json:"id"` - Created time.Time `json:"created"` - Body *string `json:"body"` - Filename string `json:"filename"` - Size int64 `json:"size"` - URL string `json:"url"` - Album *Album `json:"album"` +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/writeas/impart" + "io" + "mime/multipart" + "net/http" + "os" + "path/filepath" + "time" +) + +type ( + // Photo represents a photo on Snap.as. + Photo struct { + ID string `json:"id"` + Created time.Time `json:"created"` + Body *string `json:"body"` + Filename string `json:"filename"` + Size int64 `json:"size"` + URL string `json:"url"` + Album *Album `json:"album"` + } + + // PhotoParams holds valid values for uploading photos. + PhotoParams struct { + FileName string + } +) + +// UploadPhoto uploads a photo, and returns a Snap.as Photo. See: +// https://developers.snap.as/docs/api/#upload-a-photo +func (c *Client) UploadPhoto(sp *PhotoParams) (*Photo, error) { + f, err := os.Open(sp.FileName) + if err != nil { + return nil, fmt.Errorf("open file: %s", err) + } + defer f.Close() + + body := &bytes.Buffer{} + w := multipart.NewWriter(body) + part, err := w.CreateFormFile("file", filepath.Base(f.Name())) + if err != nil { + return nil, fmt.Errorf("create form file: %s", err) + } + + _, err = io.Copy(part, f) + if err != nil { + return nil, fmt.Errorf("copy file: %s", err) + } + err = w.Close() + if err != nil { + return nil, fmt.Errorf("close writer: %s", err) + } + + url := fmt.Sprintf("%s%s", c.Config.BaseURL, "/photos/upload") + req, err := http.NewRequest("POST", url, body) + if err != nil { + return nil, fmt.Errorf("create request: %s", err) + } + req.Header.Add("User-Agent", c.Config.UserAgent) + req.Header.Add("Content-Type", w.FormDataContentType()) + req.Header.Add("Authorization", c.Token) + + resp, err := c.Config.Client.Do(req) + if err != nil { + return nil, fmt.Errorf("request: %s", err) + } + defer resp.Body.Close() + + env := &impart.Envelope{ + Code: resp.StatusCode, + Data: &Photo{}, + } + err = json.NewDecoder(resp.Body).Decode(&env) + if err != nil { + return nil, err + } + if env.Code != http.StatusCreated { + return nil, fmt.Errorf("%s", env.ErrorMessage) + } + + return env.Data.(*Photo), nil } diff --git a/snapas.go b/snapas.go index 928ff08..8f02264 100644 --- a/snapas.go +++ b/snapas.go @@ -1,13 +1,44 @@ package snapas import ( - "code.as/core/api" + as "code.as/core/api" ) const ( - apiURL = "https://snap.as/api" + apiURL = "https://snap.as/api" + devAPIURL = "https://dev.snap.as/api" ) -func NewClient() *as.ClientConfig { - return as.NewClientConfig(apiURL, "go-snapas v1") +// Client is used to interact with the Snap.as API. +type Client struct { + as.Client +} + +// NewClient creates a new API client. All requests must be authenticated, so +// you should supply a user access token returned from the Write.as API +// library (github.com/writeas/go-writeas/v2) +// +// wc := writeas.NewClient() +// u, err := wc.LogIn("username", "password") +// if err != nil { +// // Handle error... +// } +// sc := snapas.NewClient(u.AccessToken) +func NewClient(token string) *Client { + cfg := as.NewClientConfig(apiURL, "go-snapas v1") + return NewClientWith(cfg, token) +} + +// NewDevClient creates a new API client for development and testing. It will +// communicate with our development servers, and SHOULD NOT be used in +// production. +func NewDevClient(token string) *Client { + return NewClientWith(as.NewClientConfig(devAPIURL, "go-snapas v1"), token) +} + +// NewClientWith builds a new API client with the provided configuration. +func NewClientWith(cfg *as.ClientConfig, token string) *Client { + cl := as.NewClient(cfg) + cl.Token = token + return &Client{*cl} }