This adds support for moving posts to a collection. This change relies on #13 so that we can create and delete collections during tests. Couple notes: - CollectPosts accepts a single struct rather than an alias and a list of structs. This makes it easy to add new optional parameters in the future. - We're passing `[]*CollectPost` around rather than `*[]CollectPost` which is done in some of the existing APIs. The reasoning for this is: - Idiomatically, slices are passed by-value (`[]foo`) rather than by-pointer (`*[]foo`) because slices are already reference types. Plus they're really cheap to copy since they're just a triple: pointer, length, and capacity ([related blog post][1]). We need pointers to slices only when we're trying to modify the original slice reference and don't have the ability to return a slice, like with `json.Unmarshal` (see also [this post][2]). - If we're trying to reduce copying, `[]*foo` is better than `[]foo` because otherwise `foo` will be copied when manipulating or accessing entries in the slice (like ranging over it). Existing APIs were left as-is to avoid breaking them. I can switch to `*[]CollectPost` if you'd prefer that but `[]*CollectPost` is more idiomatic. [1]: https://blog.golang.org/go-slices-usage-and-internals#TOC_4. [2]: https://blog.golang.org/slices#TOC_5. Resolves #4.pull/14/head
@@ -35,6 +35,35 @@ type ( | |||||
DeleteCollectionParams struct { | DeleteCollectionParams struct { | ||||
Alias string `json:"-"` | Alias string `json:"-"` | ||||
} | } | ||||
// CollectPostParams holds the parameters for moving posts to a collection. | |||||
CollectPostParams struct { | |||||
// Alias of the collection. | |||||
Alias string `json:"-"` | |||||
// Posts to move to this collection. | |||||
Posts []*CollectPost | |||||
} | |||||
// CollectPost is a post being moved to a collection. | |||||
CollectPost struct { | |||||
// ID of the post. | |||||
ID string `json:"id,omitempty"` | |||||
// The post's modify token. | |||||
// | |||||
// This is required if the post does not belong to the user making | |||||
// this request. | |||||
Token string `json:"token,omitempty"` | |||||
} | |||||
// CollectPostResult holds the result of moving a single post to a | |||||
// collection. | |||||
CollectPostResult struct { | |||||
Code int `json:"code,omitempty"` | |||||
ErrorMessage string `json:"error_msg,omitempty"` | |||||
Post *Post `json:"post,omitempty"` | |||||
} | |||||
) | ) | ||||
// CreateCollection creates a new collection, returning a user-friendly error | // CreateCollection creates a new collection, returning a user-friendly error | ||||
@@ -165,3 +194,28 @@ func (c *Client) DeleteCollection(p *DeleteCollectionParams) error { | |||||
return fmt.Errorf("Problem deleting collection: %d. %s\n", status, env.ErrorMessage) | return fmt.Errorf("Problem deleting collection: %d. %s\n", status, env.ErrorMessage) | ||||
} | } | ||||
} | } | ||||
// CollectPosts adds a group of posts to a collection. | |||||
// | |||||
// See https://developers.write.as/docs/api/#move-a-post-to-a-collection. | |||||
func (c *Client) CollectPosts(sp *CollectPostParams) ([]*CollectPostResult, error) { | |||||
endpoint := "/collections/" + sp.Alias + "/collect" | |||||
var p []*CollectPostResult | |||||
env, err := c.post(endpoint, sp.Posts, &p) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
status := env.Code | |||||
switch { | |||||
case status == http.StatusOK: | |||||
return p, nil | |||||
case c.isNotLoggedIn(status): | |||||
return nil, fmt.Errorf("Not authenticated.") | |||||
case status == http.StatusBadRequest: | |||||
return nil, fmt.Errorf("Bad request: %s", env.ErrorMessage) | |||||
default: | |||||
return nil, fmt.Errorf("Problem claiming post: %d. %s\n", status, env.ErrorMessage) | |||||
} | |||||
} |
@@ -2,6 +2,7 @@ package writeas | |||||
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"net/http" | |||||
"strings" | "strings" | ||||
"testing" | "testing" | ||||
"time" | "time" | ||||
@@ -103,3 +104,59 @@ func ExampleClient_GetCollection() { | |||||
fmt.Printf("%s", coll.Title) | fmt.Printf("%s", coll.Title) | ||||
// Output: write.as | // Output: write.as | ||||
} | } | ||||
func TestCollectPostsAnonymous(t *testing.T) { | |||||
// Create a post anonymously. | |||||
wac := NewDevClient() | |||||
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) | |||||
// Log in. | |||||
if _, err := wac.LogIn("demo", "demo"); err != nil { | |||||
t.Fatalf("Unable to log in: %v", err) | |||||
} | |||||
defer wac.LogOut() | |||||
now := time.Now().Unix() | |||||
alias := fmt.Sprintf("test-collection-%v", now) | |||||
// Create a collection. | |||||
_, err = wac.CreateCollection(&CollectionParams{ | |||||
Alias: alias, | |||||
Title: fmt.Sprintf("Test Collection %v", now), | |||||
}) | |||||
if err != nil { | |||||
t.Fatalf("Unable to create collection %q: %v", alias, err) | |||||
} | |||||
defer wac.DeleteCollection(&DeleteCollectionParams{Alias: alias}) | |||||
// Move the anonymous post to this collection. | |||||
res, err := wac.CollectPosts(&CollectPostParams{ | |||||
Alias: alias, | |||||
Posts: []*CollectPost{ | |||||
{ | |||||
ID: p.ID, | |||||
Token: p.Token, | |||||
}, | |||||
}, | |||||
}) | |||||
if err != nil { | |||||
t.Fatalf("Could not collect post %q: %v", p.ID, err) | |||||
} | |||||
for _, cr := range res { | |||||
if cr.Code != http.StatusOK { | |||||
t.Errorf("Failed to move post: %v", cr.ErrorMessage) | |||||
} else { | |||||
t.Logf("Moved post %q", cr.Post.ID) | |||||
} | |||||
} | |||||
} |