Commit 016538ce authored by Grisha Kruglov's avatar Grisha Kruglov Committed by Grisha Kruglov
Browse files

Part 1.1: concept-sync

This should have been folded into Part 1, but I changes got a little tangled up.
parent 86baa3a7
......@@ -15,8 +15,6 @@ import mozilla.components.concept.storage.HistoryAutocompleteResult
import mozilla.components.concept.storage.HistoryStorage
import mozilla.components.concept.storage.PageObservation
import mozilla.components.concept.storage.SearchResult
import mozilla.components.concept.sync.SyncError
import mozilla.components.concept.sync.SyncOk
import mozilla.components.concept.sync.SyncStatus
import mozilla.components.concept.sync.SyncableStore
import mozilla.components.concept.storage.VisitType
......@@ -107,9 +105,9 @@ open class PlacesHistoryStorage(context: Context) : HistoryStorage, SyncableStor
return RustPlacesConnection.newConnection(storageDir).use {
try {
it.sync(authInfo.into())
SyncOk
SyncStatus.Ok
} catch (e: PlacesException) {
SyncError(e)
SyncStatus.Error(e)
}
}
}
......
......@@ -5,40 +5,73 @@
package mozilla.components.concept.sync
import mozilla.components.support.base.observer.Observable
import java.io.Closeable
import java.lang.Exception
/**
* Results of running a sync via [SyncableStore.sync].
*/
sealed class SyncStatus {
/**
* Sync succeeded successfully.
*/
object Ok : SyncStatus()
/**
* Sync completed with an error.
*/
data class Error(val exception: Exception) : SyncStatus()
}
/**
* Describes a "sync" entry point for a storage layer.
*/
interface SyncableStore {
/**
* Performs a sync.
*
* @param authInfo Auth information necessary for syncing this store.
* @return [SyncStatus] A status object describing how sync went.
*/
suspend fun sync(authInfo: AuthInfo): SyncStatus
}
/**
* Describes a "sync" entry point for an application.
*/
interface SyncManager : Observable<SyncStatusObserver> {
/**
* An authenticated account is now available.
*/
fun authenticated(account: OAuthAccount)
/**
* Previously authenticated account has been logged-out.
*/
fun loggedOut()
fun addStore(name: String, store: SyncableStore)
/**
* Add a store, by [name], to a set of synchronization stores.
* Implementation is expected to be able to either access or instantiate concrete
* [SyncableStore] instances given this name.
*/
fun addStore(name: String)
/**
* Remove a store previously specified via [addStore] from a set of synchronization stores.
*/
fun removeStore(name: String)
/**
* Kick-off an immediate sync.
* Request an immediate synchronization of all configured stores.
*
* @param startup Boolean flag indicating if sync is being requested in a startup situation.
*/
fun syncNow(startup: Boolean = false)
fun createDispatcher(stores: Map<String, SyncableStore>, account: OAuthAccount): SyncDispatcher
}
interface SyncDispatcher : Closeable, Observable<SyncStatusObserver> {
fun isSyncActive(): Boolean
/**
* Kick-off an immediate sync.
*
* @param startup Boolean flag indicating if sync is being requested in a startup situation.
* Indicates if synchronization is currently active.
*/
fun syncNow(startup: Boolean = false)
fun startPeriodicSync()
fun stopPeriodicSync()
fun isSyncRunning(): Boolean
}
/**
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package mozilla.components.concept.sync
import java.lang.Exception
/**
* Describes a "sync" entry point for store which operates over [AuthInfo].
*/
interface SyncableStore {
/**
* Performs a sync.
* @param authInfo Auth information of type [AuthInfo] necessary for syncing this store.
* @return [SyncStatus] A status object describing how sync went.
*/
suspend fun sync(authInfo: AuthInfo): SyncStatus
}
sealed class SyncStatus
object SyncOk : SyncStatus()
data class SyncError(val exception: Exception) : SyncStatus()
......@@ -14,8 +14,6 @@ import mozilla.appservices.logins.DatabaseLoginsStorage
import mozilla.appservices.logins.LoginsStorage
import mozilla.appservices.logins.MemoryLoginsStorage
import mozilla.components.concept.sync.AuthInfo
import mozilla.components.concept.sync.SyncError
import mozilla.components.concept.sync.SyncOk
import mozilla.components.concept.sync.SyncStatus
import mozilla.components.concept.sync.SyncableStore
......@@ -375,10 +373,10 @@ data class SyncableLoginsStore(
return try {
withUnlocked {
it.sync(authInfo.into()).await()
SyncOk
SyncStatus.Ok
}
} catch (e: LoginsStorageException) {
SyncError(e)
SyncStatus.Error(e)
}
}
......
......@@ -10,8 +10,6 @@ import mozilla.components.concept.sync.AccessTokenInfo
import mozilla.components.concept.sync.AuthInfo
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.OAuthScopedKey
import mozilla.components.concept.sync.SyncError
import mozilla.components.concept.sync.SyncOk
import mozilla.components.concept.sync.SyncStatus
import mozilla.components.concept.sync.SyncStatusObserver
import mozilla.components.concept.sync.SyncableStore
......@@ -52,16 +50,16 @@ class StorageSyncTest {
val testStore: SyncableStore = mock()
val feature = StorageSync(mapOf("testStore" to testStore), "sync-scope")
`when`(testStore.sync(any())).thenReturn(SyncOk)
`when`(testStore.sync(any())).thenReturn(SyncStatus.Ok)
var results = feature.sync(mockAccount)
assertTrue(results.getValue("testStore").status is SyncOk)
assertTrue(results.getValue("testStore").status is SyncStatus.Ok)
assertEquals(1, results.size)
`when`(testStore.sync(any())).thenReturn(SyncError(Exception("test")))
`when`(testStore.sync(any())).thenReturn(SyncStatus.Error(Exception("test")))
results = feature.sync(mockAccount)
var error = results.getValue("testStore").status
assertTrue(error is SyncError)
assertEquals("test", (error as SyncError).exception.message)
assertTrue(error is SyncStatus.Error)
assertEquals("test", (error as SyncStatus.Error).exception.message)
assertEquals(1, results.size)
// Multiple stores, different result types.
......@@ -72,14 +70,14 @@ class StorageSyncTest {
"sync-scope"
)
`when`(anotherStore.sync(any())).thenReturn(SyncOk)
`when`(anotherStore.sync(any())).thenReturn(SyncStatus.Ok)
results = anotherFeature.sync(mockAccount)
assertEquals(2, results.size)
error = results.getValue("testStore").status
assertTrue(error is SyncError)
assertEquals("test", (error as SyncError).exception.message)
assertTrue(results.getValue("goodStore").status is SyncOk)
assertTrue(error is SyncStatus.Error)
assertEquals("test", (error as SyncStatus.Error).exception.message)
assertTrue(results.getValue("goodStore").status is SyncStatus.Ok)
}
@Test
......@@ -108,7 +106,7 @@ class StorageSyncTest {
val testStore = object : SyncableStore {
override suspend fun sync(authInfo: AuthInfo): SyncStatus {
verifier.verify()
return SyncOk
return SyncStatus.Ok
}
}
val syncStatusObserver: SyncStatusObserver = mock()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment