diff options
-rw-r--r-- | Gopkg.lock | 2 | ||||
-rw-r--r-- | api/feed.go | 13 | ||||
-rw-r--r-- | api/opml.go | 39 | ||||
-rw-r--r-- | daemon/routes.go | 1 | ||||
-rw-r--r-- | integration_test.go | 28 | ||||
-rw-r--r-- | reader/opml/handler.go | 5 | ||||
-rw-r--r-- | vendor/github.com/miniflux/miniflux-go/README.md | 6 | ||||
-rw-r--r-- | vendor/github.com/miniflux/miniflux-go/client.go | 7 | ||||
-rw-r--r-- | vendor/github.com/miniflux/miniflux-go/request.go | 18 |
9 files changed, 96 insertions, 23 deletions
@@ -45,7 +45,7 @@ branch = "master" name = "github.com/miniflux/miniflux-go" packages = ["."] - revision = "887ba3b062946784f0e64edb1734f435beb204f9" + revision = "7939463a4e1a1c5392d026d8d28bf7732459abd7" [[projects]] name = "github.com/tdewolff/minify" diff --git a/api/feed.go b/api/feed.go index 72eaa41..f6e3c16 100644 --- a/api/feed.go +++ b/api/feed.go @@ -11,8 +11,6 @@ import ( "github.com/miniflux/miniflux/http/context" "github.com/miniflux/miniflux/http/request" "github.com/miniflux/miniflux/http/response/json" - "github.com/miniflux/miniflux/http/response/xml" - "github.com/miniflux/miniflux/reader/opml" ) // CreateFeed is the API handler to create a new feed. @@ -143,17 +141,6 @@ func (c *Controller) GetFeeds(w http.ResponseWriter, r *http.Request) { json.OK(w, feeds) } -// Export is the API handler that incoves an OPML export. -func (c *Controller) Export(w http.ResponseWriter, r *http.Request) { - opmlHandler := opml.NewHandler(c.store) - opml, err := opmlHandler.Export(context.New(r).UserID()) - if err != nil { - json.ServerError(w, errors.New("unable to export feeds to OPML")) - } - - xml.OK(w, opml) -} - // GetFeed is the API handler to get a feed. func (c *Controller) GetFeed(w http.ResponseWriter, r *http.Request) { feedID, err := request.IntParam(r, "feedID") diff --git a/api/opml.go b/api/opml.go new file mode 100644 index 0000000..8613ded --- /dev/null +++ b/api/opml.go @@ -0,0 +1,39 @@ +// 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 ( + "net/http" + + "github.com/miniflux/miniflux/http/context" + "github.com/miniflux/miniflux/http/response/json" + "github.com/miniflux/miniflux/http/response/xml" + "github.com/miniflux/miniflux/reader/opml" +) + +// Export is the API handler that export feeds to OPML. +func (c *Controller) Export(w http.ResponseWriter, r *http.Request) { + opmlHandler := opml.NewHandler(c.store) + opml, err := opmlHandler.Export(context.New(r).UserID()) + if err != nil { + json.ServerError(w, err) + return + } + + xml.OK(w, opml) +} + +// Import is the API handler that import an OPML file. +func (c *Controller) Import(w http.ResponseWriter, r *http.Request) { + opmlHandler := opml.NewHandler(c.store) + err := opmlHandler.Import(context.New(r).UserID(), r.Body) + defer r.Body.Close() + if err != nil { + json.ServerError(w, err) + return + } + + json.Created(w, map[string]string{"message": "Feeds imported successfully"}) +} diff --git a/daemon/routes.go b/daemon/routes.go index 00d2827..a3a373a 100644 --- a/daemon/routes.go +++ b/daemon/routes.go @@ -71,6 +71,7 @@ func routes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handle apiRouter.HandleFunc("/feeds/{feedID}", apiController.RemoveFeed).Methods("DELETE") apiRouter.HandleFunc("/feeds/{feedID}/icon", apiController.FeedIcon).Methods("GET") apiRouter.HandleFunc("/export", apiController.Export).Methods("GET") + apiRouter.HandleFunc("/import", apiController.Import).Methods("POST") apiRouter.HandleFunc("/feeds/{feedID}/entries", apiController.GetFeedEntries).Methods("GET") apiRouter.HandleFunc("/feeds/{feedID}/entries/{entryID}", apiController.GetFeedEntry).Methods("GET") apiRouter.HandleFunc("/entries", apiController.GetEntries).Methods("GET") diff --git a/integration_test.go b/integration_test.go index fb0a785..88ecfa6 100644 --- a/integration_test.go +++ b/integration_test.go @@ -7,6 +7,8 @@ package main import ( + "bytes" + "io/ioutil" "math/rand" "strconv" "strings" @@ -653,6 +655,32 @@ func TestExport(t *testing.T) { } } +func TestImport(t *testing.T) { + username := getRandomUsername() + client := miniflux.NewClient(testBaseURL, testAdminUsername, testAdminPassword) + _, err := client.CreateUser(username, testStandardPassword, false) + if err != nil { + t.Fatal(err) + } + + client = miniflux.NewClient(testBaseURL, username, testStandardPassword) + + data := `<?xml version="1.0" encoding="UTF-8"?> + <opml version="2.0"> + <body> + <outline text="Test Category"> + <outline title="Test" text="Test" xmlUrl="` + testFeedURL + `" htmlUrl="` + testWebsiteURL + `"></outline> + </outline> + </body> + </opml>` + + b := bytes.NewReader([]byte(data)) + err = client.Import(ioutil.NopCloser(b)) + if err != nil { + t.Fatal(err) + } +} + func TestUpdateFeed(t *testing.T) { username := getRandomUsername() client := miniflux.NewClient(testBaseURL, testAdminUsername, testAdminPassword) diff --git a/reader/opml/handler.go b/reader/opml/handler.go index 56083c7..3bbce6c 100644 --- a/reader/opml/handler.go +++ b/reader/opml/handler.go @@ -23,8 +23,7 @@ type Handler struct { func (h *Handler) Export(userID int64) (string, error) { feeds, err := h.store.Feeds(userID) if err != nil { - logger.Error("[OPML:Export] %v", err) - return "", errors.New("unable to fetch feeds") + return "", err } var subscriptions SubcriptionList @@ -74,7 +73,7 @@ func (h *Handler) Import(userID int64, data io.Reader) error { err := h.store.CreateCategory(category) if err != nil { logger.Error("[OPML:Import] %v", err) - return fmt.Errorf(`unable to create this category: "%s"`, subscription.CategoryName) + return fmt.Errorf(`unable to create this category: %q`, subscription.CategoryName) } } } diff --git a/vendor/github.com/miniflux/miniflux-go/README.md b/vendor/github.com/miniflux/miniflux-go/README.md index d1b5cda..aea2261 100644 --- a/vendor/github.com/miniflux/miniflux-go/README.md +++ b/vendor/github.com/miniflux/miniflux-go/README.md @@ -26,7 +26,7 @@ package main import ( "fmt" - + "io/ioutil" "github.com/miniflux/miniflux-go" ) @@ -41,7 +41,7 @@ func main() { } fmt.Println(feeds) - // Backup to opml file. + // Backup your feeds to an OPML file. opml, err := client.Export() if err != nil { fmt.Println(err) @@ -53,8 +53,8 @@ func main() { fmt.Println(err) return } - fmt.Println("backup done!") + fmt.Println("backup done!") } ``` diff --git a/vendor/github.com/miniflux/miniflux-go/client.go b/vendor/github.com/miniflux/miniflux-go/client.go index d6036dd..7b6a847 100644 --- a/vendor/github.com/miniflux/miniflux-go/client.go +++ b/vendor/github.com/miniflux/miniflux-go/client.go @@ -7,6 +7,7 @@ package miniflux import ( "encoding/json" "fmt" + "io" "io/ioutil" "net/url" "strconv" @@ -230,6 +231,12 @@ func (c *Client) Export() ([]byte, error) { return opml, nil } +// Import imports an OPML file. +func (c *Client) Import(f io.ReadCloser) error { + _, err := c.request.PostFile("/v1/import", f) + return err +} + // Feed gets a feed. func (c *Client) Feed(feedID int64) (*Feed, error) { body, err := c.request.Get(fmt.Sprintf("/v1/feeds/%d", feedID)) diff --git a/vendor/github.com/miniflux/miniflux-go/request.go b/vendor/github.com/miniflux/miniflux-go/request.go index a24a425..cae4df7 100644 --- a/vendor/github.com/miniflux/miniflux-go/request.go +++ b/vendor/github.com/miniflux/miniflux-go/request.go @@ -26,6 +26,7 @@ var ( errNotAuthorized = errors.New("miniflux: unauthorized (bad credentials)") errForbidden = errors.New("miniflux: access forbidden") errServerError = errors.New("miniflux: internal server error") + errNotFound = errors.New("miniflux: resource not found") ) type errorResponse struct { @@ -46,6 +47,10 @@ func (r *request) Post(path string, data interface{}) (io.ReadCloser, error) { return r.execute(http.MethodPost, path, data) } +func (r *request) PostFile(path string, f io.ReadCloser) (io.ReadCloser, error) { + return r.execute(http.MethodPost, path, f) +} + func (r *request) Put(path string, data interface{}) (io.ReadCloser, error) { return r.execute(http.MethodPut, path, data) } @@ -72,7 +77,12 @@ func (r *request) execute(method, path string, data interface{}) (io.ReadCloser, request.SetBasicAuth(r.username, r.password) if data != nil { - request.Body = ioutil.NopCloser(bytes.NewBuffer(r.toJSON(data))) + switch data.(type) { + case io.ReadCloser: + request.Body = data.(io.ReadCloser) + default: + request.Body = ioutil.NopCloser(bytes.NewBuffer(r.toJSON(data))) + } } client := r.buildClient() @@ -88,6 +98,8 @@ func (r *request) execute(method, path string, data interface{}) (io.ReadCloser, return nil, errForbidden case http.StatusInternalServerError: return nil, errServerError + case http.StatusNotFound: + return nil, errNotFound case http.StatusBadRequest: defer response.Body.Close() @@ -100,8 +112,8 @@ func (r *request) execute(method, path string, data interface{}) (io.ReadCloser, return nil, fmt.Errorf("miniflux: bad request (%s)", resp.ErrorMessage) } - if response.StatusCode >= 400 { - return nil, fmt.Errorf("miniflux: server error (statusCode=%d)", response.StatusCode) + if response.StatusCode > 400 { + return nil, fmt.Errorf("miniflux: status code=%d", response.StatusCode) } return response.Body, nil |