diff --git a/README.md b/README.md index 17cc920..5288001 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,71 @@ # go-writeas [![godoc](https://godoc.org/go.code.as/writeas.v2?status.svg)](https://godoc.org/go.code.as/writeas.v2) Official Write.as Go client library. ## Installation **Warning**: the `v2` branch is under heavy development and its API will change without notice. For a stable API, use `go.code.as/writeas.v1` and upgrade to `v2` once everything is merged into `master`. ```bash go get go.code.as/writeas.v2 ``` ## Documentation See all functionality and usages in the [API documentation](https://developer.write.as/docs/api/). ### Example usage ```go import "go.code.as/writeas.v2" func main() { // Create the client c := writeas.NewClient() // Publish a post p, err := c.CreatePost(&writeas.PostParams{ Title: "Title!", Content: "This is a post.", Font: "sans", }) if err != nil { // Perhaps show err.Error() } // Save token for later, since it won't ever be returned again token := p.Token // Update a published post p, err = c.UpdatePost(p.ID, token, &writeas.PostParams{ Content: "Now it's been updated!", }) if err != nil { // handle } // Get a published post p, err = c.GetPost(p.ID) if err != nil { // handle } // Delete a post err = c.DeletePost(p.ID, token) } ``` ## Contributing The library covers our usage, but might not be comprehensive of the API. So we always welcome contributions and improvements from the community. Before sending pull requests, make sure you've done the following: -* Run `go fmt` on all updated .go files. +* Run `goimports` on all updated .go files. * Document all exported structs and funcs. ## License MIT diff --git a/auth_test.go b/auth_test.go index 3c78c7e..a9ece6f 100644 --- a/auth_test.go +++ b/auth_test.go @@ -1,21 +1,19 @@ package writeas -import ( - "testing" -) +import "testing" func TestAuthentication(t *testing.T) { dwac := NewDevClient() // Log in _, err := dwac.LogIn("demo", "demo") if err != nil { t.Fatalf("Unable to log in: %v", err) } // Log out err = dwac.LogOut() if err != nil { t.Fatalf("Unable to log out: %v", err) } } diff --git a/post_test.go b/post_test.go index f995f2b..a7e3475 100644 --- a/post_test.go +++ b/post_test.go @@ -1,111 +1,110 @@ package writeas import ( - "testing" - "fmt" "strings" + "testing" ) func TestCreatePost(t *testing.T) { wac := NewClient() p, err := wac.CreatePost(&PostParams{ Title: "Title!", Content: "This is a post.", Font: "sans", }) if err != nil { t.Errorf("Post create failed: %v", err) return } t.Logf("Post created: %+v", p) token := p.Token // Update post p, err = wac.UpdatePost(p.ID, token, &PostParams{ Content: "Now it's been updated!", }) if err != nil { t.Errorf("Post update failed: %v", err) return } t.Logf("Post updated: %+v", p) // Delete post err = wac.DeletePost(p.ID, token) if err != nil { t.Errorf("Post delete failed: %v", err) return } t.Logf("Post deleted!") } func TestGetPost(t *testing.T) { dwac := NewDevClient() res, err := dwac.GetPost("zekk5r9apum6p") if err != nil { t.Errorf("Unexpected fetch results: %+v, err: %v\n", res, err) } else { t.Logf("Post: %+v", res) if res.Content != "This is a post." { t.Errorf("Unexpected fetch results: %+v\n", res) } } wac := NewClient() res, err = wac.GetPost("3psnxyhqxy3hq") if err != nil { t.Errorf("Unexpected fetch results: %+v, err: %v\n", res, err) } else { if !strings.HasPrefix(res.Content, " Write.as Blog") { t.Errorf("Unexpected fetch results: %+v\n", res) } } } func TestPinPost(t *testing.T) { dwac := NewDevClient() _, err := dwac.LogIn("demo", "demo") if err != nil { t.Fatalf("Unable to log in: %v", err) } defer dwac.LogOut() err = dwac.PinPost("tester", &PinnedPostParams{ID: "olx6uk7064heqltf"}) if err != nil { t.Fatalf("Pin failed: %v", err) } } func TestUnpinPost(t *testing.T) { dwac := NewDevClient() _, err := dwac.LogIn("demo", "demo") if err != nil { t.Fatalf("Unable to log in: %v", err) } defer dwac.LogOut() err = dwac.UnpinPost("tester", &PinnedPostParams{ID: "olx6uk7064heqltf"}) if err != nil { t.Fatalf("Unpin failed: %v", err) } } func ExampleClient_CreatePost() { c := NewClient() // Publish a post p, err := c.CreatePost(&PostParams{ Title: "Title!", Content: "This is a post.", Font: "sans", }) if err != nil { fmt.Printf("Unable to create: %v", err) return } fmt.Printf("%s", p.Content) // Output: This is a post. } diff --git a/user.go b/user.go index e10f3c8..5973d9c 100644 --- a/user.go +++ b/user.go @@ -1,36 +1,34 @@ package writeas -import ( - "time" -) +import "time" type ( // AuthUser represents a just-authenticated user. It contains information // that'll only be returned once (now) per user session. AuthUser struct { AccessToken string `json:"access_token,omitempty"` Password string `json:"password,omitempty"` User *User `json:"user"` } // User represents a registered Write.as user. User struct { Username string `json:"username"` Email string `json:"email"` Created time.Time `json:"created"` // Optional properties Subscription *UserSubscription `json:"subscription"` } // UserSubscription contains information about a user's Write.as // subscription. UserSubscription struct { Name string `json:"name"` Begin time.Time `json:"begin"` End time.Time `json:"end"` AutoRenew bool `json:"auto_renew"` Active bool `json:"is_active"` Delinquent bool `json:"is_delinquent"` } ) diff --git a/writeas.go b/writeas.go index f31cfbc..1a646e7 100644 --- a/writeas.go +++ b/writeas.go @@ -1,198 +1,199 @@ // Package writeas provides the binding for the Write.as API package writeas import ( "bytes" - "code.as/core/socks" "encoding/json" "fmt" - "github.com/writeas/impart" "io" "net/http" "time" + + "code.as/core/socks" + "github.com/writeas/impart" ) const ( apiURL = "https://write.as/api" devAPIURL = "https://development.write.as/api" torAPIURL = "http://writeas7pm7rcdqg.onion/api" // Current go-writeas version Version = "2-dev" ) // Client is used to interact with the Write.as API. It can be used to make // authenticated or unauthenticated calls. type Client struct { baseURL string // Access token for the user making requests. token string // Client making requests to the API client *http.Client // UserAgent overrides the default User-Agent header UserAgent string } // defaultHTTPTimeout is the default http.Client timeout. const defaultHTTPTimeout = 10 * time.Second // NewClient creates a new API client. By default, all requests are made // unauthenticated. To optionally make authenticated requests, call `SetToken`. // // c := writeas.NewClient() // c.SetToken("00000000-0000-0000-0000-000000000000") func NewClient() *Client { return NewClientWith(Config{URL: apiURL}) } // NewTorClient creates a new API client for communicating with the Write.as // Tor hidden service, using the given port to connect to the local SOCKS // proxy. func NewTorClient(port int) *Client { return NewClientWith(Config{URL: torAPIURL, TorPort: port}) } // NewDevClient creates a new API client for development and testing. It'll // communicate with our development servers, and SHOULD NOT be used in // production. func NewDevClient() *Client { return NewClientWith(Config{URL: devAPIURL}) } // Config configures a Write.as client. type Config struct { // URL of the Write.as API service. Defaults to https://write.as/api. URL string // If specified, the API client will communicate with the Write.as Tor // hidden service using the provided port to connect to the local SOCKS // proxy. TorPort int // If specified, requests will be authenticated using this user token. // This may be provided after making a few anonymous requests with // SetToken. Token string } // NewClientWith builds a new API client with the provided configuration. func NewClientWith(c Config) *Client { if c.URL == "" { c.URL = apiURL } httpClient := &http.Client{Timeout: defaultHTTPTimeout} if c.TorPort > 0 { dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, fmt.Sprintf("127.0.0.1:%d", c.TorPort)) httpClient.Transport = &http.Transport{Dial: dialSocksProxy} } return &Client{ client: httpClient, baseURL: c.URL, token: c.Token, } } // SetToken sets the user token for all future Client requests. Setting this to // an empty string will change back to unauthenticated requests. func (c *Client) SetToken(token string) { c.token = token } // Token returns the user token currently set to the Client. func (c *Client) Token() string { return c.token } func (c *Client) get(path string, r interface{}) (*impart.Envelope, error) { method := "GET" if method != "GET" && method != "HEAD" { return nil, fmt.Errorf("Method %s not currently supported by library (only HEAD and GET).\n", method) } return c.request(method, path, nil, r) } func (c *Client) post(path string, data, r interface{}) (*impart.Envelope, error) { b := new(bytes.Buffer) json.NewEncoder(b).Encode(data) return c.request("POST", path, b, r) } func (c *Client) put(path string, data, r interface{}) (*impart.Envelope, error) { b := new(bytes.Buffer) json.NewEncoder(b).Encode(data) return c.request("PUT", path, b, r) } func (c *Client) delete(path string, data map[string]string) (*impart.Envelope, error) { r, err := c.buildRequest("DELETE", path, nil) if err != nil { return nil, err } q := r.URL.Query() for k, v := range data { q.Add(k, v) } r.URL.RawQuery = q.Encode() return c.doRequest(r, nil) } func (c *Client) request(method, path string, data io.Reader, result interface{}) (*impart.Envelope, error) { r, err := c.buildRequest(method, path, data) if err != nil { return nil, err } return c.doRequest(r, result) } func (c *Client) buildRequest(method, path string, data io.Reader) (*http.Request, error) { url := fmt.Sprintf("%s%s", c.baseURL, path) r, err := http.NewRequest(method, url, data) if err != nil { return nil, fmt.Errorf("Create request: %v", err) } c.prepareRequest(r) return r, nil } func (c *Client) doRequest(r *http.Request, result interface{}) (*impart.Envelope, error) { resp, err := c.client.Do(r) if err != nil { return nil, fmt.Errorf("Request: %v", err) } defer resp.Body.Close() env := &impart.Envelope{ Code: resp.StatusCode, } if result != nil { env.Data = result err = json.NewDecoder(resp.Body).Decode(&env) if err != nil { return nil, err } } return env, nil } func (c *Client) prepareRequest(r *http.Request) { ua := c.UserAgent if ua == "" { ua = "go-writeas v" + Version } r.Header.Add("User-Agent", ua) r.Header.Add("Content-Type", "application/json") if c.token != "" { r.Header.Add("Authorization", "Token "+c.token) } }