diff --git a/collection.go b/collection.go index 273c37e..c52f96e 100644 --- a/collection.go +++ b/collection.go @@ -1,162 +1,216 @@ package writeas import ( "fmt" "net/http" ) type ( // Collection represents a collection of posts. Blogs are a type of collection // on Write.as. Collection struct { Alias string `json:"alias"` Title string `json:"title"` Description string `json:"description"` StyleSheet string `json:"style_sheet"` Private bool `json:"private"` Views int64 `json:"views"` Domain string `json:"domain,omitempty"` Email string `json:"email,omitempty"` URL string `json:"url,omitempty"` TotalPosts int `json:"total_posts"` Posts *[]Post `json:"posts,omitempty"` } // CollectionParams holds values for creating a collection. CollectionParams struct { Alias string `json:"alias"` Title string `json:"title"` Description string `json:"description,omitempty"` } + + // 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 // if one comes up. Requires a Write.as subscription. See // https://developer.write.as/docs/api/#create-a-collection func (c *Client) CreateCollection(sp *CollectionParams) (*Collection, error) { p := &Collection{} env, err := c.post("/collections", sp, p) if err != nil { return nil, err } var ok bool if p, ok = env.Data.(*Collection); !ok { return nil, fmt.Errorf("Wrong data returned from API.") } status := env.Code if status != http.StatusCreated { if status == http.StatusBadRequest { return nil, fmt.Errorf("Bad request: %s", env.ErrorMessage) } else if status == http.StatusForbidden { return nil, fmt.Errorf("Casual or Pro user required.") } else if status == http.StatusConflict { return nil, fmt.Errorf("Collection name is already taken.") } else if status == http.StatusPreconditionFailed { return nil, fmt.Errorf("Reached max collection quota.") } return nil, fmt.Errorf("Problem getting post: %d. %v\n", status, err) } return p, nil } // GetCollection retrieves a collection, returning the Collection and any error // (in user-friendly form) that occurs. See // https://developer.write.as/docs/api/#retrieve-a-collection func (c *Client) GetCollection(alias string) (*Collection, error) { coll := &Collection{} env, err := c.get(fmt.Sprintf("/collections/%s", alias), coll) if err != nil { return nil, err } var ok bool if coll, ok = env.Data.(*Collection); !ok { return nil, fmt.Errorf("Wrong data returned from API.") } status := env.Code if status == http.StatusOK { return coll, nil } else if status == http.StatusNotFound { return nil, fmt.Errorf("Collection not found.") } else { return nil, fmt.Errorf("Problem getting collection: %d. %v\n", status, err) } } // GetCollectionPosts retrieves a collection's posts, returning the Posts // and any error (in user-friendly form) that occurs. See // https://developer.write.as/docs/api/#retrieve-collection-posts func (c *Client) GetCollectionPosts(alias string) (*[]Post, error) { coll := &Collection{} env, err := c.get(fmt.Sprintf("/collections/%s/posts", alias), coll) if err != nil { return nil, err } var ok bool if coll, ok = env.Data.(*Collection); !ok { return nil, fmt.Errorf("Wrong data returned from API.") } status := env.Code if status == http.StatusOK { return coll.Posts, nil } else if status == http.StatusNotFound { return nil, fmt.Errorf("Collection not found.") } else { return nil, fmt.Errorf("Problem getting collection: %d. %v\n", status, err) } } // GetUserCollections retrieves the authenticated user's collections. // See https://developers.write.as/docs/api/#retrieve-user-39-s-collections func (c *Client) GetUserCollections() (*[]Collection, error) { colls := &[]Collection{} env, err := c.get("/me/collections", colls) if err != nil { return nil, err } var ok bool if colls, ok = env.Data.(*[]Collection); !ok { return nil, fmt.Errorf("Wrong data returned from API.") } status := env.Code if status != http.StatusOK { if c.isNotLoggedIn(status) { return nil, fmt.Errorf("Not authenticated.") } return nil, fmt.Errorf("Problem getting collections: %d. %v\n", status, err) } return colls, nil } // DeleteCollection permanently deletes a collection and makes any posts on it // anonymous. // // See https://developers.write.as/docs/api/#delete-a-collection. func (c *Client) DeleteCollection(alias string) error { endpoint := "/collections/" + alias env, err := c.delete(endpoint, nil /* data */) if err != nil { return err } status := env.Code switch status { case http.StatusNoContent: return nil case http.StatusUnauthorized: return fmt.Errorf("Not authenticated.") case http.StatusBadRequest: return fmt.Errorf("Bad request: %s", env.ErrorMessage) default: 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) + } +} diff --git a/collection_test.go b/collection_test.go index 93e82c7..9055852 100644 --- a/collection_test.go +++ b/collection_test.go @@ -1,103 +1,160 @@ package writeas import ( "fmt" + "net/http" "strings" "testing" "time" ) func TestGetCollection(t *testing.T) { wac := NewClient() res, err := wac.GetCollection("blog") if err != nil { t.Errorf("Unexpected fetch results: %+v, err: %v\n", res, err) } else { t.Logf("Collection: %+v", res) if res.Title != "write.as" { t.Errorf("Unexpected fetch results: %+v\n", res) } } } func TestGetCollectionPosts(t *testing.T) { wac := NewClient() res, err := wac.GetCollectionPosts("blog") if err != nil { t.Errorf("Unexpected fetch results: %+v, err: %v\n", res, err) } else { if len(*res) == 0 { t.Errorf("No posts returned!") } } } func TestGetUserCollections(t *testing.T) { wac := NewDevClient() _, err := wac.LogIn("demo", "demo") if err != nil { t.Fatalf("Unable to log in: %v", err) } defer wac.LogOut() res, err := wac.GetUserCollections() if err != nil { t.Errorf("Unexpected fetch results: %+v, err: %v\n", res, err) } else { t.Logf("User collections: %+v", res) if len(*res) == 0 { t.Errorf("No collections returned!") } } } func TestCreateAndDeleteCollection(t *testing.T) { wac := NewDevClient() _, err := wac.LogIn("demo", "demo") if err != nil { t.Fatalf("Unable to log in: %v", err) } defer wac.LogOut() now := time.Now().Unix() alias := fmt.Sprintf("test-collection-%v", now) c, 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) } if err := wac.DeleteCollection(c.Alias); err != nil { t.Fatalf("Unable to delete collection %q: %v", alias, err) } } func TestDeleteCollectionUnauthenticated(t *testing.T) { wac := NewDevClient() now := time.Now().Unix() alias := fmt.Sprintf("test-collection-does-not-exist-%v", now) err := wac.DeleteCollection(alias) if err == nil { t.Fatalf("Should not be able to delete collection %q unauthenticated.", alias) } if !strings.Contains(err.Error(), "Not authenticated") { t.Fatalf("Error message should be more informative: %v", err) } } func ExampleClient_GetCollection() { c := NewClient() coll, err := c.GetCollection("blog") if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%s", coll.Title) // 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) + } + } +}