diff --git a/backend/app/migrator/migrator.go b/backend/app/migrator/migrator.go
index ae80a52b..4ff7d520 100644
--- a/backend/app/migrator/migrator.go
+++ b/backend/app/migrator/migrator.go
@@ -11,6 +11,7 @@ import (
"github.com/pkg/errors"
"github.com/umputun/remark/backend/app/store"
+ "github.com/umputun/remark/backend/app/store/service"
)
// Importer defines interface to convert posts from external sources
@@ -29,6 +30,7 @@ type Store interface {
Find(locator store.Locator, sort string) ([]store.Comment, error)
List(siteID string, limit int, skip int) ([]store.PostInfo, error)
DeleteAll(siteID string) error
+ Metas(siteID string) (umetas []service.UserMetaData, pmetas []service.PostMetaData, err error)
}
// ImportParams defines everything needed to run import
diff --git a/backend/app/migrator/remark.go b/backend/app/migrator/remark.go
index 93b1505d..666b5fe7 100644
--- a/backend/app/migrator/remark.go
+++ b/backend/app/migrator/remark.go
@@ -4,27 +4,41 @@ import (
"bufio"
"bytes"
"encoding/json"
+ "fmt"
"io"
"log"
"github.com/pkg/errors"
"github.com/umputun/remark/backend/app/store"
+ "github.com/umputun/remark/backend/app/store/service"
+)
+
+const (
+ header = `{"version":1, "comments":[`
+ metaHeader = "],\n\"meta\":"
+ footer = `}`
)
// Remark implements exporter and importer for internal store format
+// {"version": 1, comments:[{...}\n,{}], meta: {meta}}
type Remark struct {
DataStore Store
}
// Export all comments to writer as json strings. Each comment is one string, separated by "\n"
func (r *Remark) Export(w io.Writer, siteID string) (size int, err error) {
+
+ if _, err := fmt.Fprintf(w, "%s\n", header); err != nil {
+ return 0, err
+ }
+
topics, err := r.DataStore.List(siteID, 0, 0)
if err != nil {
return 0, err
}
- log.Printf("[DEBUG] exporting %d topics", len(topics))
+ log.Printf("[DEBUG] exporting %d topics", len(topics))
commentsCount := 0
for i := len(topics) - 1; i >= 0; i-- { // topics from List sorted in opposite direction
topic := topics[i]
@@ -33,7 +47,7 @@ func (r *Remark) Export(w io.Writer, siteID string) (size int, err error) {
return commentsCount, err
}
- for _, comment := range comments {
+ for n, comment := range comments {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
@@ -43,13 +57,42 @@ func (r *Remark) Export(w io.Writer, siteID string) (size int, err error) {
return commentsCount, errors.Wrapf(err, "can't marshal %v", comments)
}
data := buf.Bytes()
+ data = bytes.TrimSuffix(data, []byte("\n"))
if _, err := w.Write(data); err != nil {
return commentsCount, errors.Wrap(err, "can't write comment data")
}
+ if n < len(comments)-1 || i != 0 { // don't add , on last comment
+ w.Write([]byte(","))
+ }
+ w.Write([]byte("\n"))
+
commentsCount++
}
}
log.Printf("[DEBUG] exported %d comments", commentsCount)
+
+ if _, err := fmt.Fprintf(w, "%s", metaHeader); err != nil {
+ return 0, err
+ }
+
+ meta := struct {
+ Users []service.UserMetaData `json:"users"`
+ Posts []service.PostMetaData `json:"posts"`
+ }{}
+
+ meta.Users, meta.Posts, err = r.DataStore.Metas(siteID)
+ if err != nil {
+ return 0, err
+ }
+
+ if err := json.NewEncoder(w).Encode(meta); err != nil {
+ return 0, err
+ }
+
+ if _, err := fmt.Fprintf(w, "%s\n", footer); err != nil {
+ return 0, err
+ }
+
return commentsCount, nil
}
diff --git a/backend/app/migrator/remark_test.go b/backend/app/migrator/remark_test.go
index 7819466a..ff31a83e 100644
--- a/backend/app/migrator/remark_test.go
+++ b/backend/app/migrator/remark_test.go
@@ -2,13 +2,14 @@ package migrator
import (
"bytes"
+ "encoding/json"
"fmt"
"log"
"os"
"testing"
"time"
- "github.com/coreos/bbolt"
+ bolt "github.com/coreos/bbolt"
"github.com/stretchr/testify/assert"
"github.com/umputun/remark/backend/app/store"
@@ -22,6 +23,9 @@ var testDb = "/tmp/test-remark.db"
func TestRemark_Export(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
+ b.SetReadOnly(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, true)
+ b.SetBlock("radio-t", "user-2", true, time.Hour)
+ b.SetVerified("radio-t", "user-1", true)
r := Remark{DataStore: b}
buf := &bytes.Buffer{}
@@ -29,11 +33,35 @@ func TestRemark_Export(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, 2, size)
- c1, err := buf.ReadString('\n')
- assert.Nil(t, err)
+ c1 := buf.String()
log.Print(c1)
- exp := `{"id":"efbc17f177ee1a1c0ee6e1e025749966ec071adc","pid":"","text":"some text, link","user":{"name":"user name","id":"user1","picture":"","ip":"293ec5b0cf154855258824ec7fac5dc63d176915","admin":false},"locator":{"site":"radio-t","url":"https://radio-t.com"},"score":0,"votes":{},"time":"2017-12-20T15:18:22-06:00"}` + "\n"
- assert.Equal(t, exp, c1)
+
+ res := struct {
+ Version int `json:"version"`
+ Comments []store.Comment `json:"comments"`
+ Meta struct {
+ Users []service.UserMetaData `json:"users"`
+ Posts []service.PostMetaData `json:"posts"`
+ } `json:"meta"`
+ }{}
+
+ err = json.Unmarshal([]byte(c1), &res)
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(res.Comments))
+ assert.Equal(t, "some text, link", res.Comments[0].Text)
+
+ assert.Equal(t, 2, len(res.Meta.Users))
+ assert.Equal(t, "user-2", res.Meta.Users[0].ID)
+ assert.Equal(t, true, res.Meta.Users[0].Blocked.Status)
+ assert.Equal(t, false, res.Meta.Users[0].Verified)
+ assert.Equal(t, "user-1", res.Meta.Users[1].ID)
+ assert.Equal(t, false, res.Meta.Users[1].Blocked.Status)
+ assert.Equal(t, true, res.Meta.Users[1].Verified)
+
+ assert.Equal(t, 1, len(res.Meta.Posts))
+ assert.Equal(t, "https://radio-t.com", res.Meta.Posts[0].URL)
+ assert.Equal(t, true, res.Meta.Posts[0].ReadOnly)
+
}
func TestRemark_Import(t *testing.T) {
@@ -113,8 +141,8 @@ func prep(t *testing.T) *service.DataStore {
comment = store.Comment{
Text: "some text2", Timestamp: time.Date(2017, 12, 20, 15, 18, 23, 0, time.Local),
- Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
- User: store.User{ID: "user1", Name: "user name"},
+ Locator: store.Locator{URL: "https://radio-t.com/2", SiteID: "radio-t"},
+ User: store.User{ID: "user2", Name: "user name"},
}
_, err = b.Create(comment)
assert.Nil(t, err)
diff --git a/backend/app/store/service/service.go b/backend/app/store/service/service.go
index d774b624..7f3e6f47 100644
--- a/backend/app/store/service/service.go
+++ b/backend/app/store/service/service.go
@@ -30,18 +30,18 @@ type DataStore struct {
// UserMetaData keeps info about user flags
type UserMetaData struct {
- UserID string
+ ID string `json:"id"`
Blocked struct {
- Status bool
- Until time.Time
- }
- Verified bool
+ Status bool `json:"status"`
+ Until time.Time `json:"until"`
+ } `json:"blocked"`
+ Verified bool `json:"verified"`
}
// PostMetaData keeps info about post flags
type PostMetaData struct {
- URL string
- ReadOnly bool
+ URL string `json:"url"`
+ ReadOnly bool `json:"read_only"`
}
const defaultCommentMaxSize = 2000
@@ -224,7 +224,8 @@ func (s *DataStore) IsAdmin(siteID string, userID string) bool {
// Metas returns metadata for users and posts
func (s *DataStore) Metas(siteID string) (umetas []UserMetaData, pmetas []PostMetaData, err error) {
-
+ umetas = []UserMetaData{}
+ pmetas = []PostMetaData{}
// set posts meta
posts, err := s.List(siteID, 0, 0)
if err != nil {
@@ -248,7 +249,7 @@ func (s *DataStore) Metas(siteID string) (umetas []UserMetaData, pmetas []PostMe
for _, b := range blocked {
val, ok := m[b.ID]
if !ok {
- val = UserMetaData{UserID: b.ID}
+ val = UserMetaData{ID: b.ID}
}
val.Blocked.Status = true
val.Blocked.Until = b.Until
@@ -263,7 +264,7 @@ func (s *DataStore) Metas(siteID string) (umetas []UserMetaData, pmetas []PostMe
for _, v := range verified {
val, ok := m[v]
if !ok {
- val = UserMetaData{UserID: v}
+ val = UserMetaData{ID: v}
}
val.Verified = true
m[v] = val
diff --git a/backend/app/store/service/service_test.go b/backend/app/store/service/service_test.go
index 3ede92af..ac741074 100644
--- a/backend/app/store/service/service_test.go
+++ b/backend/app/store/service/service_test.go
@@ -9,7 +9,7 @@ import (
"testing"
"time"
- "github.com/coreos/bbolt"
+ bolt "github.com/coreos/bbolt"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -380,10 +380,15 @@ func TestService_Metas(t *testing.T) {
b := DataStore{Interface: prepStoreEngine(t), EditDuration: 100 * time.Millisecond,
AdminStore: admin.NewStaticKeyStore("secret 123")}
+ um, pm, err := b.Metas("radio-t")
+ require.NoError(t, err)
+ assert.Equal(t, 0, len(um))
+ assert.Equal(t, 0, len(pm))
+
assert.NoError(t, b.SetVerified("radio-t", "user1", true))
assert.NoError(t, b.SetReadOnly(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, true))
- um, pm, err := b.Metas("radio-t")
+ um, pm, err = b.Metas("radio-t")
require.NoError(t, err)
assert.Equal(t, 1, len(um))