diff options
author | Frédéric Guillot <fred@miniflux.net> | 2018-06-23 16:16:54 -0700 |
---|---|---|
committer | Frédéric Guillot <fred@miniflux.net> | 2018-06-23 16:16:54 -0700 |
commit | 7039df9af1de1aea72e90d4aa9fa6a37d21e1be0 (patch) | |
tree | 99df837fcec01a2972b2da91a34f9044cb016f72 /api | |
parent | cd77ebd7422b0988df9a5c017901a51507e79abd (diff) |
Improve feed and user API updates with optional values
Diffstat (limited to 'api')
-rw-r--r-- | api/feed.go | 15 | ||||
-rw-r--r-- | api/payload.go | 116 | ||||
-rw-r--r-- | api/payload_test.go | 192 | ||||
-rw-r--r-- | api/user.go | 16 |
4 files changed, 317 insertions, 22 deletions
diff --git a/api/feed.go b/api/feed.go index 351457f..b7c46ea 100644 --- a/api/feed.go +++ b/api/feed.go @@ -97,7 +97,7 @@ func (c *Controller) UpdateFeed(w http.ResponseWriter, r *http.Request) { return } - newFeed, err := decodeFeedModificationPayload(r.Body) + feedChanges, err := decodeFeedModificationPayload(r.Body) if err != nil { json.BadRequest(w, err) return @@ -106,11 +106,6 @@ func (c *Controller) UpdateFeed(w http.ResponseWriter, r *http.Request) { ctx := context.New(r) userID := ctx.UserID() - if newFeed.Category != nil && newFeed.Category.ID != 0 && !c.store.CategoryExists(userID, newFeed.Category.ID) { - json.BadRequest(w, errors.New("This category_id doesn't exists or doesn't belongs to this user")) - return - } - originalFeed, err := c.store.FeedByID(userID, feedID) if err != nil { json.NotFound(w, errors.New("Unable to find this feed")) @@ -122,7 +117,13 @@ func (c *Controller) UpdateFeed(w http.ResponseWriter, r *http.Request) { return } - originalFeed.Merge(newFeed) + feedChanges.Update(originalFeed) + + if !c.store.CategoryExists(userID, originalFeed.Category.ID) { + json.BadRequest(w, errors.New("This category_id doesn't exists or doesn't belongs to this user")) + return + } + if err := c.store.UpdateFeed(originalFeed); err != nil { json.ServerError(w, errors.New("Unable to update this feed")) return diff --git a/api/payload.go b/api/payload.go index 45cb826..ed5255a 100644 --- a/api/payload.go +++ b/api/payload.go @@ -37,13 +37,115 @@ type subscriptionDiscovery struct { Password string `json:"password"` } -func decodeUserPayload(r io.ReadCloser) (*model.User, error) { - var user model.User +type feedModification struct { + FeedURL *string `json:"feed_url"` + SiteURL *string `json:"site_url"` + Title *string `json:"title"` + ScraperRules *string `json:"scraper_rules"` + RewriteRules *string `json:"rewrite_rules"` + Crawler *bool `json:"crawler"` + Username *string `json:"username"` + Password *string `json:"password"` + CategoryID *int64 `json:"category_id"` +} + +func (f *feedModification) Update(feed *model.Feed) { + if f.FeedURL != nil && *f.FeedURL != "" { + feed.FeedURL = *f.FeedURL + } + + if f.SiteURL != nil && *f.SiteURL != "" { + feed.SiteURL = *f.SiteURL + } + + if f.Title != nil && *f.Title != "" { + feed.Title = *f.Title + } + + if f.ScraperRules != nil { + feed.ScraperRules = *f.ScraperRules + } + + if f.RewriteRules != nil { + feed.RewriteRules = *f.RewriteRules + } + + if f.Crawler != nil { + feed.Crawler = *f.Crawler + } + + if f.Username != nil { + feed.Username = *f.Username + } + if f.Password != nil { + feed.Password = *f.Password + } + + if f.CategoryID != nil && *f.CategoryID > 0 { + feed.Category.ID = *f.CategoryID + } +} + +type userModification struct { + Username *string `json:"username"` + Password *string `json:"password"` + IsAdmin *bool `json:"is_admin"` + Theme *string `json:"theme"` + Language *string `json:"language"` + Timezone *string `json:"timezone"` + EntryDirection *string `json:"entry_sorting_direction"` +} + +func (u *userModification) Update(user *model.User) { + if u.Username != nil { + user.Username = *u.Username + } + + if u.Password != nil { + user.Password = *u.Password + } + + if u.IsAdmin != nil { + user.IsAdmin = *u.IsAdmin + } + + if u.Theme != nil { + user.Theme = *u.Theme + } + + if u.Language != nil { + user.Language = *u.Language + } + + if u.Timezone != nil { + user.Timezone = *u.Timezone + } + + if u.EntryDirection != nil { + user.EntryDirection = *u.EntryDirection + } +} + +func decodeUserModificationPayload(r io.ReadCloser) (*userModification, error) { + defer r.Close() + + var user userModification decoder := json.NewDecoder(r) + if err := decoder.Decode(&user); err != nil { + return nil, fmt.Errorf("Unable to decode user modification JSON object: %v", err) + } + + return &user, nil +} + +func decodeUserCreationPayload(r io.ReadCloser) (*model.User, error) { defer r.Close() + + var user model.User + decoder := json.NewDecoder(r) if err := decoder.Decode(&user); err != nil { - return nil, fmt.Errorf("Unable to decode user JSON object: %v", err) + return nil, fmt.Errorf("Unable to decode user modification JSON object: %v", err) } return &user, nil @@ -89,13 +191,13 @@ func decodeFeedCreationPayload(r io.ReadCloser) (*feedCreation, error) { return &fc, nil } -func decodeFeedModificationPayload(r io.ReadCloser) (*model.Feed, error) { - var feed model.Feed +func decodeFeedModificationPayload(r io.ReadCloser) (*feedModification, error) { + defer r.Close() + var feed feedModification decoder := json.NewDecoder(r) - defer r.Close() if err := decoder.Decode(&feed); err != nil { - return nil, fmt.Errorf("Unable to decode feed JSON object: %v", err) + return nil, fmt.Errorf("Unable to decode feed modification JSON object: %v", err) } return &feed, nil diff --git a/api/payload_test.go b/api/payload_test.go new file mode 100644 index 0000000..debf897 --- /dev/null +++ b/api/payload_test.go @@ -0,0 +1,192 @@ +// Copyright 2018 Frédéric Guillot. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package api + +import ( + "testing" + + "github.com/miniflux/miniflux/model" +) + +func TestUpdateFeedURL(t *testing.T) { + feedURL := "http://example.com/" + changes := &feedModification{FeedURL: &feedURL} + feed := &model.Feed{FeedURL: "http://example.org/"} + changes.Update(feed) + + if feed.FeedURL != feedURL { + t.Fatalf(`Unexpected value, got %q instead of %q`, feed.FeedURL, feedURL) + } +} + +func TestUpdateFeedURLWithEmptyString(t *testing.T) { + feedURL := "" + changes := &feedModification{FeedURL: &feedURL} + feed := &model.Feed{FeedURL: "http://example.org/"} + changes.Update(feed) + + if feed.FeedURL == feedURL { + t.Fatal(`The FeedURL should not be modified`) + } +} + +func TestUpdateFeedURLWhenNotSet(t *testing.T) { + changes := &feedModification{} + feed := &model.Feed{FeedURL: "http://example.org/"} + changes.Update(feed) + + if feed.FeedURL != "http://example.org/" { + t.Fatal(`The FeedURL should not be modified`) + } +} + +func TestUpdateFeedSiteURL(t *testing.T) { + siteURL := "http://example.com/" + changes := &feedModification{SiteURL: &siteURL} + feed := &model.Feed{SiteURL: "http://example.org/"} + changes.Update(feed) + + if feed.SiteURL != siteURL { + t.Fatalf(`Unexpected value, got %q instead of %q`, feed.SiteURL, siteURL) + } +} + +func TestUpdateFeedSiteURLWithEmptyString(t *testing.T) { + siteURL := "" + changes := &feedModification{FeedURL: &siteURL} + feed := &model.Feed{SiteURL: "http://example.org/"} + changes.Update(feed) + + if feed.SiteURL == siteURL { + t.Fatal(`The FeedURL should not be modified`) + } +} + +func TestUpdateFeedSiteURLWhenNotSet(t *testing.T) { + changes := &feedModification{} + feed := &model.Feed{SiteURL: "http://example.org/"} + changes.Update(feed) + + if feed.SiteURL != "http://example.org/" { + t.Fatal(`The SiteURL should not be modified`) + } +} + +func TestUpdateFeedTitle(t *testing.T) { + title := "Example 2" + changes := &feedModification{Title: &title} + feed := &model.Feed{Title: "Example"} + changes.Update(feed) + + if feed.Title != title { + t.Fatalf(`Unexpected value, got %q instead of %q`, feed.Title, title) + } +} + +func TestUpdateFeedTitleWithEmptyString(t *testing.T) { + title := "" + changes := &feedModification{Title: &title} + feed := &model.Feed{Title: "Example"} + changes.Update(feed) + + if feed.Title == title { + t.Fatal(`The Title should not be modified`) + } +} + +func TestUpdateFeedTitleWhenNotSet(t *testing.T) { + changes := &feedModification{} + feed := &model.Feed{Title: "Example"} + changes.Update(feed) + + if feed.Title != "Example" { + t.Fatal(`The Title should not be modified`) + } +} + +func TestUpdateFeedUsername(t *testing.T) { + username := "Alice" + changes := &feedModification{Username: &username} + feed := &model.Feed{Username: "Bob"} + changes.Update(feed) + + if feed.Username != username { + t.Fatalf(`Unexpected value, got %q instead of %q`, feed.Username, username) + } +} + +func TestUpdateFeedUsernameWithEmptyString(t *testing.T) { + username := "" + changes := &feedModification{Username: &username} + feed := &model.Feed{Username: "Bob"} + changes.Update(feed) + + if feed.Username != "" { + t.Fatal(`The Username should be empty now`) + } +} + +func TestUpdateFeedUsernameWhenNotSet(t *testing.T) { + changes := &feedModification{} + feed := &model.Feed{Username: "Alice"} + changes.Update(feed) + + if feed.Username != "Alice" { + t.Fatal(`The Username should not be modified`) + } +} + +func TestUpdateFeedCategory(t *testing.T) { + categoryID := int64(1) + changes := &feedModification{CategoryID: &categoryID} + feed := &model.Feed{Category: &model.Category{ID: 42}} + changes.Update(feed) + + if feed.Category.ID != categoryID { + t.Fatalf(`Unexpected value, got %q instead of %q`, feed.Username, categoryID) + } +} + +func TestUpdateFeedCategoryWithZero(t *testing.T) { + categoryID := int64(0) + changes := &feedModification{CategoryID: &categoryID} + feed := &model.Feed{Category: &model.Category{ID: 42}} + changes.Update(feed) + + if feed.Category.ID != 42 { + t.Fatal(`The CategoryID should not be modified`) + } +} + +func TestUpdateFeedCategoryWhenNotSet(t *testing.T) { + changes := &feedModification{} + feed := &model.Feed{Category: &model.Category{ID: 42}} + changes.Update(feed) + + if feed.Category.ID != 42 { + t.Fatal(`The CategoryID should not be modified`) + } +} + +func TestUpdateUserTheme(t *testing.T) { + theme := "Example 2" + changes := &userModification{Theme: &theme} + user := &model.User{Theme: "Example"} + changes.Update(user) + + if user.Theme != theme { + t.Fatalf(`Unexpected value, got %q instead of %q`, user.Theme, theme) + } +} + +func TestUserThemeWhenNotSet(t *testing.T) { + changes := &userModification{} + user := &model.User{Theme: "Example"} + changes.Update(user) + + if user.Theme != "Example" { + t.Fatal(`The user Theme should not be modified`) + } +} diff --git a/api/user.go b/api/user.go index a231e8c..4e5fa82 100644 --- a/api/user.go +++ b/api/user.go @@ -33,7 +33,7 @@ func (c *Controller) CreateUser(w http.ResponseWriter, r *http.Request) { return } - user, err := decodeUserPayload(r.Body) + user, err := decodeUserCreationPayload(r.Body) if err != nil { json.BadRequest(w, err) return @@ -73,17 +73,12 @@ func (c *Controller) UpdateUser(w http.ResponseWriter, r *http.Request) { return } - user, err := decodeUserPayload(r.Body) + userChanges, err := decodeUserModificationPayload(r.Body) if err != nil { json.BadRequest(w, err) return } - if err := user.ValidateUserModification(); err != nil { - json.BadRequest(w, err) - return - } - originalUser, err := c.store.UserByID(userID) if err != nil { json.BadRequest(w, errors.New("Unable to fetch this user from the database")) @@ -95,7 +90,12 @@ func (c *Controller) UpdateUser(w http.ResponseWriter, r *http.Request) { return } - originalUser.Merge(user) + userChanges.Update(originalUser) + if err := originalUser.ValidateUserModification(); err != nil { + json.BadRequest(w, err) + return + } + if err = c.store.UpdateUser(originalUser); err != nil { json.ServerError(w, errors.New("Unable to update this user")) return |