Moved Kotlin-only functions to top-level
This commit is contained in:
parent
9435df289a
commit
de30b3f638
13 changed files with 525 additions and 366 deletions
|
@ -9,7 +9,7 @@ A simple Kotlin/Java library to retrieve jokes from [Sv443's JokeAPI](https://v2
|
|||
## Examples (TL;DR)
|
||||
|
||||
```kotlin
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.getJoke
|
||||
import net.thauvin.erik.jokeapi.getJoke
|
||||
|
||||
val joke = getJoke()
|
||||
val safe = getJoke(safe = true)
|
||||
|
@ -124,9 +124,7 @@ var config = new JokeConfig.Builder()
|
|||
.safe(true)
|
||||
.build();
|
||||
var joke = JokeApi.getJoke(config);
|
||||
for (var j : joke.getJoke()) {
|
||||
System.out.println(j);
|
||||
}
|
||||
joke.getJoke().forEach(System.out::println);
|
||||
```
|
||||
|
||||
## Extending
|
||||
|
@ -136,7 +134,7 @@ A generic `apiCall()` function is available to access other [JokeAPI endpoints](
|
|||
For example to retrieve the French [language code](https://v2.jokeapi.dev/#langcode-endpoint):
|
||||
|
||||
```kotlin
|
||||
val lang = apiCall(
|
||||
val lang = JokeApi.apiCall(
|
||||
endPoint = "langcode",
|
||||
path = "french",
|
||||
params = mapOf(Parameter.FORMAT to Format.YAML.value)
|
||||
|
|
|
@ -2,24 +2,22 @@
|
|||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ComplexMethod:JokeApi.kt$JokeApi.Companion$@JvmStatic @Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class) fun getRawJokes( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, ): String</ID>
|
||||
<ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( amount: Int, categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = false )</ID>
|
||||
<ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, )</ID>
|
||||
<ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = false )</ID>
|
||||
<ID>LongParameterList:JokeConfig.kt$JokeConfig$( val categories: Set<Category>, val language: Language, val flags: Set<Flag>, val type: Type, val format: Format, val search: String, val idRange: IdRange, val amount: Int = 1, val safe: Boolean, val splitNewLine: Boolean, )</ID>
|
||||
<ID>ComplexMethod:JokeApi.kt$fun getRawJokes( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, auth: String = "" ): String</ID>
|
||||
<ID>LongParameterList:JokeApi.kt$( amount: Int, categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = false, auth: String = "" )</ID>
|
||||
<ID>LongParameterList:JokeApi.kt$( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, auth: String = "" )</ID>
|
||||
<ID>LongParameterList:JokeApi.kt$( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = false, auth: String = "" )</ID>
|
||||
<ID>LongParameterList:JokeConfig.kt$JokeConfig$( val categories: Set<Category>, val language: Language, val flags: Set<Flag>, val type: Type, val format: Format, val search: String, val idRange: IdRange, val amount: Int, val safe: Boolean, val splitNewLine: Boolean, val auth: String )</ID>
|
||||
<ID>LongParameterList:JokeException.kt$JokeException$( val internalError: Boolean, val code: Int, message: String, val causedBy: List<String>, val additionalInfo: String, val timestamp: Long, cause: Throwable? = null )</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$200</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$399</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$400</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$403</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$404</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$413</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$414</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$429</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$500</ID>
|
||||
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$523</ID>
|
||||
<ID>TooManyFunctions:JokeApi.kt$JokeApi$Companion</ID>
|
||||
<ID>MagicNumber:util.kt$200</ID>
|
||||
<ID>MagicNumber:util.kt$399</ID>
|
||||
<ID>MagicNumber:util.kt$400</ID>
|
||||
<ID>MagicNumber:util.kt$403</ID>
|
||||
<ID>MagicNumber:util.kt$404</ID>
|
||||
<ID>MagicNumber:util.kt$413</ID>
|
||||
<ID>MagicNumber:util.kt$414</ID>
|
||||
<ID>MagicNumber:util.kt$429</ID>
|
||||
<ID>MagicNumber:util.kt$500</ID>
|
||||
<ID>MagicNumber:util.kt$523</ID>
|
||||
<ID>TooManyFunctions:JokeConfig.kt$JokeConfig$Builder</ID>
|
||||
<ID>UtilityClassWithPublicConstructor:JokeApi.kt$JokeApi</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
<artifactId>jokeapi</artifactId>
|
||||
<version>0.9-SNAPSHOT</version>
|
||||
<name>jokeapi</name>
|
||||
<description>Kotlin/Java Wrapper for Sv443's JokeApi</description>
|
||||
<description>Kotlin/Java Wrapper for Sv443's JokeAPI</description>
|
||||
<url>https://github.com/ethauvin/jokeapi</url>
|
||||
<licenses>
|
||||
<license>
|
||||
|
|
|
@ -43,22 +43,17 @@ import net.thauvin.erik.jokeapi.models.Language
|
|||
import net.thauvin.erik.jokeapi.models.Parameter
|
||||
import net.thauvin.erik.jokeapi.models.Type
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
import java.util.stream.Collectors
|
||||
|
||||
/**
|
||||
* Implements the [Sv443's JokeAPI](https://jokeapi.dev/).
|
||||
*/
|
||||
class JokeApi {
|
||||
class JokeApi private constructor() {
|
||||
companion object {
|
||||
private const val API_URL = "https://v2.jokeapi.dev/"
|
||||
private const val JOKE_ENDPOINT = "joke"
|
||||
|
||||
@JvmStatic
|
||||
val logger: Logger by lazy { Logger.getLogger(JokeApi::class.java.simpleName) }
|
||||
|
@ -70,8 +65,13 @@ class JokeApi {
|
|||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
@Throws(HttpErrorException::class, IOException::class)
|
||||
fun apiCall(endPoint: String, path: String = "", params: Map<String, String> = emptyMap()): String {
|
||||
@Throws(HttpErrorException::class)
|
||||
fun apiCall(
|
||||
endPoint: String,
|
||||
path: String = "",
|
||||
params: Map<String, String> = emptyMap(),
|
||||
auth: String = ""
|
||||
): String {
|
||||
val urlBuilder = StringBuilder("$API_URL$endPoint")
|
||||
|
||||
if (path.isNotEmpty()) {
|
||||
|
@ -98,99 +98,16 @@ class JokeApi {
|
|||
}
|
||||
}
|
||||
}
|
||||
return fetchUrl(urlBuilder.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one or more jokes.
|
||||
*
|
||||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
* @see [getJoke]
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class)
|
||||
fun getRawJokes(
|
||||
categories: Set<Category> = setOf(Category.ANY),
|
||||
language: Language = Language.ENGLISH,
|
||||
flags: Set<Flag> = emptySet(),
|
||||
type: Type = Type.ALL,
|
||||
format: Format = Format.JSON,
|
||||
search: String = "",
|
||||
idRange: IdRange = IdRange(),
|
||||
amount: Int = 1,
|
||||
safe: Boolean = false,
|
||||
): String {
|
||||
val params = mutableMapOf<String, String>()
|
||||
|
||||
// Categories
|
||||
val path = if (!categories.contains(Category.ANY)) {
|
||||
categories.stream().map(Category::value).collect(Collectors.joining(","))
|
||||
} else {
|
||||
Category.ANY.value
|
||||
}
|
||||
|
||||
// Language
|
||||
if (language != Language.ENGLISH) {
|
||||
params[Parameter.LANG] = language.value
|
||||
}
|
||||
|
||||
// Flags
|
||||
if (flags.isNotEmpty()) {
|
||||
if (flags.contains(Flag.ALL)) {
|
||||
params[Parameter.FLAGS] = Flag.ALL.value
|
||||
} else {
|
||||
params[Parameter.FLAGS] = flags.stream().map(Flag::value).collect(Collectors.joining(","))
|
||||
}
|
||||
}
|
||||
|
||||
// Type
|
||||
if (type != Type.ALL) {
|
||||
params[Parameter.TYPE] = type.value
|
||||
}
|
||||
|
||||
// Format
|
||||
if (format != Format.JSON) {
|
||||
params[Parameter.FORMAT] = format.value
|
||||
}
|
||||
|
||||
// Contains
|
||||
if (search.isNotBlank()) {
|
||||
params[Parameter.CONTAINS] = search
|
||||
}
|
||||
|
||||
// Range
|
||||
if (idRange.start >= 0) {
|
||||
if (idRange.end == -1 || idRange.start == idRange.end) {
|
||||
params[Parameter.RANGE] = idRange.start.toString()
|
||||
} else if (idRange.end > idRange.start) {
|
||||
params[Parameter.RANGE] = "${idRange.start}-${idRange.end}"
|
||||
} else {
|
||||
throw IllegalArgumentException("Invalid ID Range: ${idRange.start}, ${idRange.end}")
|
||||
}
|
||||
}
|
||||
|
||||
// Amount
|
||||
if (amount > 1) {
|
||||
params[Parameter.AMOUNT] = amount.toString()
|
||||
} else if (amount <= 0) {
|
||||
throw IllegalArgumentException("Invalid Amount: $amount")
|
||||
}
|
||||
|
||||
// Safe
|
||||
if (safe) {
|
||||
params[Parameter.SAFE] = ""
|
||||
}
|
||||
|
||||
return apiCall(JOKE_ENDPOINT, path, params)
|
||||
return fetchUrl(urlBuilder.toString(), auth)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one or more jokes using a [configuration][JokeConfig].
|
||||
*
|
||||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
* See the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class)
|
||||
@Throws(HttpErrorException::class)
|
||||
fun getRawJokes(config: JokeConfig): String {
|
||||
return getRawJokes(
|
||||
categories = config.categories,
|
||||
|
@ -201,181 +118,20 @@ class JokeApi {
|
|||
search = config.search,
|
||||
idRange = config.idRange,
|
||||
amount = config.amount,
|
||||
safe = config.safe
|
||||
safe = config.safe,
|
||||
auth = config.auth
|
||||
)
|
||||
}
|
||||
|
||||
@Throws(HttpErrorException::class, IOException::class)
|
||||
internal fun fetchUrl(url: String): String {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine(url)
|
||||
}
|
||||
|
||||
val connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.setRequestProperty(
|
||||
"User-Agent", "Mozilla/5.0 (Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0"
|
||||
)
|
||||
|
||||
if (connection.responseCode in 200..399) {
|
||||
val body = connection.inputStream.bufferedReader().readText()
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine(body)
|
||||
}
|
||||
return body
|
||||
} else {
|
||||
throw httpError(connection.responseCode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun httpError(responseCode: Int): HttpErrorException {
|
||||
val httpException: HttpErrorException
|
||||
when (responseCode) {
|
||||
400 -> httpException = HttpErrorException(
|
||||
responseCode, "Bad Request", IOException(
|
||||
"The request you have sent to JokeAPI is formatted incorrectly and cannot be processed."
|
||||
)
|
||||
)
|
||||
|
||||
403 -> httpException = HttpErrorException(
|
||||
responseCode, "Forbidden", IOException(
|
||||
"You have been added to the blacklist due to malicious behavior and are not allowed"
|
||||
+ " to send requests to JokeAPI anymore."
|
||||
)
|
||||
)
|
||||
|
||||
404 -> httpException = HttpErrorException(
|
||||
responseCode, "Not Found", IOException("The URL you have requested couldn't be found.")
|
||||
)
|
||||
|
||||
413 -> httpException = HttpErrorException(
|
||||
responseCode, "URI Too Long", IOException("The URL exceeds the maximum length of 250 characters.")
|
||||
)
|
||||
|
||||
414 -> httpException = HttpErrorException(
|
||||
responseCode,
|
||||
"Payload Too Large",
|
||||
IOException("The payload data sent to the server exceeds the maximum size of 5120 bytes.")
|
||||
)
|
||||
|
||||
429 -> httpException = HttpErrorException(
|
||||
responseCode, "Too Many Requests", IOException(
|
||||
"You have exceeded the limit of 120 requests per minute and have to wait a bit"
|
||||
+ " until you are allowed to send requests again."
|
||||
)
|
||||
)
|
||||
|
||||
500 -> httpException = HttpErrorException(
|
||||
responseCode, "Internal Server Error", IOException(
|
||||
"There was a general internal error within JokeAPI. You can get more info from"
|
||||
+ " the properties in the response text."
|
||||
)
|
||||
)
|
||||
|
||||
523 -> httpException = HttpErrorException(
|
||||
responseCode, "Origin Unreachable", IOException(
|
||||
"The server is temporarily offline due to maintenance or a dynamic IP update."
|
||||
+ " Please be patient in this case."
|
||||
)
|
||||
)
|
||||
|
||||
else -> httpException = HttpErrorException(responseCode, "Unknown HTTP Error")
|
||||
}
|
||||
|
||||
return httpException
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [Joke] instance.
|
||||
*
|
||||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
*
|
||||
* @param splitNewLine Split newline within [Type.SINGLE] joke.
|
||||
* @see [getRawJokes]
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class)
|
||||
fun getJoke(
|
||||
categories: Set<Category> = setOf(Category.ANY),
|
||||
language: Language = Language.ENGLISH,
|
||||
flags: Set<Flag> = emptySet(),
|
||||
type: Type = Type.ALL,
|
||||
search: String = "",
|
||||
idRange: IdRange = IdRange(),
|
||||
safe: Boolean = false,
|
||||
splitNewLine: Boolean = false
|
||||
): Joke {
|
||||
val json = JSONObject(
|
||||
getRawJokes(
|
||||
categories = categories,
|
||||
language = language,
|
||||
flags = flags,
|
||||
type = type,
|
||||
search = search,
|
||||
idRange = idRange,
|
||||
safe = safe
|
||||
)
|
||||
)
|
||||
if (json.getBoolean("error")) {
|
||||
throw parseError(json)
|
||||
} else {
|
||||
return parseJoke(json, splitNewLine)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of [Joke] instances.
|
||||
*
|
||||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
*
|
||||
* @param amount The required amount of jokes to return.
|
||||
* @param splitNewLine Split newline within [Type.SINGLE] joke.
|
||||
* @see [getRawJokes]
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class)
|
||||
fun getJokes(
|
||||
amount: Int,
|
||||
categories: Set<Category> = setOf(Category.ANY),
|
||||
language: Language = Language.ENGLISH,
|
||||
flags: Set<Flag> = emptySet(),
|
||||
type: Type = Type.ALL,
|
||||
search: String = "",
|
||||
idRange: IdRange = IdRange(),
|
||||
safe: Boolean = false,
|
||||
splitNewLine: Boolean = false
|
||||
): Array<Joke> {
|
||||
val json = JSONObject(
|
||||
getRawJokes(
|
||||
categories = categories,
|
||||
language = language,
|
||||
flags = flags,
|
||||
type = type,
|
||||
search = search,
|
||||
idRange = idRange,
|
||||
amount = amount,
|
||||
safe = safe
|
||||
)
|
||||
)
|
||||
if (json.getBoolean("error")) {
|
||||
throw parseError(json)
|
||||
} else {
|
||||
return if (json.has("amount")) {
|
||||
val jokes = json.getJSONArray("jokes")
|
||||
Array(jokes.length()) { i -> parseJoke(jokes.getJSONObject(i), splitNewLine) }
|
||||
} else {
|
||||
arrayOf(parseJoke(json, splitNewLine))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a [Joke] instance using a [configuration][JokeConfig].
|
||||
*
|
||||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class)
|
||||
fun getJoke(config: JokeConfig): Joke {
|
||||
@JvmOverloads
|
||||
@Throws(HttpErrorException::class, JokeException::class)
|
||||
fun getJoke(config: JokeConfig = JokeConfig.Builder().build()): Joke {
|
||||
return getJoke(
|
||||
categories = config.categories,
|
||||
language = config.language,
|
||||
|
@ -384,7 +140,8 @@ class JokeApi {
|
|||
search = config.search,
|
||||
idRange = config.idRange,
|
||||
safe = config.safe,
|
||||
splitNewLine = config.splitNewLine
|
||||
splitNewLine = config.splitNewLine,
|
||||
auth = config.auth
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -394,7 +151,7 @@ class JokeApi {
|
|||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class)
|
||||
@Throws(HttpErrorException::class, JokeException::class)
|
||||
fun getJokes(config: JokeConfig): Array<Joke> {
|
||||
return getJokes(
|
||||
categories = config.categories,
|
||||
|
@ -405,51 +162,173 @@ class JokeApi {
|
|||
idRange = config.idRange,
|
||||
amount = config.amount,
|
||||
safe = config.safe,
|
||||
splitNewLine = config.splitNewLine
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseError(json: JSONObject): JokeException {
|
||||
val causedBy = json.getJSONArray("causedBy")
|
||||
val causes = List<String>(causedBy.length()) { i -> causedBy.getString(i) }
|
||||
return JokeException(
|
||||
internalError = json.getBoolean("internalError"),
|
||||
code = json.getInt("code"),
|
||||
message = json.getString("message"),
|
||||
causedBy = causes,
|
||||
additionalInfo = json.getString("additionalInfo"),
|
||||
timestamp = json.getLong("timestamp")
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseJoke(json: JSONObject, splitNewLine: Boolean): Joke {
|
||||
val jokes = mutableListOf<String>()
|
||||
if (json.has("setup")) {
|
||||
jokes.add(json.getString("setup"))
|
||||
jokes.add(json.getString(("delivery")))
|
||||
} else {
|
||||
if (splitNewLine) {
|
||||
jokes.addAll(json.getString("joke").split("\n"))
|
||||
} else {
|
||||
jokes.add(json.getString("joke"))
|
||||
}
|
||||
}
|
||||
val enabledFlags = mutableSetOf<Flag>()
|
||||
val jsonFlags = json.getJSONObject("flags")
|
||||
Flag.values().filter { it != Flag.ALL }.forEach {
|
||||
if (jsonFlags.has(it.value) && jsonFlags.getBoolean(it.value)) {
|
||||
enabledFlags.add(it)
|
||||
}
|
||||
}
|
||||
return Joke(
|
||||
category = Category.valueOf(json.getString("category").uppercase()),
|
||||
type = Type.valueOf(json.getString(Parameter.TYPE).uppercase()),
|
||||
joke = jokes,
|
||||
flags = enabledFlags,
|
||||
safe = json.getBoolean("safe"),
|
||||
id = json.getInt("id"),
|
||||
language = Language.valueOf(json.getString(Parameter.LANG).uppercase())
|
||||
splitNewLine = config.splitNewLine,
|
||||
auth = config.auth
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a [Joke] instance.
|
||||
*
|
||||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
*
|
||||
* @param splitNewLine Split newline within [Type.SINGLE] joke.
|
||||
*/
|
||||
fun getJoke(
|
||||
categories: Set<Category> = setOf(Category.ANY),
|
||||
language: Language = Language.ENGLISH,
|
||||
flags: Set<Flag> = emptySet(),
|
||||
type: Type = Type.ALL,
|
||||
search: String = "",
|
||||
idRange: IdRange = IdRange(),
|
||||
safe: Boolean = false,
|
||||
splitNewLine: Boolean = false,
|
||||
auth: String = ""
|
||||
): Joke {
|
||||
val json = JSONObject(
|
||||
getRawJokes(
|
||||
categories = categories,
|
||||
language = language,
|
||||
flags = flags,
|
||||
type = type,
|
||||
search = search,
|
||||
idRange = idRange,
|
||||
safe = safe,
|
||||
auth = auth
|
||||
)
|
||||
)
|
||||
if (json.getBoolean("error")) {
|
||||
throw parseError(json)
|
||||
} else {
|
||||
return parseJoke(json, splitNewLine)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of [Joke] instances.
|
||||
*
|
||||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
*
|
||||
* @param amount The required amount of jokes to return.
|
||||
* @param splitNewLine Split newline within [Type.SINGLE] joke.
|
||||
*/
|
||||
fun getJokes(
|
||||
amount: Int,
|
||||
categories: Set<Category> = setOf(Category.ANY),
|
||||
language: Language = Language.ENGLISH,
|
||||
flags: Set<Flag> = emptySet(),
|
||||
type: Type = Type.ALL,
|
||||
search: String = "",
|
||||
idRange: IdRange = IdRange(),
|
||||
safe: Boolean = false,
|
||||
splitNewLine: Boolean = false,
|
||||
auth: String = ""
|
||||
): Array<Joke> {
|
||||
val json = JSONObject(
|
||||
getRawJokes(
|
||||
categories = categories,
|
||||
language = language,
|
||||
flags = flags,
|
||||
type = type,
|
||||
search = search,
|
||||
idRange = idRange,
|
||||
amount = amount,
|
||||
safe = safe,
|
||||
auth = auth
|
||||
)
|
||||
)
|
||||
if (json.getBoolean("error")) {
|
||||
throw parseError(json)
|
||||
} else {
|
||||
return if (json.has("amount")) {
|
||||
val jokes = json.getJSONArray("jokes")
|
||||
Array(jokes.length()) { i -> parseJoke(jokes.getJSONObject(i), splitNewLine) }
|
||||
} else {
|
||||
arrayOf(parseJoke(json, splitNewLine))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one or more jokes.
|
||||
*
|
||||
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
|
||||
*/
|
||||
fun getRawJokes(
|
||||
categories: Set<Category> = setOf(Category.ANY),
|
||||
language: Language = Language.ENGLISH,
|
||||
flags: Set<Flag> = emptySet(),
|
||||
type: Type = Type.ALL,
|
||||
format: Format = Format.JSON,
|
||||
search: String = "",
|
||||
idRange: IdRange = IdRange(),
|
||||
amount: Int = 1,
|
||||
safe: Boolean = false,
|
||||
auth: String = ""
|
||||
): String {
|
||||
val params = mutableMapOf<String, String>()
|
||||
|
||||
// Categories
|
||||
val path = if (!categories.contains(Category.ANY)) {
|
||||
categories.stream().map(Category::value).collect(Collectors.joining(","))
|
||||
} else {
|
||||
Category.ANY.value
|
||||
}
|
||||
|
||||
// Language
|
||||
if (language != Language.ENGLISH) {
|
||||
params[Parameter.LANG] = language.value
|
||||
}
|
||||
|
||||
// Flags
|
||||
if (flags.isNotEmpty()) {
|
||||
if (flags.contains(Flag.ALL)) {
|
||||
params[Parameter.FLAGS] = Flag.ALL.value
|
||||
} else {
|
||||
params[Parameter.FLAGS] = flags.stream().map(Flag::value).collect(Collectors.joining(","))
|
||||
}
|
||||
}
|
||||
|
||||
// Type
|
||||
if (type != Type.ALL) {
|
||||
params[Parameter.TYPE] = type.value
|
||||
}
|
||||
|
||||
// Format
|
||||
if (format != Format.JSON) {
|
||||
params[Parameter.FORMAT] = format.value
|
||||
}
|
||||
|
||||
// Contains
|
||||
if (search.isNotBlank()) {
|
||||
params[Parameter.CONTAINS] = search
|
||||
}
|
||||
|
||||
// Range
|
||||
if (idRange.start >= 0) {
|
||||
if (idRange.end == -1 || idRange.start == idRange.end) {
|
||||
params[Parameter.RANGE] = idRange.start.toString()
|
||||
} else if (idRange.end > idRange.start) {
|
||||
params[Parameter.RANGE] = "${idRange.start}-${idRange.end}"
|
||||
} else {
|
||||
throw IllegalArgumentException("Invalid ID Range: ${idRange.start}, ${idRange.end}")
|
||||
}
|
||||
}
|
||||
|
||||
// Amount
|
||||
if (amount > 1) {
|
||||
params[Parameter.AMOUNT] = amount.toString()
|
||||
} else if (amount <= 0) {
|
||||
throw IllegalArgumentException("Invalid Amount: $amount")
|
||||
}
|
||||
|
||||
// Safe
|
||||
if (safe) {
|
||||
params[Parameter.SAFE] = ""
|
||||
}
|
||||
|
||||
return JokeApi.apiCall("joke", path, params, auth)
|
||||
}
|
||||
|
|
|
@ -53,9 +53,10 @@ class JokeConfig private constructor(
|
|||
val format: Format,
|
||||
val search: String,
|
||||
val idRange: IdRange,
|
||||
val amount: Int = 1,
|
||||
val amount: Int,
|
||||
val safe: Boolean,
|
||||
val splitNewLine: Boolean,
|
||||
val auth: String
|
||||
) {
|
||||
/**
|
||||
* [Builds][build] a new configuration.
|
||||
|
@ -74,7 +75,8 @@ class JokeConfig private constructor(
|
|||
var idRange: IdRange = IdRange(),
|
||||
var amount: Int = 1,
|
||||
var safe: Boolean = false,
|
||||
var splitNewLine: Boolean = false
|
||||
var splitNewLine: Boolean = false,
|
||||
var auth: String = ""
|
||||
) {
|
||||
fun categories(categories: Set<Category>) = apply { this.categories = categories }
|
||||
fun language(language: Language) = apply { this.language = language }
|
||||
|
@ -86,9 +88,10 @@ class JokeConfig private constructor(
|
|||
fun amount(amount: Int) = apply { this.amount = amount }
|
||||
fun safe(safe: Boolean) = apply { this.safe = safe }
|
||||
fun splitNewLine(splitNewLine: Boolean) = apply { this.splitNewLine = splitNewLine }
|
||||
fun auth(auth: String) = apply { this.auth = auth }
|
||||
|
||||
fun build() = JokeConfig(
|
||||
categories, language, flags, type, format, search, idRange, amount, safe, splitNewLine
|
||||
categories, language, flags, type, format, search, idRange, amount, safe, splitNewLine, auth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class JokeException @JvmOverloads constructor(
|
|||
val additionalInfo: String,
|
||||
val timestamp: Long,
|
||||
cause: Throwable? = null
|
||||
) : Exception(message, cause) {
|
||||
) : RuntimeException(message, cause) {
|
||||
companion object {
|
||||
private const val serialVersionUID = 1L
|
||||
}
|
||||
|
|
172
src/main/kotlin/net/thauvin/erik/jokeapi/util.kt
Normal file
172
src/main/kotlin/net/thauvin/erik/jokeapi/util.kt
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* util.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
|
||||
|
||||
import net.thauvin.erik.jokeapi.exceptions.HttpErrorException
|
||||
import net.thauvin.erik.jokeapi.exceptions.JokeException
|
||||
import net.thauvin.erik.jokeapi.models.Category
|
||||
import net.thauvin.erik.jokeapi.models.Flag
|
||||
import net.thauvin.erik.jokeapi.models.Joke
|
||||
import net.thauvin.erik.jokeapi.models.Language
|
||||
import net.thauvin.erik.jokeapi.models.Parameter
|
||||
import net.thauvin.erik.jokeapi.models.Type
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.logging.Level
|
||||
|
||||
internal fun fetchUrl(url: String, auth: String = ""): String {
|
||||
if (JokeApi.logger.isLoggable(Level.FINE)) {
|
||||
JokeApi.logger.fine(url)
|
||||
}
|
||||
|
||||
val connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.setRequestProperty(
|
||||
"User-Agent", "Mozilla/5.0 (Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0"
|
||||
)
|
||||
if (auth.isNotEmpty()) {
|
||||
connection.setRequestProperty("Authentication", auth)
|
||||
}
|
||||
|
||||
if (connection.responseCode in 200..399) {
|
||||
val body = connection.inputStream.bufferedReader().readText()
|
||||
if (JokeApi.logger.isLoggable(Level.FINE)) {
|
||||
JokeApi.logger.fine(body)
|
||||
}
|
||||
return body
|
||||
} else {
|
||||
throw httpError(connection.responseCode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun httpError(responseCode: Int): HttpErrorException {
|
||||
val httpException: HttpErrorException
|
||||
when (responseCode) {
|
||||
400 -> httpException = HttpErrorException(
|
||||
responseCode, "Bad Request", IOException(
|
||||
"The request you have sent to JokeAPI is formatted incorrectly and cannot be processed."
|
||||
)
|
||||
)
|
||||
|
||||
403 -> httpException = HttpErrorException(
|
||||
responseCode, "Forbidden", IOException(
|
||||
"You have been added to the blacklist due to malicious behavior and are not allowed"
|
||||
+ " to send requests to JokeAPI anymore."
|
||||
)
|
||||
)
|
||||
|
||||
404 -> httpException = HttpErrorException(
|
||||
responseCode, "Not Found", IOException("The URL you have requested couldn't be found.")
|
||||
)
|
||||
|
||||
413 -> httpException = HttpErrorException(
|
||||
responseCode, "URI Too Long", IOException("The URL exceeds the maximum length of 250 characters.")
|
||||
)
|
||||
|
||||
414 -> httpException = HttpErrorException(
|
||||
responseCode,
|
||||
"Payload Too Large",
|
||||
IOException("The payload data sent to the server exceeds the maximum size of 5120 bytes.")
|
||||
)
|
||||
|
||||
429 -> httpException = HttpErrorException(
|
||||
responseCode, "Too Many Requests", IOException(
|
||||
"You have exceeded the limit of 120 requests per minute and have to wait a bit"
|
||||
+ " until you are allowed to send requests again."
|
||||
)
|
||||
)
|
||||
|
||||
500 -> httpException = HttpErrorException(
|
||||
responseCode, "Internal Server Error", IOException(
|
||||
"There was a general internal error within JokeAPI. You can get more info from"
|
||||
+ " the properties in the response text."
|
||||
)
|
||||
)
|
||||
|
||||
523 -> httpException = HttpErrorException(
|
||||
responseCode, "Origin Unreachable", IOException(
|
||||
"The server is temporarily offline due to maintenance or a dynamic IP update."
|
||||
+ " Please be patient in this case."
|
||||
)
|
||||
)
|
||||
|
||||
else -> httpException = HttpErrorException(responseCode, "Unknown HTTP Error")
|
||||
}
|
||||
|
||||
return httpException
|
||||
}
|
||||
|
||||
internal fun parseError(json: JSONObject): JokeException {
|
||||
val causedBy = json.getJSONArray("causedBy")
|
||||
val causes = List<String>(causedBy.length()) { i -> causedBy.getString(i) }
|
||||
return JokeException(
|
||||
internalError = json.getBoolean("internalError"),
|
||||
code = json.getInt("code"),
|
||||
message = json.getString("message"),
|
||||
causedBy = causes,
|
||||
additionalInfo = json.getString("additionalInfo"),
|
||||
timestamp = json.getLong("timestamp")
|
||||
)
|
||||
}
|
||||
|
||||
internal fun parseJoke(json: JSONObject, splitNewLine: Boolean): Joke {
|
||||
val jokes = mutableListOf<String>()
|
||||
if (json.has("setup")) {
|
||||
jokes.add(json.getString("setup"))
|
||||
jokes.add(json.getString(("delivery")))
|
||||
} else {
|
||||
if (splitNewLine) {
|
||||
jokes.addAll(json.getString("joke").split("\n"))
|
||||
} else {
|
||||
jokes.add(json.getString("joke"))
|
||||
}
|
||||
}
|
||||
val enabledFlags = mutableSetOf<Flag>()
|
||||
val jsonFlags = json.getJSONObject("flags")
|
||||
Flag.values().filter { it != Flag.ALL }.forEach {
|
||||
if (jsonFlags.has(it.value) && jsonFlags.getBoolean(it.value)) {
|
||||
enabledFlags.add(it)
|
||||
}
|
||||
}
|
||||
return Joke(
|
||||
category = Category.valueOf(json.getString("category").uppercase()),
|
||||
type = Type.valueOf(json.getString(Parameter.TYPE).uppercase()),
|
||||
joke = jokes,
|
||||
flags = enabledFlags,
|
||||
safe = json.getBoolean("safe"),
|
||||
id = json.getInt("id"),
|
||||
language = Language.valueOf(json.getString(Parameter.LANG).uppercase())
|
||||
)
|
||||
}
|
||||
|
|
@ -44,8 +44,6 @@ import assertk.assertions.isNull
|
|||
import assertk.assertions.prop
|
||||
import assertk.assertions.size
|
||||
import assertk.assertions.startsWith
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.fetchUrl
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.getJoke
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.logger
|
||||
import net.thauvin.erik.jokeapi.exceptions.HttpErrorException
|
||||
import net.thauvin.erik.jokeapi.exceptions.JokeException
|
||||
|
@ -59,8 +57,6 @@ import java.util.logging.ConsoleHandler
|
|||
import java.util.logging.Level
|
||||
|
||||
internal class ExceptionsTest {
|
||||
private val httpStat = "https://httpstat.us"
|
||||
|
||||
@Test
|
||||
fun `Validate Joke Exception`() {
|
||||
val e = assertThrows<JokeException> {
|
||||
|
@ -78,29 +74,19 @@ internal class ExceptionsTest {
|
|||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "HTTP Status Code: {0}")
|
||||
@ValueSource(ints = [400, 404, 403, 413, 414, 429, 500, 523])
|
||||
fun `Validate HTTP Error Exceptions`(input: Int) {
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = [400, 404, 403, 413, 414, 429, 500, 523, 666])
|
||||
fun `Validate HTTP Exceptions`(code: Int) {
|
||||
val e = assertThrows<HttpErrorException> {
|
||||
fetchUrl("$httpStat/$input")
|
||||
fetchUrl("https://httpstat.us/$code")
|
||||
}
|
||||
assertThat(e, "fetchUrl($httpStat/$input)").all {
|
||||
prop(HttpErrorException::statusCode).isEqualTo(input)
|
||||
assertThat(e, "fetchUrl($code)").all {
|
||||
prop(HttpErrorException::statusCode).isEqualTo(code)
|
||||
prop(HttpErrorException::message).isNotNull().isNotEmpty()
|
||||
prop(HttpErrorException::cause).isNotNull().assertThat(Throwable::message).isNotNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Fetch Invalid URL`() {
|
||||
val statusCode = 999
|
||||
val e = assertThrows<HttpErrorException> {
|
||||
fetchUrl("$httpStat/$statusCode")
|
||||
}
|
||||
assertThat(e, "fetchUrl($httpStat/$statusCode).statusCode").all {
|
||||
prop(HttpErrorException::statusCode).isEqualTo(statusCode)
|
||||
prop(HttpErrorException::message).isNotNull().isNotEmpty()
|
||||
prop(HttpErrorException::cause).isNull()
|
||||
if (code < 600)
|
||||
prop(HttpErrorException::cause).isNotNull().assertThat(Throwable::message).isNotNull()
|
||||
else
|
||||
prop(HttpErrorException::cause).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ import assertk.assertions.isNotNull
|
|||
import assertk.assertions.isTrue
|
||||
import assertk.assertions.prop
|
||||
import assertk.assertions.size
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.getJoke
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.logger
|
||||
import net.thauvin.erik.jokeapi.exceptions.JokeException
|
||||
import net.thauvin.erik.jokeapi.models.Category
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* GetJokeTest.kt
|
||||
* GetJokeTests.kt
|
||||
*
|
||||
* Copyright (c) 2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -44,7 +44,6 @@ import assertk.assertions.isNotNull
|
|||
import assertk.assertions.isTrue
|
||||
import assertk.assertions.prop
|
||||
import assertk.assertions.size
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.getJokes
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.logger
|
||||
import net.thauvin.erik.jokeapi.models.Joke
|
||||
import net.thauvin.erik.jokeapi.models.Language
|
||||
|
@ -79,10 +78,6 @@ internal class GetJokesTest {
|
|||
@Test
|
||||
fun `Get One Joke as Multiple`() {
|
||||
val jokes = getJokes(amount = 1, safe = true)
|
||||
jokes.forEach {
|
||||
println(it.joke.joinToString("\n"))
|
||||
println("-".repeat(46))
|
||||
}
|
||||
assertThat(jokes, "jokes").all {
|
||||
size().isEqualTo(1)
|
||||
index(0).all {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* GetRawJokeTest.kt
|
||||
* GetRawJokesTest.kt
|
||||
*
|
||||
* Copyright (c) 2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -37,9 +37,9 @@ import assertk.assertThat
|
|||
import assertk.assertions.doesNotContain
|
||||
import assertk.assertions.isNotEmpty
|
||||
import assertk.assertions.startsWith
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.getRawJokes
|
||||
import net.thauvin.erik.jokeapi.JokeApi.Companion.logger
|
||||
import net.thauvin.erik.jokeapi.models.Format
|
||||
import net.thauvin.erik.jokeapi.models.IdRange
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.logging.ConsoleHandler
|
||||
|
@ -74,6 +74,12 @@ internal class GetRawJokesTest {
|
|||
assertContains(response, "\"amount\": 2", false, "getRawJoke(2)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get Raw Invalid Jokes`() {
|
||||
val response = getRawJokes(search = "foo", safe = true, amount = 2, idRange = IdRange(160, 161))
|
||||
assertContains(response, "\"error\": true", false, "getRawJokes(foo)")
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
|
|
|
@ -60,6 +60,15 @@ import java.util.logging.Level
|
|||
import kotlin.test.assertContains
|
||||
|
||||
class JokeConfigTest {
|
||||
@Test
|
||||
fun `Get Joke with Default Builder`() {
|
||||
val joke = getJoke()
|
||||
assertThat(joke, "joke").all {
|
||||
prop(Joke::id).isGreaterThanOrEqualTo(0)
|
||||
prop(Joke::language).isEqualTo(Language.EN)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get Joke with Builder`() {
|
||||
val id = 266
|
||||
|
@ -132,6 +141,47 @@ class JokeConfigTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Validate Config`() {
|
||||
val categories = setOf(Category.ANY)
|
||||
val language = Language.CS
|
||||
val flags = setOf(Flag.POLITICAL, Flag.RELIGIOUS)
|
||||
val type = Type.TWOPART
|
||||
val format = Format.XML
|
||||
val search = "foo"
|
||||
val idRange = IdRange(1, 20)
|
||||
val amount = 10
|
||||
val safe = true
|
||||
val splitNewLine = true
|
||||
val auth = "token"
|
||||
val config = JokeConfig.Builder().apply {
|
||||
categories(categories)
|
||||
language(language)
|
||||
flags(flags)
|
||||
type(type)
|
||||
format(format)
|
||||
search(search)
|
||||
idRange(idRange)
|
||||
amount(amount)
|
||||
safe(safe)
|
||||
splitNewLine(splitNewLine)
|
||||
auth(auth)
|
||||
}.build()
|
||||
assertThat(config, "config").all {
|
||||
prop(JokeConfig::categories).isEqualTo(categories)
|
||||
prop(JokeConfig::language).isEqualTo(language)
|
||||
prop(JokeConfig::flags).isEqualTo(flags)
|
||||
prop(JokeConfig::type).isEqualTo(type)
|
||||
prop(JokeConfig::format).isEqualTo(format)
|
||||
prop(JokeConfig::search).isEqualTo(search)
|
||||
prop(JokeConfig::idRange).isEqualTo(idRange)
|
||||
prop(JokeConfig::amount).isEqualTo(amount)
|
||||
prop(JokeConfig::safe).isEqualTo(safe)
|
||||
prop(JokeConfig::splitNewLine).isEqualTo(splitNewLine)
|
||||
prop(JokeConfig::auth).isEqualTo(auth)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
|
|
73
src/test/kotlin/net/thauvin/erik/jokeapi/UtilTest.kt
Normal file
73
src/test/kotlin/net/thauvin/erik/jokeapi/UtilTest.kt
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* FetchUrlTest.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
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import java.util.logging.ConsoleHandler
|
||||
import java.util.logging.Level
|
||||
|
||||
class UtilTest {
|
||||
@Test
|
||||
fun `Invalid JSON Error`() {
|
||||
assertThrows<JSONException> { parseError(JSONObject("{}")) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Invalid JSON Joke`() {
|
||||
assertThrows<JSONException> { parseJoke(JSONObject("{}"), false) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Validate Authentication Header`() {
|
||||
val token = "AUTH-TOKEN"
|
||||
val body = fetchUrl("https://postman-echo.com/get", token)
|
||||
assertThat(body, "body").contains("\"authentication\":\"$token\"")
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
fun beforeAll() {
|
||||
with(JokeApi.logger) {
|
||||
addHandler(ConsoleHandler().apply { level = Level.FINE })
|
||||
level = Level.FINE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue