diff --git a/VERSION b/VERSION index c64d9d48a..a2a3eca1f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.105.1 +0.106.1 diff --git a/protocol/messenger_browsers.go b/protocol/messenger_browsers.go new file mode 100644 index 000000000..6204c70af --- /dev/null +++ b/protocol/messenger_browsers.go @@ -0,0 +1,19 @@ +package protocol + +import ( + "context" + + "github.com/status-im/status-go/services/browsers" +) + +func (m *Messenger) AddBrowser(ctx context.Context, browser browsers.Browser) error { + return m.persistence.AddBrowser(browser) +} + +func (m *Messenger) GetBrowsers(ctx context.Context) (browsers []*browsers.Browser, err error) { + return m.persistence.GetBrowsers() +} + +func (m *Messenger) DeleteBrowser(ctx context.Context, id string) error { + return m.persistence.DeleteBrowser(id) +} diff --git a/protocol/messenger_browsers_test.go b/protocol/messenger_browsers_test.go new file mode 100644 index 000000000..7d24cda8e --- /dev/null +++ b/protocol/messenger_browsers_test.go @@ -0,0 +1,155 @@ +package protocol + +import ( + "context" + "crypto/ecdsa" + "sort" + "testing" + + "github.com/stretchr/testify/suite" + "go.uber.org/zap" + + gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/tt" + "github.com/status-im/status-go/services/browsers" + "github.com/status-im/status-go/waku" +) + +func TestBrowserSuite(t *testing.T) { + suite.Run(t, new(BrowserSuite)) +} + +type BrowserSuite struct { + suite.Suite + m *Messenger // main instance of Messenger + privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger + // If one wants to send messages between different instances of Messenger, + // a single waku service should be shared. + shh types.Waku + logger *zap.Logger +} + +func (s *BrowserSuite) SetupTest() { + s.logger = tt.MustCreateTestLogger() + + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) + s.Require().NoError(shh.Start()) + + s.m = s.newMessenger() + s.privateKey = s.m.identity + _, err := s.m.Start() + s.Require().NoError(err) +} + +func (s *BrowserSuite) TearDownTest() { + s.Require().NoError(s.m.Shutdown()) +} + +func (s *BrowserSuite) newMessenger() *Messenger { + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + messenger, err := newMessengerWithKey(s.shh, privateKey, s.logger, nil) + s.Require().NoError(err) + return messenger +} + +func (s *MessengerBackupSuite) TestBrowsersOrderedNewestFirst() { + msngr := s.newMessenger() + testBrowsers := []*browsers.Browser{ + { + ID: "1", + Name: "first", + Dapp: true, + Timestamp: 10, + }, + { + ID: "2", + Name: "second", + Dapp: true, + Timestamp: 50, + }, + { + ID: "3", + Name: "third", + Dapp: true, + Timestamp: 100, + HistoryIndex: 0, + History: []string{"zero"}, + }, + } + for i := 0; i < len(testBrowsers); i++ { + s.Require().NoError(msngr.AddBrowser(context.TODO(), *testBrowsers[i])) + } + + sort.Slice(testBrowsers, func(i, j int) bool { + return testBrowsers[i].Timestamp > testBrowsers[j].Timestamp + }) + + rst, err := msngr.GetBrowsers(context.TODO()) + s.Require().NoError(err) + s.Require().Equal(testBrowsers, rst) +} + +func (s *MessengerBackupSuite) TestBrowsersHistoryIncluded() { + msngr := s.newMessenger() + browser := &browsers.Browser{ + ID: "1", + Name: "first", + Dapp: true, + Timestamp: 10, + HistoryIndex: 1, + History: []string{"one", "two"}, + } + s.Require().NoError(msngr.AddBrowser(context.TODO(), *browser)) + rst, err := msngr.GetBrowsers(context.TODO()) + s.Require().NoError(err) + s.Require().Len(rst, 1) + s.Require().Equal(browser, rst[0]) +} + +func (s *MessengerBackupSuite) TestBrowsersReplaceOnUpdate() { + msngr := s.newMessenger() + browser := &browsers.Browser{ + ID: "1", + Name: "first", + Dapp: true, + Timestamp: 10, + History: []string{"one", "two"}, + } + s.Require().NoError(msngr.AddBrowser(context.TODO(), *browser)) + browser.Dapp = false + browser.History = []string{"one", "three"} + browser.Timestamp = 107 + s.Require().NoError(msngr.AddBrowser(context.TODO(), *browser)) + rst, err := msngr.GetBrowsers(context.TODO()) + s.Require().NoError(err) + s.Require().Len(rst, 1) + s.Require().Equal(browser, rst[0]) +} + +func (s *MessengerBackupSuite) TestDeleteBrowser() { + msngr := s.newMessenger() + browser := &browsers.Browser{ + ID: "1", + Name: "first", + Dapp: true, + Timestamp: 10, + History: []string{"one", "two"}, + } + + s.Require().NoError(msngr.AddBrowser(context.TODO(), *browser)) + rst, err := msngr.GetBrowsers(context.TODO()) + s.Require().NoError(err) + s.Require().Len(rst, 1) + + s.Require().NoError(msngr.DeleteBrowser(context.TODO(), browser.ID)) + rst, err = msngr.GetBrowsers(context.TODO()) + s.Require().NoError(err) + s.Require().Len(rst, 0) +} diff --git a/protocol/persistence.go b/protocol/persistence.go index b47baeafe..9773ab8fb 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -1176,6 +1176,87 @@ func (db *sqlitePersistence) AddBookmark(bookmark browsers.Bookmark) (browsers.B return bookmark, err } +func (db *sqlitePersistence) AddBrowser(browser browsers.Browser) (err error) { + tx, err := db.db.Begin() + if err != nil { + return + } + defer func() { + if err == nil { + err = tx.Commit() + return + } + _ = tx.Rollback() + }() + insert, err := tx.Prepare("INSERT OR REPLACE INTO browsers(id, name, timestamp, dapp, historyIndex) VALUES(?, ?, ?, ?, ?)") + if err != nil { + return + } + + _, err = insert.Exec(browser.ID, browser.Name, browser.Timestamp, browser.Dapp, browser.HistoryIndex) + insert.Close() + if err != nil { + return + } + + if len(browser.History) == 0 { + return + } + bhInsert, err := tx.Prepare("INSERT INTO browsers_history(browser_id, history) VALUES(?, ?)") + if err != nil { + return + } + defer bhInsert.Close() + for _, history := range browser.History { + _, err = bhInsert.Exec(browser.ID, history) + if err != nil { + return + } + } + return +} + +func (db *sqlitePersistence) InsertBrowser(browser browsers.Browser) (err error) { + tx, err := db.db.Begin() + if err != nil { + return + } + defer func() { + if err == nil { + err = tx.Commit() + return + } + _ = tx.Rollback() + }() + + bInsert, err := tx.Prepare("INSERT OR REPLACE INTO browsers(id, name, timestamp, dapp, historyIndex) VALUES(?, ?, ?, ?, ?)") + if err != nil { + return + } + _, err = bInsert.Exec(browser.ID, browser.Name, browser.Timestamp, browser.Dapp, browser.HistoryIndex) + bInsert.Close() + if err != nil { + return + } + + if len(browser.History) == 0 { + return + } + bhInsert, err := tx.Prepare("INSERT INTO browsers_history(browser_id, history) VALUES(?, ?)") + if err != nil { + return + } + defer bhInsert.Close() + for _, history := range browser.History { + _, err = bhInsert.Exec(browser.ID, history) + if err != nil { + return + } + } + + return +} + func (db *sqlitePersistence) RemoveBookmark(url string, deletedAt uint64) error { tx, err := db.db.Begin() if err != nil { @@ -1193,6 +1274,61 @@ func (db *sqlitePersistence) RemoveBookmark(url string, deletedAt uint64) error return err } +func (db *sqlitePersistence) GetBrowsers() (rst []*browsers.Browser, err error) { + tx, err := db.db.Begin() + if err != nil { + return + } + defer func() { + if err == nil { + err = tx.Commit() + return + } + _ = tx.Rollback() + }() + + // FULL and RIGHT joins are not supported + bRows, err := tx.Query("SELECT id, name, timestamp, dapp, historyIndex FROM browsers ORDER BY timestamp DESC") + if err != nil { + return + } + defer bRows.Close() + browsersArr := map[string]*browsers.Browser{} + for bRows.Next() { + browser := browsers.Browser{} + err = bRows.Scan(&browser.ID, &browser.Name, &browser.Timestamp, &browser.Dapp, &browser.HistoryIndex) + if err != nil { + return nil, err + } + browsersArr[browser.ID] = &browser + rst = append(rst, &browser) + } + + bhRows, err := tx.Query("SELECT browser_id, history from browsers_history") + if err != nil { + return + } + defer bhRows.Close() + var ( + id string + history string + ) + for bhRows.Next() { + err = bhRows.Scan(&id, &history) + if err != nil { + return + } + browsersArr[id].History = append(browsersArr[id].History, history) + } + + return rst, nil +} + +func (db *sqlitePersistence) DeleteBrowser(id string) error { + _, err := db.db.Exec("DELETE from browsers WHERE id = ?", id) + return err +} + func (db *sqlitePersistence) GetBookmarkByURL(url string) (*browsers.Bookmark, error) { bookmark := browsers.Bookmark{} err := db.db.QueryRow(`SELECT url, name, image_url, removed, clock, deleted_at FROM bookmarks WHERE url = ?`, url).Scan(&bookmark.URL, &bookmark.Name, &bookmark.ImageURL, &bookmark.Removed, &bookmark.Clock, &bookmark.DeletedAt) diff --git a/services/browsers/api.go b/services/browsers/api.go index 402ab5449..d880287d9 100644 --- a/services/browsers/api.go +++ b/services/browsers/api.go @@ -15,18 +15,6 @@ type API struct { db *Database } -func (api *API) AddBrowser(ctx context.Context, browser Browser) error { - return api.db.InsertBrowser(browser) -} - -func (api *API) GetBrowsers(ctx context.Context) ([]*Browser, error) { - return api.db.GetBrowsers() -} - -func (api *API) DeleteBrowser(ctx context.Context, id string) error { - return api.db.DeleteBrowser(id) -} - func (api *API) GetBookmarks(ctx context.Context) ([]*Bookmark, error) { log.Debug("call to get bookmarks") rst, err := api.db.GetBookmarks() diff --git a/services/browsers/api_test.go b/services/browsers/api_test.go index 0f504af73..df550c7e7 100644 --- a/services/browsers/api_test.go +++ b/services/browsers/api_test.go @@ -4,7 +4,6 @@ import ( "context" "io/ioutil" "os" - "sort" "testing" "github.com/stretchr/testify/require" @@ -28,109 +27,6 @@ func setupTestAPI(t *testing.T) (*API, func()) { return &API{db: db}, cancel } -func TestBrowsersOrderedNewestFirst(t *testing.T) { - api, cancel := setupTestAPI(t) - defer cancel() - - browsers := []*Browser{ - { - ID: "1", - Name: "first", - Dapp: true, - Timestamp: 10, - }, - { - ID: "2", - Name: "second", - Dapp: true, - Timestamp: 50, - }, - { - ID: "3", - Name: "third", - Dapp: true, - Timestamp: 100, - HistoryIndex: 0, - History: []string{"zero"}, - }, - } - for i := 0; i < len(browsers); i++ { - require.NoError(t, api.AddBrowser(context.TODO(), *browsers[i])) - } - - sort.Slice(browsers, func(i, j int) bool { - return browsers[i].Timestamp > browsers[j].Timestamp - }) - - rst, err := api.GetBrowsers(context.TODO()) - require.NoError(t, err) - require.Equal(t, browsers, rst) -} - -func TestBrowsersHistoryIncluded(t *testing.T) { - api, cancel := setupTestAPI(t) - defer cancel() - - browser := &Browser{ - ID: "1", - Name: "first", - Dapp: true, - Timestamp: 10, - HistoryIndex: 1, - History: []string{"one", "two"}, - } - require.NoError(t, api.AddBrowser(context.TODO(), *browser)) - rst, err := api.GetBrowsers(context.TODO()) - require.NoError(t, err) - require.Len(t, rst, 1) - require.Equal(t, browser, rst[0]) -} - -func TestBrowsersReplaceOnUpdate(t *testing.T) { - api, cancel := setupTestAPI(t) - defer cancel() - - browser := &Browser{ - ID: "1", - Name: "first", - Dapp: true, - Timestamp: 10, - History: []string{"one", "two"}, - } - require.NoError(t, api.AddBrowser(context.TODO(), *browser)) - browser.Dapp = false - browser.History = []string{"one", "three"} - browser.Timestamp = 107 - require.NoError(t, api.AddBrowser(context.TODO(), *browser)) - rst, err := api.GetBrowsers(context.TODO()) - require.NoError(t, err) - require.Len(t, rst, 1) - require.Equal(t, browser, rst[0]) -} - -func TestDeleteBrowser(t *testing.T) { - api, cancel := setupTestAPI(t) - defer cancel() - - browser := &Browser{ - ID: "1", - Name: "first", - Dapp: true, - Timestamp: 10, - History: []string{"one", "two"}, - } - - require.NoError(t, api.AddBrowser(context.TODO(), *browser)) - rst, err := api.GetBrowsers(context.TODO()) - require.NoError(t, err) - require.Len(t, rst, 1) - - require.NoError(t, api.DeleteBrowser(context.TODO(), browser.ID)) - rst, err = api.GetBrowsers(context.TODO()) - require.NoError(t, err) - require.Len(t, rst, 0) -} - func TestBookmarks(t *testing.T) { api, cancel := setupTestAPI(t) defer cancel() diff --git a/services/browsers/database.go b/services/browsers/database.go index bdfc10338..e9ec05e20 100644 --- a/services/browsers/database.go +++ b/services/browsers/database.go @@ -23,111 +23,6 @@ func NewDB(db *sql.DB) *Database { return &Database{db: db} } -type Browser struct { - ID string `json:"browser-id"` - Name string `json:"name"` - Timestamp uint64 `json:"timestamp"` - Dapp bool `json:"dapp?"` - HistoryIndex int `json:"history-index"` - History []string `json:"history,omitempty"` -} - -func (db *Database) InsertBrowser(browser Browser) (err error) { - tx, err := db.db.Begin() - if err != nil { - return - } - defer func() { - if err == nil { - err = tx.Commit() - return - } - _ = tx.Rollback() - }() - - bInsert, err := tx.Prepare("INSERT OR REPLACE INTO browsers(id, name, timestamp, dapp, historyIndex) VALUES(?, ?, ?, ?, ?)") - if err != nil { - return - } - _, err = bInsert.Exec(browser.ID, browser.Name, browser.Timestamp, browser.Dapp, browser.HistoryIndex) - bInsert.Close() - if err != nil { - return - } - - if len(browser.History) == 0 { - return - } - bhInsert, err := tx.Prepare("INSERT INTO browsers_history(browser_id, history) VALUES(?, ?)") - if err != nil { - return - } - defer bhInsert.Close() - for _, history := range browser.History { - _, err = bhInsert.Exec(browser.ID, history) - if err != nil { - return - } - } - - return -} - -func (db *Database) GetBrowsers() (rst []*Browser, err error) { - tx, err := db.db.Begin() - if err != nil { - return - } - defer func() { - if err == nil { - err = tx.Commit() - return - } - _ = tx.Rollback() - }() - - // FULL and RIGHT joins are not supported - bRows, err := tx.Query("SELECT id, name, timestamp, dapp, historyIndex FROM browsers ORDER BY timestamp DESC") - if err != nil { - return - } - defer bRows.Close() - browsers := map[string]*Browser{} - for bRows.Next() { - browser := Browser{} - err = bRows.Scan(&browser.ID, &browser.Name, &browser.Timestamp, &browser.Dapp, &browser.HistoryIndex) - if err != nil { - return nil, err - } - browsers[browser.ID] = &browser - rst = append(rst, &browser) - } - - bhRows, err := tx.Query("SELECT browser_id, history from browsers_history") - if err != nil { - return - } - defer bhRows.Close() - var ( - id string - history string - ) - for bhRows.Next() { - err = bhRows.Scan(&id, &history) - if err != nil { - return - } - browsers[id].History = append(browsers[id].History, history) - } - - return rst, nil -} - -func (db *Database) DeleteBrowser(id string) error { - _, err := db.db.Exec("DELETE from browsers WHERE id = ?", id) - return err -} - type BookmarksType string type Bookmark struct { @@ -138,6 +33,14 @@ type Bookmark struct { Clock uint64 `json:"-"` //used to sync DeletedAt uint64 `json:"deletedAt,omitempty"` } +type Browser struct { + ID string `json:"browser-id"` + Name string `json:"name"` + Timestamp uint64 `json:"timestamp"` + Dapp bool `json:"dapp?"` + HistoryIndex int `json:"history-index"` + History []string `json:"history,omitempty"` +} func (db *Database) GetBookmarks() ([]*Bookmark, error) { rows, err := db.db.Query(`SELECT url, name, image_url, removed, deleted_at FROM bookmarks`) diff --git a/services/ext/api.go b/services/ext/api.go index 8e9a6b05a..81a63a15e 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -813,6 +813,18 @@ func (api *PublicAPI) AddBookmark(ctx context.Context, bookmark browsers.Bookmar return api.service.messenger.AddBookmark(ctx, bookmark) } +func (api *PublicAPI) AddBrowser(ctx context.Context, browser browsers.Browser) error { + return api.service.messenger.AddBrowser(ctx, browser) +} + +func (api *PublicAPI) GetBrowsers(ctx context.Context) (browsers []*browsers.Browser, err error) { + return api.service.messenger.GetBrowsers(ctx) +} + +func (api *PublicAPI) DeleteBrowser(ctx context.Context, id string) error { + return api.service.messenger.DeleteBrowser(ctx, id) +} + func (api *PublicAPI) RemoveBookmark(ctx context.Context, url string) error { return api.service.messenger.RemoveBookmark(ctx, url) }