Added configuration builder

This commit is contained in:
Erik C. Thauvin 2022-09-26 14:22:13 -07:00
parent a2636f404d
commit 6925d68db7
8 changed files with 241 additions and 6 deletions

6
.idea/compiler.xml generated
View file

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" /> <bytecodeTargetLevel target="11">
<module name="jokeapi-examples.app" target="18" />
<module name="jokeapi-examples.app.main" target="18" />
<module name="jokeapi-examples.app.test" target="18" />
</bytecodeTargetLevel>
</component> </component>
</project> </project>

View file

@ -21,5 +21,10 @@
<option name="name" value="maven" /> <option name="name" value="maven" />
<option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" /> <option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" />
</remote-repository> </remote-repository>
<remote-repository>
<option name="id" value="MavenLocal" />
<option name="name" value="MavenLocal" />
<option name="url" value="file:$PROJECT_DIR$/../../maven/repository/" />
</remote-repository>
</component> </component>
</project> </project>

View file

@ -99,6 +99,21 @@ lang: "en"
``` ```
- View more [examples](https://github.com/ethauvin/jokeapi/blob/master/src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokeTest.kt)... - View more [examples](https://github.com/ethauvin/jokeapi/blob/master/src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokeTest.kt)...
## Java
To make it easier to use the library with Java, a configuration builder is also available:
```java
var config = new JokeConfig.Builder()
.type(Type.SINGLE)
.safe(true)
.build();
var joke = JokeApi.getJoke(config);
for (String j : joke.getJoke()) {
System.out.println(j);
}
```
## Extending ## Extending
A generic `apiCall()` function is available to access other [JokeAPI endpoints](https://v2.jokeapi.dev/#endpoints). A generic `apiCall()` function is available to access other [JokeAPI endpoints](https://v2.jokeapi.dev/#endpoints).

View file

@ -2,9 +2,10 @@
<SmellBaseline> <SmellBaseline>
<ManuallySuppressedIssues/> <ManuallySuppressedIssues/>
<CurrentIssues> <CurrentIssues>
<ID>ComplexMethod:JokeApi.kt$JokeApi.Companion$@JvmStatic @JvmOverloads @Throws(HttpErrorException::class, IOException::class) fun getRawJoke( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, ): String</ID> <ID>ComplexMethod:JokeApi.kt$JokeApi.Companion$@JvmStatic @Throws(HttpErrorException::class, IOException::class) fun getRawJoke( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;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$( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;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&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;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&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = true )</ID> <ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = true )</ID>
<ID>LongParameterList:JokeConfig.kt$JokeConfig$( val categories: Set&lt;Category>, val language: Language, val flags: Set&lt;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>LongParameterList:JokeException.kt$JokeException$( val error: Boolean, val internalError: Boolean, val code: Int, message: String, val causedBy: List&lt;String>, val additionalInfo: String, val timestamp: Long, cause: Throwable? = null )</ID> <ID>LongParameterList:JokeException.kt$JokeException$( val error: Boolean, val internalError: Boolean, val code: Int, message: String, val causedBy: List&lt;String>, val additionalInfo: String, val timestamp: Long, cause: Throwable? = null )</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$10</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$10</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$200</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$200</ID>
@ -17,6 +18,7 @@
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$429</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$429</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$500</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$500</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$523</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$523</ID>
<ID>TooManyFunctions:JokeConfig.kt$JokeConfig$Builder</ID>
<ID>UtilityClassWithPublicConstructor:JokeApi.kt$JokeApi</ID> <ID>UtilityClassWithPublicConstructor:JokeApi.kt$JokeApi</ID>
</CurrentIssues> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View file

@ -94,7 +94,6 @@ class JokeApi {
} }
@JvmStatic @JvmStatic
@JvmOverloads
@Throws(HttpErrorException::class, IOException::class) @Throws(HttpErrorException::class, IOException::class)
fun getRawJoke( fun getRawJoke(
categories: Set<Category> = setOf(Category.ANY), categories: Set<Category> = setOf(Category.ANY),
@ -171,6 +170,22 @@ class JokeApi {
return apiCall(JOKE_ENDPOINT, path, params) return apiCall(JOKE_ENDPOINT, path, params)
} }
@JvmStatic
@Throws(HttpErrorException::class, IOException::class)
fun getRawJoke(config: JokeConfig): String {
return getRawJoke(
categories = config.categories,
language = config.language,
flags = config.flags,
type = config.type,
format = config.format,
search = config.search,
idRange = config.idRange,
amount = config.amount,
safe = config.safe
)
}
@Throws(HttpErrorException::class, IOException::class) @Throws(HttpErrorException::class, IOException::class)
internal fun fetchUrl(url: String): String { internal fun fetchUrl(url: String): String {
if (logger.isLoggable(Level.FINE)) { if (logger.isLoggable(Level.FINE)) {
@ -251,7 +266,6 @@ class JokeApi {
} }
@JvmStatic @JvmStatic
@JvmOverloads
@Throws(JokeException::class, HttpErrorException::class, IOException::class) @Throws(JokeException::class, HttpErrorException::class, IOException::class)
fun getJoke( fun getJoke(
categories: Set<Category> = setOf(Category.ANY), categories: Set<Category> = setOf(Category.ANY),
@ -312,5 +326,20 @@ class JokeApi {
) )
} }
} }
@JvmStatic
@Throws(JokeException::class, HttpErrorException::class, IOException::class)
fun getJoke(config: JokeConfig): Joke {
return getJoke(
categories = config.categories,
language = config.language,
flags = config.flags,
type = config.type,
search = config.search,
idRange = config.idRange,
safe = config.safe,
splitNewLine = config.splitNewLine
)
}
} }
} }

View file

@ -0,0 +1,81 @@
/*
* Configuration.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.models.Category
import net.thauvin.erik.jokeapi.models.Flag
import net.thauvin.erik.jokeapi.models.Format
import net.thauvin.erik.jokeapi.models.IdRange
import net.thauvin.erik.jokeapi.models.Language
import net.thauvin.erik.jokeapi.models.Type
class JokeConfig private constructor(
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,
) {
data class Builder(
var categories: Set<Category> = setOf(Category.ANY),
var language: Language = Language.ENGLISH,
var flags: Set<Flag> = emptySet(),
var type: Type = Type.ALL,
var format: Format = Format.JSON,
var search: String = "",
var idRange: IdRange = IdRange(),
var amount: Int = 1,
var safe: Boolean = false,
var splitNewLine: Boolean = true
) {
fun categories(categories: Set<Category>) = apply { this.categories = categories }
fun language(language: Language) = apply { this.language = language }
fun flags(flags: Set<Flag>) = apply { this.flags = flags }
fun type(type: Type) = apply { this.type = type }
fun format(format: Format) = apply { this.format = format }
fun search(search: String) = apply { this.search = search }
fun idRange(idRange: IdRange) = apply { this.idRange = idRange }
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 build() = JokeConfig(
categories, language, flags, type, format, search, idRange, amount, safe, splitNewLine
)
}
}

View file

@ -32,10 +32,8 @@
package net.thauvin.erik.jokeapi package net.thauvin.erik.jokeapi
import net.thauvin.erik.jokeapi.JokeApi.Companion.fetchUrl
import net.thauvin.erik.jokeapi.JokeApi.Companion.getJoke import net.thauvin.erik.jokeapi.JokeApi.Companion.getJoke
import net.thauvin.erik.jokeapi.JokeApi.Companion.logger import net.thauvin.erik.jokeapi.JokeApi.Companion.logger
import net.thauvin.erik.jokeapi.exceptions.HttpErrorException
import net.thauvin.erik.jokeapi.exceptions.JokeException import net.thauvin.erik.jokeapi.exceptions.JokeException
import net.thauvin.erik.jokeapi.models.Category import net.thauvin.erik.jokeapi.models.Category
import net.thauvin.erik.jokeapi.models.Flag import net.thauvin.erik.jokeapi.models.Flag

View file

@ -0,0 +1,101 @@
/*
* JokeConfigTest.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.JokeApi.Companion.getJoke
import net.thauvin.erik.jokeapi.JokeApi.Companion.getRawJoke
import net.thauvin.erik.jokeapi.JokeApi.Companion.logger
import net.thauvin.erik.jokeapi.models.Category
import net.thauvin.erik.jokeapi.models.Flag
import net.thauvin.erik.jokeapi.models.Format
import net.thauvin.erik.jokeapi.models.IdRange
import net.thauvin.erik.jokeapi.models.Language
import net.thauvin.erik.jokeapi.models.Type
import org.junit.jupiter.api.Assertions.assertAll
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import java.util.logging.ConsoleHandler
import java.util.logging.Level
class JokeConfigTest {
@Test
fun `Get Joke with Builder`() {
val id = 266
val config = JokeConfig.Builder().apply {
categories(setOf(Category.PROGRAMMING))
language(Language.EN)
flags(setOf(Flag.ALL))
type(Type.TWOPART)
idRange(IdRange(id - 2, id + 2))
safe(true)
splitNewLine(false)
}.build()
val joke = getJoke(config)
logger.fine(joke.toString())
assertAll("Two-Parts Joke",
{ assertEquals(Type.TWOPART, joke.type, "type should be two-part") },
{ assertEquals(joke.category, Category.PROGRAMMING) { "category should be ${Category.PROGRAMMING}" } },
{ assertEquals(joke.joke.size, 2, "should have two lines") },
{ assertEquals(joke.language, Language.EN, "language should be english") },
{ assertTrue(joke.flags.isEmpty(), "flags should empty") },
{ assertTrue(joke.id in id - 2..id + 2) { "id should be $id +- 2" } })
}
@Test
fun `Get Raw Joke with Builder`() {
val config = JokeConfig.Builder().apply {
categories(setOf(Category.PROGRAMMING))
format(Format.TEXT)
search("bar")
amount(2)
safe(true)
}.build()
val joke = getRawJoke(config)
assertTrue(
joke.contains("----------------------------------------------"), "should contain -- delimiter"
)
}
companion object {
@JvmStatic
@BeforeAll
fun beforeAll() {
with(logger) {
addHandler(ConsoleHandler().apply { level = Level.FINE })
level = Level.FINE
}
}
}
}