mirror of https://github.com/readrops/Readrops.git
Rewrite Nextcloud News API implementation
* New data source * New service * Tests
This commit is contained in:
parent
7a4d4d7225
commit
b879d0ae9d
|
@ -0,0 +1,157 @@
|
||||||
|
package com.readrops.api.services.nextcloudnews
|
||||||
|
|
||||||
|
import com.gitlab.mvysny.konsumexml.konsumeXml
|
||||||
|
import com.readrops.api.services.SyncResult
|
||||||
|
import com.readrops.api.services.SyncType
|
||||||
|
import com.readrops.api.services.nextcloudnews.adapters.NextNewsUserAdapter
|
||||||
|
import com.readrops.db.entities.Feed
|
||||||
|
import com.readrops.db.entities.Folder
|
||||||
|
import com.readrops.db.entities.Item
|
||||||
|
import com.readrops.db.entities.account.Account
|
||||||
|
import com.readrops.db.pojo.StarItem
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
|
||||||
|
class NewNextcloudNewsDataSource(private val service: NewNextcloudNewsService) {
|
||||||
|
|
||||||
|
suspend fun login(client: OkHttpClient, account: Account): String {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(account.url + "/ocs/v1.php/cloud/users/" + account.login)
|
||||||
|
.addHeader("OCS-APIRequest", "true")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val response = client.newCall(request)
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
val displayName = NextNewsUserAdapter().fromXml(response.body!!.byteStream().konsumeXml())
|
||||||
|
response.close()
|
||||||
|
|
||||||
|
return displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun synchronize(syncType: SyncType, syncData: NextcloudNewsSyncData): SyncResult =
|
||||||
|
with(CoroutineScope(Dispatchers.IO)) {
|
||||||
|
return if (syncType == SyncType.INITIAL_SYNC) {
|
||||||
|
SyncResult().apply {
|
||||||
|
listOf(
|
||||||
|
async { folders = getFolders() },
|
||||||
|
async { feeds = getFeeds() },
|
||||||
|
async { items = getItems(ItemQueryType.ALL.value, false, MAX_ITEMS) },
|
||||||
|
async {
|
||||||
|
starredItems =
|
||||||
|
getItems(ItemQueryType.STARRED.value, true, MAX_STARRED_ITEMS)
|
||||||
|
}
|
||||||
|
).awaitAll()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
listOf(
|
||||||
|
async { setItemsReadState(syncData) },
|
||||||
|
async { setItemsStarState(StateType.STAR, syncData.starredIds) },
|
||||||
|
async { setItemsStarState(StateType.UNSTAR, syncData.unstarredIds) }
|
||||||
|
).awaitAll()
|
||||||
|
|
||||||
|
SyncResult().apply {
|
||||||
|
listOf(
|
||||||
|
async { folders = getFolders() },
|
||||||
|
async { feeds = getFeeds() },
|
||||||
|
async { items = getNewItems(syncData.lastModified, ItemQueryType.ALL) }
|
||||||
|
).awaitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getFolders() = service.getFolders()
|
||||||
|
|
||||||
|
suspend fun getFeeds() = service.getFeeds()
|
||||||
|
|
||||||
|
suspend fun getItems(type: Int, read: Boolean, batchSize: Int): List<Item> {
|
||||||
|
return service.getItems(type, read, batchSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getNewItems(lastModified: Long, itemQueryType: ItemQueryType): List<Item> {
|
||||||
|
return service.getNewItems(lastModified, itemQueryType.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun createFeed(url: String, folderId: Int?): List<Feed> {
|
||||||
|
return service.createFeed(url, folderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun changeFeedFolder(newFolderId: Int, feedId: Int) {
|
||||||
|
service.changeFeedFolder(feedId, mapOf("folderId" to newFolderId))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun renameFeed(name: String, folderId: Int) {
|
||||||
|
service.renameFeed(folderId, mapOf("feedTitle" to name))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteFeed(feedId: Int) {
|
||||||
|
service.deleteFeed(feedId)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun createFolder(name: String): List<Folder> {
|
||||||
|
return service.createFolder(mapOf("name" to name))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun renameFolder(name: String, folderId: Int) {
|
||||||
|
service.renameFolder(folderId, mapOf("name" to name))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteFolder(folderId: Int) {
|
||||||
|
service.deleteFolder(folderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setItemsReadState(syncData: NextcloudNewsSyncData) = with(syncData) {
|
||||||
|
if (unreadIds.isNotEmpty()) {
|
||||||
|
service.setReadState(
|
||||||
|
StateType.UNREAD.name.lowercase(),
|
||||||
|
mapOf("items" to unreadIds)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readIds.isNotEmpty()) {
|
||||||
|
service.setReadState(
|
||||||
|
StateType.READ.name.lowercase(),
|
||||||
|
mapOf("items" to readIds)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setItemsStarState(stateType: StateType, itemIds: List<StarItem>) {
|
||||||
|
if (itemIds.isNotEmpty()) {
|
||||||
|
val body = arrayListOf<Map<String, String>>()
|
||||||
|
|
||||||
|
for (item in itemIds) {
|
||||||
|
body += mapOf(
|
||||||
|
"feedId" to item.feedRemoteId,
|
||||||
|
"guidHash" to item.guidHash
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
service.setStarState(
|
||||||
|
stateType.name.lowercase(),
|
||||||
|
mapOf("items" to body)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ItemQueryType(val value: Int) {
|
||||||
|
ALL(3),
|
||||||
|
STARRED(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class StateType {
|
||||||
|
READ,
|
||||||
|
UNREAD,
|
||||||
|
STAR,
|
||||||
|
UNSTAR
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_ITEMS = 5000
|
||||||
|
private const val MAX_STARRED_ITEMS = 1000
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.readrops.api.services.nextcloudnews
|
||||||
|
|
||||||
|
import com.readrops.db.entities.Feed
|
||||||
|
import com.readrops.db.entities.Folder
|
||||||
|
import com.readrops.db.entities.Item
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.DELETE
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.PUT
|
||||||
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
interface NewNextcloudNewsService {
|
||||||
|
|
||||||
|
@GET("folders")
|
||||||
|
suspend fun getFolders(): List<Folder>
|
||||||
|
|
||||||
|
@GET("feeds")
|
||||||
|
suspend fun getFeeds(): List<Feed>
|
||||||
|
|
||||||
|
@GET("items")
|
||||||
|
suspend fun getItems(
|
||||||
|
@Query("type") type: Int,
|
||||||
|
@Query("getRead") read: Boolean,
|
||||||
|
@Query("batchSize") batchSize: Int
|
||||||
|
): List<Item>
|
||||||
|
|
||||||
|
@GET("items/updated")
|
||||||
|
suspend fun getNewItems(
|
||||||
|
@Query("lastModified") lastModified: Long,
|
||||||
|
@Query("type") type: Int
|
||||||
|
): List<Item>
|
||||||
|
|
||||||
|
@PUT("items/{stateType}/multiple")
|
||||||
|
@JvmSuppressWildcards
|
||||||
|
suspend fun setReadState(
|
||||||
|
@Path("stateType") stateType: String,
|
||||||
|
@Body itemIdsMap: Map<String, List<Int>>
|
||||||
|
)
|
||||||
|
|
||||||
|
@PUT("items/{starType}/multiple")
|
||||||
|
@JvmSuppressWildcards
|
||||||
|
suspend fun setStarState(
|
||||||
|
@Path("starType") starType: String?,
|
||||||
|
@Body body: Map<String?, List<Map<String, String>>>
|
||||||
|
)
|
||||||
|
|
||||||
|
@POST("feeds")
|
||||||
|
suspend fun createFeed(@Query("url") url: String, @Query("folderId") folderId: Int?): List<Feed>
|
||||||
|
|
||||||
|
@DELETE("feeds/{feedId}")
|
||||||
|
suspend fun deleteFeed(@Path("feedId") feedId: Int)
|
||||||
|
|
||||||
|
@PUT("feeds/{feedId}/move")
|
||||||
|
suspend fun changeFeedFolder(@Path("feedId") feedId: Int, @Body folderIdMap: Map<String, Int>)
|
||||||
|
|
||||||
|
@PUT("feeds/{feedId}/rename")
|
||||||
|
suspend fun renameFeed(@Path("feedId") feedId: Int, @Body feedTitleMap: Map<String, String>)
|
||||||
|
|
||||||
|
@POST("folders")
|
||||||
|
suspend fun createFolder(@Body folderName: Map<String, String>): List<Folder>
|
||||||
|
|
||||||
|
@DELETE("folders/{folderId}")
|
||||||
|
suspend fun deleteFolder(@Path("folderId") folderId: Int)
|
||||||
|
|
||||||
|
@PUT("folders/{folderId}")
|
||||||
|
suspend fun renameFolder(@Path("folderId") folderId: Int, @Body folderName: Map<String, String>)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val END_POINT = "/index.php/apps/news/api/v1-2/"
|
||||||
|
}
|
||||||
|
}
|
|
@ -72,7 +72,7 @@ public class NextNewsDataSource {
|
||||||
return response.body();
|
return response.body();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SyncResult sync(@NonNull SyncType syncType, @Nullable NextNewsSyncData data) throws IOException {
|
public SyncResult sync(@NonNull SyncType syncType, @Nullable NextcloudNewsSyncData data) throws IOException {
|
||||||
SyncResult syncResult = new SyncResult();
|
SyncResult syncResult = new SyncResult();
|
||||||
switch (syncType) {
|
switch (syncType) {
|
||||||
case INITIAL_SYNC:
|
case INITIAL_SYNC:
|
||||||
|
@ -113,7 +113,7 @@ public class NextNewsDataSource {
|
||||||
syncResult.setStarredItems(starredItems);
|
syncResult.setStarredItems(starredItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void classicSync(SyncResult syncResult, NextNewsSyncData data) throws IOException {
|
private void classicSync(SyncResult syncResult, NextcloudNewsSyncData data) throws IOException {
|
||||||
putModifiedItems(data, syncResult);
|
putModifiedItems(data, syncResult);
|
||||||
getFeedsAndFolders(syncResult);
|
getFeedsAndFolders(syncResult);
|
||||||
|
|
||||||
|
@ -148,12 +148,12 @@ public class NextNewsDataSource {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putModifiedItems(NextNewsSyncData data, SyncResult syncResult) throws IOException {
|
private void putModifiedItems(NextcloudNewsSyncData data, SyncResult syncResult) throws IOException {
|
||||||
setReadState(data.getReadItems(), syncResult, StateType.READ);
|
/*setReadState(data.getReadIds(), syncResult, StateType.READ);
|
||||||
setReadState(data.getUnreadItems(), syncResult, StateType.UNREAD);
|
setReadState(data.getUnreadIds(), syncResult, StateType.UNREAD);
|
||||||
|
|
||||||
setStarState(data.getStarredItems(), syncResult, StateType.STAR);
|
setStarState(data.getStarredIds(), syncResult, StateType.STAR);
|
||||||
setStarState(data.getUnstarredItems(), syncResult, StateType.UNSTAR);
|
setStarState(data.getUnstarredIds(), syncResult, StateType.UNSTAR);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Folder> createFolder(Folder folder) throws IOException, UnknownFormatException, ConflictException {
|
public List<Folder> createFolder(Folder folder) throws IOException, UnknownFormatException, ConflictException {
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package com.readrops.api.services.nextcloudnews
|
|
||||||
|
|
||||||
import com.readrops.db.pojo.StarItem
|
|
||||||
|
|
||||||
data class NextNewsSyncData(
|
|
||||||
var lastModified: Long = 0,
|
|
||||||
var unreadItems: List<String> = listOf(),
|
|
||||||
var readItems: List<String> = listOf(),
|
|
||||||
var starredItems: List<StarItem> = listOf(),
|
|
||||||
var unstarredItems: List<StarItem> = listOf(),
|
|
||||||
)
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.readrops.api.services.nextcloudnews
|
||||||
|
|
||||||
|
import com.readrops.db.pojo.StarItem
|
||||||
|
|
||||||
|
data class NextcloudNewsSyncData(
|
||||||
|
val lastModified: Long = 0,
|
||||||
|
val readIds: List<Int> = listOf(),
|
||||||
|
val unreadIds: List<Int> = listOf(),
|
||||||
|
val starredIds: List<StarItem> = listOf(),
|
||||||
|
val unstarredIds: List<StarItem> = listOf(),
|
||||||
|
)
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.readrops.api
|
||||||
|
|
||||||
|
import okhttp3.mockwebserver.MockResponse
|
||||||
|
import okhttp3.mockwebserver.MockWebServer
|
||||||
|
import okio.Buffer
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
|
||||||
|
fun MockWebServer.enqueueOK() {
|
||||||
|
enqueue(MockResponse()
|
||||||
|
.setResponseCode(HttpURLConnection.HTTP_OK)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MockWebServer.enqueueStream(stream: InputStream) {
|
||||||
|
enqueue(MockResponse()
|
||||||
|
.setResponseCode(HttpURLConnection.HTTP_OK)
|
||||||
|
.setBody(Buffer().readFrom(stream)))
|
||||||
|
}
|
|
@ -0,0 +1,306 @@
|
||||||
|
package com.readrops.api.services.nextcloudnews
|
||||||
|
|
||||||
|
import com.readrops.api.TestUtils
|
||||||
|
import com.readrops.api.apiModule
|
||||||
|
import com.readrops.api.enqueueOK
|
||||||
|
import com.readrops.api.enqueueStream
|
||||||
|
import com.readrops.api.services.nextcloudnews.NextNewsDataSource.ItemQueryType
|
||||||
|
import com.readrops.db.entities.account.Account
|
||||||
|
import com.readrops.db.pojo.StarItem
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import com.squareup.moshi.Types
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import okhttp3.mockwebserver.MockWebServer
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.koin.core.qualifier.named
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import org.koin.test.KoinTest
|
||||||
|
import org.koin.test.KoinTestRule
|
||||||
|
import org.koin.test.get
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class NextcloudNewsDataSourceTest : KoinTest {
|
||||||
|
|
||||||
|
private lateinit var nextcloudNewsDataSource: NewNextcloudNewsDataSource
|
||||||
|
private val mockServer = MockWebServer()
|
||||||
|
private val moshi = Moshi.Builder()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val koinTestRule = KoinTestRule.create {
|
||||||
|
modules(apiModule, module {
|
||||||
|
single {
|
||||||
|
Retrofit.Builder()
|
||||||
|
.baseUrl("http://localhost:8080/")
|
||||||
|
.client(get())
|
||||||
|
.addConverterFactory(MoshiConverterFactory.create(get(named("nextcloudNewsMoshi"))))
|
||||||
|
.build()
|
||||||
|
.create(NewNextcloudNewsService::class.java)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun before() {
|
||||||
|
mockServer.start(8080)
|
||||||
|
nextcloudNewsDataSource = NewNextcloudNewsDataSource(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mockServer.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loginTest() = runTest {
|
||||||
|
val stream = TestUtils.loadResource("services/nextcloudnews/user.xml")
|
||||||
|
val account = Account(login = "login", url = mockServer.url("").toString())
|
||||||
|
|
||||||
|
mockServer.enqueueStream(stream)
|
||||||
|
|
||||||
|
val displayName = nextcloudNewsDataSource.login(get(), account)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
assertTrue { displayName == "Shinokuni" }
|
||||||
|
assertTrue { request.headers.contains("OCS-APIRequest" to "true") }
|
||||||
|
assertTrue { request.path == "//ocs/v1.php/cloud/users/login" }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun foldersTest() = runTest {
|
||||||
|
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/valid_folder.json")
|
||||||
|
mockServer.enqueueStream(stream)
|
||||||
|
|
||||||
|
val folders = nextcloudNewsDataSource.getFolders()
|
||||||
|
assertTrue { folders.size == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun feedsTest() = runTest {
|
||||||
|
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/feeds.json")
|
||||||
|
mockServer.enqueueStream(stream)
|
||||||
|
|
||||||
|
val feeds = nextcloudNewsDataSource.getFeeds()
|
||||||
|
assertTrue { feeds.size == 3 }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun itemsTest() = runTest {
|
||||||
|
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/items.json")
|
||||||
|
mockServer.enqueueStream(stream)
|
||||||
|
|
||||||
|
val items = nextcloudNewsDataSource.getItems(ItemQueryType.ALL.value, false, 10)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
assertTrue { items.size == 3 }
|
||||||
|
with(request.requestUrl!!) {
|
||||||
|
assertEquals("3", queryParameter("type"))
|
||||||
|
assertEquals("false", queryParameter("getRead"))
|
||||||
|
assertEquals("10", queryParameter("batchSize"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newItemsTest() = runTest {
|
||||||
|
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/items.json")
|
||||||
|
mockServer.enqueueStream(stream)
|
||||||
|
|
||||||
|
val items =
|
||||||
|
nextcloudNewsDataSource.getNewItems(1512, NewNextcloudNewsDataSource.ItemQueryType.ALL)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
assertTrue { items.size == 3 }
|
||||||
|
with(request.requestUrl!!) {
|
||||||
|
assertEquals("1512", queryParameter("lastModified"))
|
||||||
|
assertEquals("3", queryParameter("type"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createFeedTest() = runTest {
|
||||||
|
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/feeds.json")
|
||||||
|
mockServer.enqueueStream(stream)
|
||||||
|
|
||||||
|
val feeds = nextcloudNewsDataSource.createFeed("https://news.ycombinator.com/rss", null)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
assertTrue { feeds.isNotEmpty() }
|
||||||
|
with(request.requestUrl!!) {
|
||||||
|
assertEquals("https://news.ycombinator.com/rss", queryParameter("url"))
|
||||||
|
assertEquals(null, queryParameter("folderId"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deleteFeedTest() = runTest {
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
|
||||||
|
nextcloudNewsDataSource.deleteFeed(15)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
assertTrue { request.path!!.endsWith("/15") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun changeFeedFolderTest() = runTest {
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
|
||||||
|
nextcloudNewsDataSource.changeFeedFolder(15, 18)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
val type =
|
||||||
|
Types.newParameterizedType(
|
||||||
|
Map::class.java,
|
||||||
|
String::class.java,
|
||||||
|
Int::class.javaObjectType
|
||||||
|
)
|
||||||
|
val adapter = moshi.adapter<Map<String, Int>>(type)
|
||||||
|
val body = adapter.fromJson(request.body)!!
|
||||||
|
|
||||||
|
assertTrue { request.path!!.endsWith("/18/move") }
|
||||||
|
assertEquals(15, body["folderId"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameFeedTest() = runTest {
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
|
||||||
|
nextcloudNewsDataSource.renameFeed("name", 15)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
val type =
|
||||||
|
Types.newParameterizedType(Map::class.java, String::class.java, String::class.java)
|
||||||
|
val adapter = moshi.adapter<Map<String, String>>(type)
|
||||||
|
val body = adapter.fromJson(request.body)!!
|
||||||
|
|
||||||
|
assertTrue { request.path!!.endsWith("/15/rename") }
|
||||||
|
assertEquals("name", body["feedTitle"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createFolderTest() = runTest {
|
||||||
|
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/valid_folder.json")
|
||||||
|
mockServer.enqueueStream(stream)
|
||||||
|
|
||||||
|
val folders = nextcloudNewsDataSource.createFolder("folder name")
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
val type =
|
||||||
|
Types.newParameterizedType(Map::class.java, String::class.java, String::class.java)
|
||||||
|
val adapter = moshi.adapter<Map<String, String>>(type)
|
||||||
|
val body = adapter.fromJson(request.body)!!
|
||||||
|
|
||||||
|
assertTrue { folders.size == 1 }
|
||||||
|
assertEquals("folder name", body["name"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameFolderTest() = runTest {
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
|
||||||
|
nextcloudNewsDataSource.renameFolder("new name", 15)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
val type =
|
||||||
|
Types.newParameterizedType(Map::class.java, String::class.java, String::class.java)
|
||||||
|
val adapter = moshi.adapter<Map<String, String>>(type)
|
||||||
|
val body = adapter.fromJson(request.body)!!
|
||||||
|
|
||||||
|
assertTrue { request.path!!.endsWith("/15") }
|
||||||
|
assertEquals("new name", body["name"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deleteFolderTest() = runTest {
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
|
||||||
|
nextcloudNewsDataSource.deleteFolder(15)
|
||||||
|
val request = mockServer.takeRequest()
|
||||||
|
|
||||||
|
assertEquals(request.method, "DELETE")
|
||||||
|
assertTrue { request.path!!.endsWith("/15") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setItemsReadStateTest() = runTest {
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
|
||||||
|
val data = NextcloudNewsSyncData(
|
||||||
|
readIds = listOf(15, 16, 17),
|
||||||
|
unreadIds = listOf(18, 19, 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
nextcloudNewsDataSource.setItemsReadState(data)
|
||||||
|
val unreadRequest = mockServer.takeRequest()
|
||||||
|
val readRequest = mockServer.takeRequest()
|
||||||
|
|
||||||
|
val type =
|
||||||
|
Types.newParameterizedType(
|
||||||
|
Map::class.java,
|
||||||
|
String::class.java,
|
||||||
|
Types.newParameterizedType(List::class.java, Int::class.javaObjectType)
|
||||||
|
)
|
||||||
|
val adapter = moshi.adapter<Map<String, List<Int>>>(type)
|
||||||
|
val unreadBody = adapter.fromJson(unreadRequest.body)!!
|
||||||
|
val readBody = adapter.fromJson(readRequest.body)!!
|
||||||
|
|
||||||
|
assertEquals(data.readIds, readBody["items"])
|
||||||
|
assertEquals(data.unreadIds, unreadBody["items"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setItemsStarStateTest() = runTest {
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
mockServer.enqueueOK()
|
||||||
|
|
||||||
|
val starList = listOf(
|
||||||
|
StarItem("remote1", "guid1"),
|
||||||
|
StarItem("remote2", "guid2")
|
||||||
|
)
|
||||||
|
nextcloudNewsDataSource.setItemsStarState(
|
||||||
|
NewNextcloudNewsDataSource.StateType.STAR,
|
||||||
|
starList
|
||||||
|
)
|
||||||
|
|
||||||
|
val starRequest = mockServer.takeRequest()
|
||||||
|
|
||||||
|
val unstarList = listOf(
|
||||||
|
StarItem("remote3", "guid3"),
|
||||||
|
StarItem("remote4", "guid4")
|
||||||
|
)
|
||||||
|
nextcloudNewsDataSource.setItemsStarState(
|
||||||
|
NewNextcloudNewsDataSource.StateType.UNSTAR,
|
||||||
|
unstarList
|
||||||
|
)
|
||||||
|
|
||||||
|
val unstarRequest = mockServer.takeRequest()
|
||||||
|
|
||||||
|
val type =
|
||||||
|
Types.newParameterizedType(
|
||||||
|
Map::class.java,
|
||||||
|
String::class.java,
|
||||||
|
Types.newParameterizedType(
|
||||||
|
List::class.java,
|
||||||
|
Types.newParameterizedType(
|
||||||
|
Map::class.java,
|
||||||
|
String::class.java,
|
||||||
|
String::class.java
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val adapter = moshi.adapter<Map<String, List<Map<String, String>>>>(type)
|
||||||
|
|
||||||
|
val starBody = adapter.fromJson(starRequest.body)!!
|
||||||
|
val unstarBody = adapter.fromJson(unstarRequest.body)!!
|
||||||
|
|
||||||
|
assertEquals(starList[0].feedRemoteId, starBody.values.first().first()["feedId"])
|
||||||
|
assertEquals(unstarList[0].feedRemoteId, unstarBody.values.first().first()["feedId"])
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ import androidx.annotation.Nullable;
|
||||||
import com.readrops.api.services.SyncResult;
|
import com.readrops.api.services.SyncResult;
|
||||||
import com.readrops.api.services.SyncType;
|
import com.readrops.api.services.SyncType;
|
||||||
import com.readrops.api.services.nextcloudnews.NextNewsDataSource;
|
import com.readrops.api.services.nextcloudnews.NextNewsDataSource;
|
||||||
import com.readrops.api.services.nextcloudnews.NextNewsSyncData;
|
import com.readrops.api.services.nextcloudnews.NextcloudNewsSyncData;
|
||||||
import com.readrops.api.utils.exceptions.UnknownFormatException;
|
import com.readrops.api.utils.exceptions.UnknownFormatException;
|
||||||
import com.readrops.app.addfeed.FeedInsertionResult;
|
import com.readrops.app.addfeed.FeedInsertionResult;
|
||||||
import com.readrops.app.addfeed.ParsingResult;
|
import com.readrops.app.addfeed.ParsingResult;
|
||||||
|
@ -86,21 +86,21 @@ public class NextNewsRepository extends ARepository {
|
||||||
syncType = SyncType.INITIAL_SYNC;
|
syncType = SyncType.INITIAL_SYNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
NextNewsSyncData syncData = new NextNewsSyncData();
|
NextcloudNewsSyncData syncData = new NextcloudNewsSyncData();
|
||||||
|
|
||||||
if (syncType == SyncType.CLASSIC_SYNC) {
|
/*if (syncType == SyncType.CLASSIC_SYNC) {
|
||||||
syncData.setLastModified(account.getLastModified() / 1000L);
|
syncData.setLastModified(account.getLastModified() / 1000L);
|
||||||
|
|
||||||
List<ItemReadStarState> itemStateChanges = database
|
List<ItemReadStarState> itemStateChanges = database
|
||||||
.itemStateChangesDao()
|
.itemStateChangesDao()
|
||||||
.getNextcloudNewsStateChanges(account.getId());
|
.getNextcloudNewsStateChanges(account.getId());
|
||||||
|
|
||||||
syncData.setReadItems(itemStateChanges.stream()
|
syncData.setReadIds(itemStateChanges.stream()
|
||||||
.filter(it -> it.getReadChange() && it.getRead())
|
.filter(it -> it.getReadChange() && it.getRead())
|
||||||
.map(ItemReadStarState::getRemoteId)
|
.map(ItemReadStarState::getRemoteId)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
|
||||||
syncData.setUnreadItems(itemStateChanges.stream()
|
syncData.setUnreadIds(itemStateChanges.stream()
|
||||||
.filter(it -> it.getReadChange() && !it.getRead())
|
.filter(it -> it.getReadChange() && !it.getRead())
|
||||||
.map(ItemReadStarState::getRemoteId)
|
.map(ItemReadStarState::getRemoteId)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
@ -111,7 +111,7 @@ public class NextNewsRepository extends ARepository {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (!starredItemsIds.isEmpty()) {
|
if (!starredItemsIds.isEmpty()) {
|
||||||
syncData.setStarredItems(database.itemDao().getStarChanges(starredItemsIds, account.getId()));
|
syncData.setStarredIds(database.itemDao().getStarChanges(starredItemsIds, account.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> unstarredItemsIds = itemStateChanges.stream()
|
List<String> unstarredItemsIds = itemStateChanges.stream()
|
||||||
|
@ -120,10 +120,10 @@ public class NextNewsRepository extends ARepository {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (!unstarredItemsIds.isEmpty()) {
|
if (!unstarredItemsIds.isEmpty()) {
|
||||||
syncData.setUnstarredItems(database.itemDao().getStarChanges(unstarredItemsIds, account.getId()));
|
syncData.setUnstarredIds(database.itemDao().getStarChanges(unstarredItemsIds, account.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}*/
|
||||||
|
|
||||||
TimingLogger timings = new TimingLogger(TAG, "nextcloud news " + syncType.name().toLowerCase());
|
TimingLogger timings = new TimingLogger(TAG, "nextcloud news " + syncType.name().toLowerCase());
|
||||||
SyncResult result = dataSource.sync(syncType, syncData);
|
SyncResult result = dataSource.sync(syncType, syncData);
|
||||||
|
|
Loading…
Reference in New Issue