aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Gopkg.lock2
-rw-r--r--api/feed.go13
-rw-r--r--api/opml.go39
-rw-r--r--daemon/routes.go1
-rw-r--r--integration_test.go28
-rw-r--r--reader/opml/handler.go5
-rw-r--r--vendor/github.com/miniflux/miniflux-go/README.md6
-rw-r--r--vendor/github.com/miniflux/miniflux-go/client.go7
-rw-r--r--vendor/github.com/miniflux/miniflux-go/request.go18
9 files changed, 96 insertions, 23 deletions
diff --git a/Gopkg.lock b/Gopkg.lock
index 1f1ea17..bac6827 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -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