Added apiCall function
This commit is contained in:
parent
f07b1a4258
commit
9b5047e5fa
4 changed files with 127 additions and 36 deletions
27
README.md
27
README.md
|
@ -11,6 +11,7 @@ val joke = getJoke()
|
||||||
val safe = getJoke(safe = true)
|
val safe = getJoke(safe = true)
|
||||||
val pun = getJoke(category = Category.PUN)
|
val pun = getJoke(category = Category.PUN)
|
||||||
```
|
```
|
||||||
|
The parameters match the [joke endpoint](/https://v2.jokeapi.dev/#joke-endpoint).
|
||||||
|
|
||||||
A `Joke` class instance is returned:
|
A `Joke` class instance is returned:
|
||||||
|
|
||||||
|
@ -19,13 +20,14 @@ data class Joke(
|
||||||
val error: Boolean,
|
val error: Boolean,
|
||||||
val category: Category,
|
val category: Category,
|
||||||
val type: Type,
|
val type: Type,
|
||||||
val joke: Set<String>,
|
val joke: List<String>,
|
||||||
val flags: Set<Flag>,
|
val flags: Set<Flag>,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val safe: Boolean,
|
val safe: Boolean,
|
||||||
val language: Language
|
val language: Language
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
- View more [examples](https://github.com/ethauvin/jokeapi/blob/master/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokeTest.kt)...
|
||||||
|
|
||||||
If an error occurs, a `JokeException` is thrown:
|
If an error occurs, a `JokeException` is thrown:
|
||||||
|
|
||||||
|
@ -51,6 +53,7 @@ class HttpErrorException(
|
||||||
cause: Throwable? = null
|
cause: Throwable? = null
|
||||||
) : IOException(message, cause)
|
) : IOException(message, cause)
|
||||||
```
|
```
|
||||||
|
- View more [examples](https://github.com/ethauvin/jokeapi/blob/master/src/test/kotlin/net/thauvin/erik/jokeapi/Exceptions.kt)...
|
||||||
|
|
||||||
## Gradle, Maven, etc.
|
## Gradle, Maven, etc.
|
||||||
To use with [Gradle](https://gradle.org/), include the following dependency in your build file:
|
To use with [Gradle](https://gradle.org/), include the following dependency in your build file:
|
||||||
|
@ -89,6 +92,28 @@ safe: true
|
||||||
lang: "en"
|
lang: "en"
|
||||||
|
|
||||||
```
|
```
|
||||||
|
- View more [examples](https://github.com/ethauvin/jokeapi/blob/master/src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokeTest.kt)...
|
||||||
|
|
||||||
|
## Extending
|
||||||
|
|
||||||
|
A generic `apiCall()` function is available to access other [JokeAPI endpoints](https://v2.jokeapi.dev/#endpoints).
|
||||||
|
|
||||||
|
For example to retrieve the French [language code](https://v2.jokeapi.dev/#langcode-endpoint):
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val lang = apiCall(
|
||||||
|
endPoint = "langcode",
|
||||||
|
path = "french",
|
||||||
|
params = mapOf(Parameter.FORMAT to Format.YAML.value)
|
||||||
|
)
|
||||||
|
println(lang)
|
||||||
|
```
|
||||||
|
```yaml
|
||||||
|
error: false
|
||||||
|
code: "fr"
|
||||||
|
```
|
||||||
|
- View more [examples](https://github.com/ethauvin/jokeapi/blob/master/src/test/kotlin/net/thauvin/erik/jokeapi/Exceptions.kt)...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ import net.thauvin.erik.jokeapi.models.Format
|
||||||
import net.thauvin.erik.jokeapi.models.IdRange
|
import net.thauvin.erik.jokeapi.models.IdRange
|
||||||
import net.thauvin.erik.jokeapi.models.Joke
|
import net.thauvin.erik.jokeapi.models.Joke
|
||||||
import net.thauvin.erik.jokeapi.models.Language
|
import net.thauvin.erik.jokeapi.models.Language
|
||||||
|
import net.thauvin.erik.jokeapi.models.Parameter
|
||||||
import net.thauvin.erik.jokeapi.models.Type
|
import net.thauvin.erik.jokeapi.models.Type
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -53,11 +54,42 @@ import java.util.stream.Collectors
|
||||||
|
|
||||||
class JokeApi {
|
class JokeApi {
|
||||||
companion object {
|
companion object {
|
||||||
private const val API_URL = "https://v2.jokeapi.dev/joke/"
|
private const val API_URL = "https://v2.jokeapi.dev/"
|
||||||
|
private const val JOKE_ENDPOINT = "joke"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val logger: Logger by lazy { Logger.getLogger(JokeApi::class.java.simpleName) }
|
val logger: Logger by lazy { Logger.getLogger(JokeApi::class.java.simpleName) }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@JvmOverloads
|
||||||
|
@Throws(HttpErrorException::class, IOException::class)
|
||||||
|
fun apiCall(endPoint: String, path: String = "", params: Map<String, String> = emptyMap()): String {
|
||||||
|
val urlBuilder = StringBuilder("$API_URL$endPoint")
|
||||||
|
|
||||||
|
if (path.isNotEmpty()) {
|
||||||
|
if (!urlBuilder.endsWith(('/'))) {
|
||||||
|
urlBuilder.append('/')
|
||||||
|
}
|
||||||
|
urlBuilder.append(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.isNotEmpty()) {
|
||||||
|
urlBuilder.append('?')
|
||||||
|
val it = params.iterator()
|
||||||
|
while (it.hasNext()) {
|
||||||
|
val param = it.next()
|
||||||
|
urlBuilder.append(param.key)
|
||||||
|
if (param.value.isNotEmpty()) {
|
||||||
|
urlBuilder.append("=${param.value}")
|
||||||
|
}
|
||||||
|
if (it.hasNext()) {
|
||||||
|
urlBuilder.append("&")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fetchUrl(urlBuilder.toString())
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
@Throws(HttpErrorException::class, IOException::class)
|
@Throws(HttpErrorException::class, IOException::class)
|
||||||
|
@ -72,53 +104,50 @@ class JokeApi {
|
||||||
amount: Int = 1,
|
amount: Int = 1,
|
||||||
safe: Boolean = false,
|
safe: Boolean = false,
|
||||||
): String {
|
): String {
|
||||||
val urlBuilder = StringBuilder(API_URL)
|
val params = mutableMapOf<String, String>()
|
||||||
val urlParams = mutableListOf<String>()
|
|
||||||
|
|
||||||
// Category
|
// Categories
|
||||||
if (!categories.contains(Category.ANY)) {
|
val path = if (!categories.contains(Category.ANY)) {
|
||||||
urlBuilder.append(categories.stream().map(Category::value).collect(Collectors.joining(",")))
|
categories.stream().map(Category::value).collect(Collectors.joining(","))
|
||||||
} else {
|
} else {
|
||||||
urlBuilder.append(Category.ANY.value)
|
Category.ANY.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
if (language != Language.ENGLISH) {
|
if (language != Language.ENGLISH) {
|
||||||
urlParams.add("lang=${language.value}")
|
params[Parameter.LANG] = language.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
if (flags.isNotEmpty()) {
|
if (flags.isNotEmpty()) {
|
||||||
if (flags.contains(Flag.ALL)) {
|
if (flags.contains(Flag.ALL)) {
|
||||||
urlParams.add("blacklistFlags=${Flag.ALL.value}")
|
params[Parameter.FLAGS] = Flag.ALL.value
|
||||||
} else {
|
} else {
|
||||||
urlParams.add(
|
params[Parameter.FLAGS] = flags.stream().map(Flag::value).collect(Collectors.joining(","))
|
||||||
"blacklistFlags=" + flags.stream().map(Flag::value).collect(Collectors.joining(","))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type
|
// Type
|
||||||
if (type != Type.ALL) {
|
if (type != Type.ALL) {
|
||||||
urlParams.add("type=${type.value}")
|
params[Parameter.TYPE] = type.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format
|
// Format
|
||||||
if (format != Format.JSON) {
|
if (format != Format.JSON) {
|
||||||
urlParams.add("format=${format.value}")
|
params[Parameter.FORMAT] = format.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains
|
// Contains
|
||||||
if (search.isNotBlank()) {
|
if (search.isNotBlank()) {
|
||||||
urlParams.add("contains=${URLEncoder.encode(search, StandardCharsets.UTF_8)}")
|
params[Parameter.CONTAINS] = URLEncoder.encode(search, StandardCharsets.UTF_8).replace("+", "%20")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range
|
// Range
|
||||||
if (idRange.start >= 0) {
|
if (idRange.start >= 0) {
|
||||||
if (idRange.end == -1 || idRange.start == idRange.end) {
|
if (idRange.end == -1 || idRange.start == idRange.end) {
|
||||||
urlParams.add("idRange=${idRange.start}")
|
params[Parameter.RANGE] = idRange.start.toString()
|
||||||
} else if (idRange.end > idRange.start) {
|
} else if (idRange.end > idRange.start) {
|
||||||
urlParams.add("idRange=${idRange.start}-${idRange.end}")
|
params[Parameter.RANGE] = "${idRange.start}-${idRange.end}"
|
||||||
} else if (logger.isLoggable(Level.WARNING)) {
|
} else if (logger.isLoggable(Level.WARNING)) {
|
||||||
logger.warning("Invalid ID Range: ${idRange.start}, ${idRange.end}")
|
logger.warning("Invalid ID Range: ${idRange.start}, ${idRange.end}")
|
||||||
}
|
}
|
||||||
|
@ -126,28 +155,17 @@ class JokeApi {
|
||||||
|
|
||||||
// Amount
|
// Amount
|
||||||
if (amount in 2..10) {
|
if (amount in 2..10) {
|
||||||
urlParams.add("amount=${amount}")
|
params[Parameter.AMOUNT] = amount.toString()
|
||||||
} else if (amount != 1 && logger.isLoggable(Level.WARNING)) {
|
} else if (amount != 1 && logger.isLoggable(Level.WARNING)) {
|
||||||
logger.warning("Invalid Amount: $amount")
|
logger.warning("Invalid Amount: $amount")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe
|
// Safe
|
||||||
if (safe) {
|
if (safe) {
|
||||||
urlParams.add("safe-mode")
|
params[Parameter.SAFE] = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlParams.isNotEmpty()) {
|
return apiCall(JOKE_ENDPOINT, path, params)
|
||||||
urlBuilder.append('?')
|
|
||||||
val it = urlParams.iterator()
|
|
||||||
while (it.hasNext()) {
|
|
||||||
urlBuilder.append(it.next())
|
|
||||||
if (it.hasNext()) {
|
|
||||||
urlBuilder.append("&")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetchUrl(urlBuilder.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(HttpErrorException::class, IOException::class)
|
@Throws(HttpErrorException::class, IOException::class)
|
||||||
|
@ -270,7 +288,7 @@ class JokeApi {
|
||||||
timestamp = json.getLong("timestamp")
|
timestamp = json.getLong("timestamp")
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val jokes = mutableSetOf<String>()
|
val jokes = mutableListOf<String>()
|
||||||
if (json.has("setup")) {
|
if (json.has("setup")) {
|
||||||
jokes.add(json.getString("setup"))
|
jokes.add(json.getString("setup"))
|
||||||
jokes.add(json.getString(("delivery")))
|
jokes.add(json.getString(("delivery")))
|
||||||
|
@ -291,12 +309,12 @@ class JokeApi {
|
||||||
return Joke(
|
return Joke(
|
||||||
error = false,
|
error = false,
|
||||||
category = Category.valueOf(json.getString("category").uppercase()),
|
category = Category.valueOf(json.getString("category").uppercase()),
|
||||||
type = Type.valueOf(json.getString("type").uppercase()),
|
type = Type.valueOf(json.getString(Parameter.TYPE).uppercase()),
|
||||||
joke = jokes,
|
joke = jokes,
|
||||||
flags = enabledFlags,
|
flags = enabledFlags,
|
||||||
safe = json.getBoolean("safe"),
|
safe = json.getBoolean("safe"),
|
||||||
id = json.getInt("id"),
|
id = json.getInt("id"),
|
||||||
language = Language.valueOf(json.getString("lang").uppercase())
|
language = Language.valueOf(json.getString(Parameter.LANG).uppercase())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ data class Joke(
|
||||||
val error: Boolean,
|
val error: Boolean,
|
||||||
val category: Category,
|
val category: Category,
|
||||||
val type: Type,
|
val type: Type,
|
||||||
val joke: Set<String>,
|
val joke: List<String>,
|
||||||
val flags: Set<Flag>,
|
val flags: Set<Flag>,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val safe: Boolean,
|
val safe: Boolean,
|
||||||
|
|
48
src/main/kotlin/net/thauvin/erik/jokeapi/models/Parameter.kt
Normal file
48
src/main/kotlin/net/thauvin/erik/jokeapi/models/Parameter.kt
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Parameter.kt
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022, Erik C. Thauvin (erik@thauvin.net)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* Neither the name of this project nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.thauvin.erik.jokeapi.models
|
||||||
|
|
||||||
|
object Parameter {
|
||||||
|
const val AMOUNT = "amount"
|
||||||
|
const val CONTAINS = "contains"
|
||||||
|
const val FLAGS = "blacklistFlags"
|
||||||
|
const val FORMAT = "format"
|
||||||
|
const val RANGE = "idRange"
|
||||||
|
const val LANG = "lang"
|
||||||
|
const val SAFE = "safe-mode"
|
||||||
|
const val TYPE = "type"
|
||||||
|
|
||||||
|
const val BLACKLIST_FLAGS = FLAGS
|
||||||
|
const val ID_RANGE = RANGE
|
||||||
|
const val SAFE_MODE = SAFE
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue