Commit 5c2ace00 authored by Ricki Hirner's avatar Ricki Hirner

Refactor API, especially DavResponse

parent 720c01db
......@@ -33,7 +33,8 @@ android {
buildToolsVersion '27.0.3'
defaultConfig {
minSdkVersion 14
//noinspection MinSdkTooLow
minSdkVersion 9 // Android 2.3
targetSdkVersion 27
buildConfigField "String", "version_okhttp", "\"$okhttp_version\""
......@@ -58,6 +59,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
implementation "commons-io:commons-io:2.6"
androidTestImplementation "com.squareup.okhttp3:mockwebserver:$okhttp_version"
androidTestImplementation 'com.android.support.test:runner:1.0.2'
......
......@@ -94,25 +94,25 @@ class DavCollectionTest {
" <D:sync-token>http://example.com/ns/sync/1234</D:sync-token>\n" +
" </D:multistatus>")
)
val changes = collection.reportChanges(null, false, null, GetETag.NAME)
assertEquals(3, changes.members.size)
val members = changes.members.iterator()
val member1 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("test.doc").build(), member1.url)
assertEquals("00001-abcd1", member1[GetETag::class.java]!!.eTag)
val member2 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("vcard.vcf").build(), member2.url)
assertEquals("00002-abcd1", member2[GetETag::class.java]!!.eTag)
val member3 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("calendar.ics").build(), member3.url)
assertEquals("00003-abcd1", member3[GetETag::class.java]!!.eTag)
assertEquals(0, changes.removedMembers.size)
assertFalse(changes.furtherResults)
assertEquals("http://example.com/ns/sync/1234", changes.syncToken!!.token)
collection.reportChanges(null, false, null, GetETag.NAME).use { changes ->
assertEquals(3, changes.members.size)
val members = changes.members.iterator()
val member1 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("test.doc").build(), member1.url)
assertEquals("00001-abcd1", member1[GetETag::class.java]!!.eTag)
val member2 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("vcard.vcf").build(), member2.url)
assertEquals("00002-abcd1", member2[GetETag::class.java]!!.eTag)
val member3 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("calendar.ics").build(), member3.url)
assertEquals("00003-abcd1", member3[GetETag::class.java]!!.eTag)
assertEquals(0, changes.removedMembers.size)
assertFalse(changes.furtherResults)
assertEquals("http://example.com/ns/sync/1234", changes.syncToken!!.token)
}
}
/**
......@@ -158,24 +158,24 @@ class DavCollectionTest {
" <D:sync-token>http://example.com/ns/sync/1233</D:sync-token>\n" +
" </D:multistatus>")
)
val changes = collection.reportChanges(null, false, null, GetETag.NAME)
assertEquals(2, changes.members.size)
val members = changes.members.iterator()
val member1 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("test.doc").build(), member1.url)
assertEquals("00001-abcd1", member1[GetETag::class.java]!!.eTag)
val member2 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("vcard.vcf").build(), member2.url)
assertEquals("00002-abcd1", member2[GetETag::class.java]!!.eTag)
assertEquals(1, changes.removedMembers.size)
val removedMember = changes.removedMembers.first()
assertEquals(sampleUrl().newBuilder().addPathSegment("removed.txt").build(), removedMember.url)
assertTrue(changes.furtherResults)
assertEquals("http://example.com/ns/sync/1233", changes.syncToken!!.token)
collection.reportChanges(null, false, null, GetETag.NAME).use { changes ->
assertEquals(2, changes.members.size)
val members = changes.members.iterator()
val member1 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("test.doc").build(), member1.url)
assertEquals("00001-abcd1", member1[GetETag::class.java]!!.eTag)
val member2 = members.next()
assertEquals(sampleUrl().newBuilder().addPathSegment("vcard.vcf").build(), member2.url)
assertEquals("00002-abcd1", member2[GetETag::class.java]!!.eTag)
assertEquals(1, changes.removedMembers.size)
val removedMember = changes.removedMembers.first()
assertEquals(sampleUrl().newBuilder().addPathSegment("removed.txt").build(), removedMember.url)
assertTrue(changes.furtherResults)
assertEquals("http://example.com/ns/sync/1233", changes.syncToken!!.token)
}
}
/**
......@@ -196,13 +196,11 @@ class DavCollectionTest {
)
try {
collection.reportChanges("http://example.com/ns/sync/1232", false, 100, GetETag.NAME)
collection.reportChanges("http://example.com/ns/sync/1232", false, 100, GetETag.NAME).close()
fail("Expected HttpException")
} catch (e: HttpException) {
assertEquals(507, e.status)
return
}
fail()
}
}
\ No newline at end of file
......@@ -10,7 +10,6 @@ package at.bitfire.dav4android
import at.bitfire.dav4android.exception.DavException
import at.bitfire.dav4android.exception.HttpException
import at.bitfire.dav4android.exception.InvalidDavResponseException
import okhttp3.*
import java.io.IOException
import java.io.StringWriter
......@@ -62,14 +61,10 @@ class DavAddressBook @JvmOverloads constructor(
.header("Depth", "1")
.build()).execute()
checkStatus(response, false)
checkStatus(response)
assertMultiStatus(response)
response.body()?.charStream()?.use {
return processMultiStatus(it)
}
throw InvalidDavResponseException("Didn't receive 207 Multi-status response on REPORT addressbook-queryys")
return processMultiStatus(response.body()?.charStream()!!)
}
/**
......@@ -117,14 +112,10 @@ class DavAddressBook @JvmOverloads constructor(
.header("Depth", "0") // "The request MUST include a Depth: 0 header [...]"
.build()).execute()
checkStatus(response, false)
checkStatus(response)
assertMultiStatus(response)
response.body()?.charStream()?.use {
return processMultiStatus(it)
}
throw InvalidDavResponseException("Didn't receive 207 Multi-status response on REPORT addressbook-multiget")
return processMultiStatus(response.body()?.charStream()!!)
}
}
......@@ -10,7 +10,6 @@ package at.bitfire.dav4android
import at.bitfire.dav4android.exception.DavException
import at.bitfire.dav4android.exception.HttpException
import at.bitfire.dav4android.exception.InvalidDavResponseException
import okhttp3.*
import java.io.IOException
import java.io.StringWriter
......@@ -89,14 +88,10 @@ class DavCalendar @JvmOverloads constructor(
.header("Depth", "1")
.build()).execute()
checkStatus(response, false)
checkStatus(response)
assertMultiStatus(response)
response.body()?.charStream()?.use {
return processMultiStatus(it)
}
throw InvalidDavResponseException("Didn't receive 207 Multi-status response on REPORT calendar-query")
return processMultiStatus(response.body()?.charStream()!!)
}
/**
......@@ -138,14 +133,10 @@ class DavCalendar @JvmOverloads constructor(
.method("REPORT", RequestBody.create(MIME_XML, writer.toString()))
.build()).execute()
checkStatus(response, false)
checkStatus(response)
assertMultiStatus(response)
response.body()?.charStream()?.use {
return processMultiStatus(it)
}
throw InvalidDavResponseException("Didn't receive 207 Multi-status response on REPORT calendar-multiget")
return processMultiStatus(response.body()?.charStream()!!)
}
}
......@@ -10,7 +10,6 @@ package at.bitfire.dav4android
import at.bitfire.dav4android.exception.DavException
import at.bitfire.dav4android.exception.HttpException
import at.bitfire.dav4android.exception.InvalidDavResponseException
import at.bitfire.dav4android.property.SyncToken
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
......@@ -83,14 +82,10 @@ open class DavCollection @JvmOverloads constructor(
.header("Depth", "0")
.build()).execute()
checkStatus(response, false)
checkStatus(response)
assertMultiStatus(response)
response.body()?.charStream()?.use {
return processMultiStatus(it)
}
throw InvalidDavResponseException("Didn't receive 207 Multi-status response on REPORT sync-collection")
return processMultiStatus(response.body()?.charStream()!!)
}
}
\ No newline at end of file
......@@ -2,6 +2,9 @@ package at.bitfire.dav4android
import at.bitfire.dav4android.property.SyncToken
import okhttp3.HttpUrl
import okhttp3.Response
import okhttp3.ResponseBody
import java.io.Closeable
/**
* Immutable container for a WebDAV multistatus response. Note that property elements
......@@ -12,6 +15,9 @@ class DavResponse private constructor(
/** resource this response is about */
val url: HttpUrl,
/** corresponding HTTP response object */
val body: ResponseBody?,
/** HTTP capabilities reported by an OPTIONS response */
val capabilities: Set<String>,
......@@ -33,7 +39,15 @@ class DavResponse private constructor(
/** whether further results are available (requested collection had HTTP status 507) */
val furtherResults: Boolean
) {
): Closeable {
/**
* After closing this response, the [body] will not be usable anymore, but other properties
* can be used normally.
*/
override fun close() {
body?.close()
}
/**
* Convenience method to get a certain property from the current response. Does't take
......@@ -43,11 +57,48 @@ class DavResponse private constructor(
return properties.filterIsInstance(clazz).firstOrNull()
}
/**
* Recursively searches for a property, i.e. in this object and in
* [members] and [related] and returns the first occurrence with its source.
*/
fun<T: Property> searchProperty(clazz: Class<T>): Pair<DavResponse, T>? {
get(clazz)?.let { return Pair(this, it) }
members.forEach { response ->
response[clazz]?.let { return Pair(response, it) }
}
related.forEach { response ->
response[clazz]?.let { return Pair(response, it) }
}
return null
}
/**
* Recursively (i.e. in this object, and in [members] and [related]) searches for a
* property, and returns all occurrences together with their sources.
*/
fun<T: Property> searchProperties(clazz: Class<T>): Map<DavResponse, T> {
val map = mutableMapOf<DavResponse, T>()
get(clazz)?.let { map[this] = it }
members.forEach { response ->
response[clazz]?.let { map[response] = it }
}
related.forEach { response ->
response[clazz]?.let { map[response] = it }
}
return map
}
class Builder(
val url: HttpUrl
) {
private var responseBody: ResponseBody? = null
fun responseBody(newValue: ResponseBody?): Builder {
responseBody = newValue
return this
}
private var capabilities: Set<String> = setOf()
fun capabilities(newValue: Set<String>): Builder {
capabilities = newValue
......@@ -92,6 +143,7 @@ class DavResponse private constructor(
fun build(): DavResponse = DavResponse(
url,
responseBody,
capabilities,
properties,
members.map { it.build() },
......
......@@ -11,6 +11,7 @@ package at.bitfire.dav4android.exception
import at.bitfire.dav4android.Constants
import okhttp3.Response
import okio.Buffer
import org.apache.commons.io.IOUtils
import java.io.*
open class HttpException: Exception, Serializable {
......@@ -37,6 +38,10 @@ open class HttpException: Exception, Serializable {
response = null
}
constructor(message: String, response: Response): this(message) {
// TODO
}
/**
* Brings [response] into an readable format. Reads and closes the [response] body.
*/
......
/*
* Copyright © Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.dav4android.exception
class InvalidDavResponseException @JvmOverloads constructor(
message: String,
ex: Throwable? = null
): DavException(message, ex)
\ No newline at end of file
Markdown is supported
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