Skip to content
This repository was archived by the owner on Jul 29, 2022. It is now read-only.

Add new functions that use HttpRequest and Try to eventually replace Fuel and Promises #55

Merged
merged 3 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ All notable changes to this project will be documented in this file.

**Warning:** Features marked as *experimental* may change or be removed in a future release without notice. Use with caution.

<!-- ## [Unreleased] -->
## [Unreleased]

### Added

* New APIs using coroutines and R2's `HttpClient` instead of Fuel and kovenant (contributed by [@stevenzeck](https://github.com/readium/r2-opds-kotlin/pull/55)).

## [2.0.0]

Expand Down
3 changes: 1 addition & 2 deletions r2-opds/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ dependencies {
}

implementation "androidx.appcompat:appcompat:1.3.0-beta1"
implementation "com.github.kittinunf.fuel:fuel-android:2.2.2"
implementation "com.github.kittinunf.fuel:fuel:2.2.2"
implementation "com.jakewharton.timber:timber:4.7.1"
implementation "joda-time:joda-time:2.10.5"
implementation "nl.komponents.kovenant:kovenant:3.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"

testImplementation "junit:junit:4.13.2"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
Expand Down
18 changes: 18 additions & 0 deletions r2-opds/src/main/java/org/readium/r2/opds/Extensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2021 Readium Foundation. All rights reserved.
* Use of this source code is governed by the BSD-style license
* available in the top-level LICENSE file of the project.
*/

package org.readium.r2.opds

import kotlinx.coroutines.runBlocking
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.task
import org.readium.r2.shared.util.http.HttpClient
import org.readium.r2.shared.util.http.HttpFetchResponse
import org.readium.r2.shared.util.http.HttpRequest

internal fun HttpClient.fetchPromise(request: HttpRequest): Promise<HttpFetchResponse, Exception> {
return task { runBlocking { fetch(request).getOrThrow() } }
}
105 changes: 94 additions & 11 deletions r2-opds/src/main/java/org/readium/r2/opds/OPDS1Parser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

package org.readium.r2.opds

import com.github.kittinunf.fuel.Fuel
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.then
import org.joda.time.DateTime
Expand All @@ -18,10 +17,14 @@ import org.readium.r2.shared.extensions.toMap
import org.readium.r2.shared.opds.*
import org.readium.r2.shared.parser.xml.ElementNode
import org.readium.r2.shared.parser.xml.XmlParser
import org.readium.r2.shared.promise
import org.readium.r2.shared.publication.*
import org.readium.r2.shared.toJSON
import org.readium.r2.shared.util.Href
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.http.DefaultHttpClient
import org.readium.r2.shared.util.http.HttpClient
import org.readium.r2.shared.util.http.HttpRequest
import org.readium.r2.shared.util.http.fetchWithDecoder
import java.net.URL

enum class OPDSParserError {
Expand All @@ -45,18 +48,38 @@ object Namespaces {
class OPDS1Parser {
companion object {

suspend fun parseUrlString(url: String, client: HttpClient = DefaultHttpClient()): Try<ParseData, Exception> {
return client.fetchWithDecoder(HttpRequest(url)) {
this.parse(it.body, URL(url))
}
}

suspend fun parseRequest(request: HttpRequest, client: HttpClient = DefaultHttpClient()): Try<ParseData, Exception> {
return client.fetchWithDecoder(request) {
this.parse(it.body, URL(request.url))
}
}

@Deprecated(
"Use `parseRequest` or `parseUrlString` with coroutines instead",
ReplaceWith("OPDS1Parser.parseUrlString(url)"),
DeprecationLevel.WARNING
)
fun parseURL(url: URL): Promise<ParseData, Exception> {
return Fuel.get(url.toString(), null).promise() then {
val (_, _, result) = it
this.parse(xmlData = result, url = url)
return DefaultHttpClient().fetchPromise(HttpRequest(url.toString())) then {
this.parse(xmlData = it.body, url = url)
}
}

@Deprecated(
"Use `parseRequest` or `parseUrlString` with coroutines instead",
ReplaceWith("OPDS1Parser.parseUrlString(url)"),
DeprecationLevel.WARNING
)
@Suppress("unused")
fun parseURL(headers: MutableMap<String,String>, url: URL): Promise<ParseData, Exception> {
return Fuel.get(url.toString(), null).header(headers).promise() then {
val (_, _, result) = it
this.parse(xmlData = result, url = url)
return DefaultHttpClient().fetchPromise(HttpRequest(url = url.toString(), headers = headers)) then {
this.parse(xmlData = it.body, url = url)
}
}

Expand Down Expand Up @@ -178,6 +201,67 @@ class OPDS1Parser {
return MimeTypeParameters(type = type, parameters = params)
}

@Suppress("unused")
suspend fun retrieveOpenSearchTemplate(feed: Feed): Try<String?, Exception> {

var openSearchURL: URL? = null
var selfMimeType: String? = null

for (link in feed.links) {
if (link.rels.contains("self")) {
if (link.type != null) {
selfMimeType = link.type
}
} else if (link.rels.contains("search")) {
openSearchURL = URL(link.href)
}
}

val unwrappedURL = openSearchURL?.let {
return@let it
}

return DefaultHttpClient().fetchWithDecoder(HttpRequest(unwrappedURL.toString())) {

val document = XmlParser().parse(it.body.inputStream())

val urls = document.get("Url", Namespaces.Search)

var typeAndProfileMatch: ElementNode? = null
var typeMatch: ElementNode? = null

selfMimeType?.let { s ->

val selfMimeParams = parseMimeType(mimeTypeString = s)
for (url in urls) {
val urlMimeType = url.getAttr("type") ?: continue
val otherMimeParams = parseMimeType(mimeTypeString = urlMimeType)
if (selfMimeParams.type == otherMimeParams.type) {
if (typeMatch == null) {
typeMatch = url
}
if (selfMimeParams.parameters["profile"] == otherMimeParams.parameters["profile"]) {
typeAndProfileMatch = url
break
}
}
}
val match = typeAndProfileMatch ?: (typeMatch ?: urls[0])
val template = match.getAttr("template")

template

}
null
}

}

@Deprecated(
"Use `retrieveOpenSearchTemplate` with coroutines instead",
ReplaceWith("OPDS1Parser.retrieveOpenSearchTemplate(feed)"),
DeprecationLevel.WARNING
)
@Suppress("unused")
fun fetchOpenSearchTemplate(feed: Feed): Promise<String?, Exception> {

Expand All @@ -198,10 +282,9 @@ class OPDS1Parser {
return@let it
}

return Fuel.get(unwrappedURL.toString(), null).promise() then {
val (_, _, result) = it
return DefaultHttpClient().fetchPromise(HttpRequest(unwrappedURL.toString())) then {

val document = XmlParser().parse(result.inputStream())
val document = XmlParser().parse(it.body.inputStream())

val urls = document.get("Url", Namespaces.Search)

Expand Down
41 changes: 32 additions & 9 deletions r2-opds/src/main/java/org/readium/r2/opds/OPDS2Parser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@

package org.readium.r2.opds

import com.github.kittinunf.fuel.Fuel
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.then
import org.joda.time.DateTime
import org.json.JSONArray
import org.json.JSONObject
import org.readium.r2.shared.opds.*
import org.readium.r2.shared.promise
import org.readium.r2.shared.publication.Link
import org.readium.r2.shared.publication.Manifest
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.http.DefaultHttpClient
import org.readium.r2.shared.util.http.HttpClient
import org.readium.r2.shared.util.http.HttpRequest
import org.readium.r2.shared.util.http.fetchWithDecoder
import java.net.URL

enum class OPDS2ParserError {
Expand All @@ -36,18 +39,38 @@ class OPDS2Parser {

private lateinit var feed: Feed

suspend fun parseUrlString(url: String, client: HttpClient = DefaultHttpClient()): Try<ParseData, Exception> {
return client.fetchWithDecoder(HttpRequest(url)) {
this.parse(it.body, URL(url))
}
}

suspend fun parseRequest(request: HttpRequest, client: HttpClient = DefaultHttpClient()): Try<ParseData, Exception> {
return client.fetchWithDecoder(request) {
this.parse(it.body, URL(request.url))
}
}

@Deprecated(
"Use `parseRequest` or `parseUrlString` with coroutines instead",
ReplaceWith("OPDS2Parser.parseUrlString(url)"),
DeprecationLevel.WARNING
)
fun parseURL(url: URL): Promise<ParseData, Exception> {
return Fuel.get(url.toString(), null).promise() then {
val (_, _, result) = it
this.parse(result, url)
return DefaultHttpClient().fetchPromise(HttpRequest(url.toString())) then {
this.parse(it.body, url)
}
}

@Deprecated(
"Use `parseRequest` or `parseUrlString` with coroutines instead",
ReplaceWith("OPDS2Parser.parseUrlString(url)"),
DeprecationLevel.WARNING
)
@Suppress("unused")
fun parseURL(headers:MutableMap<String,String>, url: URL): Promise<ParseData, Exception> {
return Fuel.get(url.toString(), null).header(headers).promise() then {
val (_, _, result) = it
this.parse(result, url)
fun parseURL(headers: MutableMap<String,String>, url: URL): Promise<ParseData, Exception> {
return DefaultHttpClient().fetchPromise(HttpRequest(url = url.toString(), headers = headers)) then {
this.parse(it.body, url)
}
}

Expand Down