Implemented errorMessage and JSON (de)serialization.
This commit is contained in:
parent
139048d7c4
commit
8a20a5cdb2
5 changed files with 154 additions and 43 deletions
|
@ -16,6 +16,7 @@ plugins {
|
|||
id("net.thauvin.erik.gradle.semver") version "1.0.4"
|
||||
id("org.jetbrains.dokka") version "0.9.18"
|
||||
id("org.jetbrains.kotlin.kapt").version("1.3.50")
|
||||
id("org.jetbrains.kotlin.plugin.serialization").version("1.3.50")
|
||||
id("org.jmailen.kotlinter") version "2.1.1"
|
||||
id("org.sonarqube") version "2.7.1"
|
||||
}
|
||||
|
@ -59,6 +60,7 @@ dependencies {
|
|||
compile("com.squareup.okhttp3:logging-interceptor:4.2.0")
|
||||
|
||||
compile(kotlin("stdlib"))
|
||||
compile("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.13.0")
|
||||
|
||||
testImplementation("org.mockito:mockito-core:3.0.0")
|
||||
testImplementation("org.testng:testng:7.0.0")
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
<SmellBaseline>
|
||||
<Blacklist></Blacklist>
|
||||
<Whitelist>
|
||||
<ID>ComplexMethod:Akismet.kt$Akismet$private fun buildFormBody( userIp: String, userAgent: String, referrer: String, permalink: String, type: String, author: String, authorEmail: String, authorUrl: String, content: String, dateGmt: String, postModifiedGmt: String, blogLang: String, blogCharset: String, userRole: String, isTest: Boolean, recheckReason: String, other: Map<String, String> ): FormBody</ID>
|
||||
<ID>LongMethod:Akismet.kt$Akismet$private fun buildFormBody( userIp: String, userAgent: String, referrer: String, permalink: String, type: String, author: String, authorEmail: String, authorUrl: String, content: String, dateGmt: String, postModifiedGmt: String, blogLang: String, blogCharset: String, userRole: String, isTest: Boolean, recheckReason: String, other: Map<String, String> ): FormBody</ID>
|
||||
<ID>ComplexMethod:Akismet.kt$Akismet$ @JvmOverloads fun executeMethod(apiUrl: HttpUrl?, formBody: FormBody, trueOnError: Boolean = false): Boolean</ID>
|
||||
<ID>ComplexMethod:Akismet.kt$Akismet$private fun buildFormBody(comment: AkismetComment): FormBody</ID>
|
||||
<ID>ComplexMethod:AkismetComment.kt$AkismetComment$ @Suppress("DuplicatedCode") override fun equals(other: Any?): Boolean</ID>
|
||||
<ID>MagicNumber:Akismet.kt$Akismet$12</ID>
|
||||
<ID>MagicNumber:Akismet.kt$Akismet.<no name provided>$8</ID>
|
||||
<ID>NestedBlockDepth:Akismet.kt$Akismet$ @Suppress("MemberVisibilityCanBePrivate") protected fun executeMethod(apiUrl: HttpUrl?, formBody: FormBody): Boolean</ID>
|
||||
<ID>NestedBlockDepth:AkismetTest.kt$fun getApiKey(): String</ID>
|
||||
<ID>MaxLineLength:Akismet.kt$Akismet$/** * The _x-akismet-pro-tip_ header from the last operation, if any. * * If the _x-akismet-pro-tip_ header is set to discard, then Akismet has determined that the comment is blatant spam, * and you can safely discard it without saving it in any spam queue. * * Read more about this feature in this * [Akismet blog post](https://blog.akismet.com/2014/04/23/theres-a-ninja-in-your-akismet/). * * @see [Akismet.isDiscard] */ @Suppress("MemberVisibilityCanBePrivate") var proTip: String = "" private set</ID>
|
||||
<ID>NestedBlockDepth:Akismet.kt$Akismet$ @JvmOverloads fun executeMethod(apiUrl: HttpUrl?, formBody: FormBody, trueOnError: Boolean = false): Boolean</ID>
|
||||
<ID>NestedBlockDepth:AkismetTest.kt$fun getKey(key: String): String</ID>
|
||||
<ID>ReturnCount:Akismet.kt$Akismet$ @JvmOverloads fun executeMethod(apiUrl: HttpUrl?, formBody: FormBody, trueOnError: Boolean = false): Boolean</ID>
|
||||
<ID>TooManyFunctions:Akismet.kt$Akismet</ID>
|
||||
</Whitelist>
|
||||
</SmellBaseline>
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
*/
|
||||
package net.thauvin.erik.akismet
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import net.thauvin.erik.semver.Version
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.HttpUrl
|
||||
|
@ -108,6 +110,16 @@ open class Akismet(apiKey: String) {
|
|||
var response: String = ""
|
||||
private set
|
||||
|
||||
/**
|
||||
* The error message.
|
||||
*
|
||||
* The error (IO, empty response from Akismet, etc.) message is also logged as a warning.
|
||||
*
|
||||
* @see [Akismet.checkComment]
|
||||
*/
|
||||
var errorMessage: String = ""
|
||||
private set
|
||||
|
||||
/**
|
||||
* The _x-akismet-pro-tip_ header from the last operation, if any.
|
||||
*
|
||||
|
@ -269,6 +281,15 @@ open class Akismet(apiKey: String) {
|
|||
return executeMethod(buildApiUrl("submit-ham"), buildFormBody(comment))
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)Create a [comment][AkismetComment] from a JSON string.
|
||||
*
|
||||
* @see [AkismetComment.toString]
|
||||
*/
|
||||
fun jsonComment(json: String): AkismetComment {
|
||||
return Json(JsonConfiguration.Stable).parse(AkismetComment.serializer(), json)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a date to a UTC timestamp. (ISO 8601)
|
||||
*
|
||||
|
@ -304,7 +325,11 @@ open class Akismet(apiKey: String) {
|
|||
fun executeMethod(apiUrl: HttpUrl?, formBody: FormBody, trueOnError: Boolean = false): Boolean {
|
||||
reset()
|
||||
if (apiUrl != null) {
|
||||
val request = Request.Builder().url(apiUrl).post(formBody).header("User-Agent", buildUserAgent()).build()
|
||||
val request = if (formBody.size == 0) {
|
||||
Request.Builder().url(apiUrl).header("User-Agent", buildUserAgent()).build()
|
||||
} else {
|
||||
Request.Builder().url(apiUrl).post(formBody).header("User-Agent", buildUserAgent()).build()
|
||||
}
|
||||
try {
|
||||
val result = client.newCall(request).execute()
|
||||
httpStatusCode = result.code
|
||||
|
@ -317,26 +342,28 @@ open class Akismet(apiKey: String) {
|
|||
if (response == "valid" || response == "true" || response.startsWith("Thanks")) {
|
||||
return true
|
||||
} else if (response != "false" && response != "invalid") {
|
||||
logger.warning("Unexpected response: $body")
|
||||
return trueOnError
|
||||
errorMessage = "Unexpected response: " + if (body.isBlank()) "(0-byte body)" else body
|
||||
}
|
||||
} else {
|
||||
val message = "An empty response was received from Akismet."
|
||||
if (debugHelp.isNotBlank()) {
|
||||
logger.warning("$message: $debugHelp")
|
||||
errorMessage = if (debugHelp.isNotBlank()) {
|
||||
"$message: $debugHelp"
|
||||
} else {
|
||||
logger.warning(message)
|
||||
message
|
||||
}
|
||||
return trueOnError
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
logger.log(Level.SEVERE, "An IO error occurred while communicating with the Akismet service.", e)
|
||||
return trueOnError
|
||||
errorMessage = "An IO error occurred while communicating with the Akismet service."
|
||||
}
|
||||
} else {
|
||||
logger.severe("Invalid API end point URL.")
|
||||
errorMessage = "Invalid API end point URL."
|
||||
}
|
||||
|
||||
if (errorMessage.isNotEmpty()) {
|
||||
logger.warning(errorMessage)
|
||||
return trueOnError
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -345,6 +372,7 @@ open class Akismet(apiKey: String) {
|
|||
*/
|
||||
fun reset() {
|
||||
debugHelp = ""
|
||||
errorMessage = ""
|
||||
httpStatusCode = 0
|
||||
isDiscard = false
|
||||
isVerifiedKey = false
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
|
||||
package net.thauvin.erik.akismet
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
/**
|
||||
|
@ -50,6 +53,7 @@ import javax.servlet.http.HttpServletRequest
|
|||
* @param userIp IP address of the comment submitter.
|
||||
* @param userAgent User agent string of the web browser submitting the comment.
|
||||
*/
|
||||
@Serializable
|
||||
open class AkismetComment(val userIp: String, val userAgent: String) {
|
||||
@Suppress("unused")
|
||||
companion object {
|
||||
|
@ -171,27 +175,68 @@ open class AkismetComment(val userIp: String, val userAgent: String) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the comment.
|
||||
* Returns a JSON representation of the comment.
|
||||
*
|
||||
* @see [Akismet.jsonComment]
|
||||
*/
|
||||
override fun toString(): String {
|
||||
return this.javaClass.simpleName +
|
||||
"(userIp=$userIp" +
|
||||
", userAgent=$userAgent" +
|
||||
", referrer=$referrer" +
|
||||
", permalink=$permalink" +
|
||||
", type=$type" +
|
||||
", author=$author" +
|
||||
", authorEmail=$authorEmail" +
|
||||
", authorUrl=$authorUrl" +
|
||||
", content=$content" +
|
||||
", dateGmt=$dateGmt" +
|
||||
", postModifiedGmt=$postModifiedGmt" +
|
||||
", blogLang=$blogLang" +
|
||||
", blogCharset=$blogCharset" +
|
||||
", userRole=$userRole" +
|
||||
", isTest=$isTest" +
|
||||
", recheckReason=$recheckReason" +
|
||||
", serverEnv=$serverEnv)"
|
||||
return Json(JsonConfiguration.Stable).stringify(serializer(), this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether some other object is _equal to_ this one.
|
||||
*/
|
||||
@Suppress("DuplicatedCode")
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AkismetComment
|
||||
|
||||
if (userIp != other.userIp) return false
|
||||
if (userAgent != other.userAgent) return false
|
||||
if (referrer != other.referrer) return false
|
||||
if (permalink != other.permalink) return false
|
||||
if (type != other.type) return false
|
||||
if (author != other.author) return false
|
||||
if (authorEmail != other.authorEmail) return false
|
||||
if (authorUrl != other.authorUrl) return false
|
||||
if (content != other.content) return false
|
||||
if (dateGmt != other.dateGmt) return false
|
||||
if (postModifiedGmt != other.postModifiedGmt) return false
|
||||
if (blogLang != other.blogLang) return false
|
||||
if (blogCharset != other.blogCharset) return false
|
||||
if (userRole != other.userRole) return false
|
||||
if (isTest != other.isTest) return false
|
||||
if (recheckReason != other.recheckReason) return false
|
||||
if (serverEnv != other.serverEnv) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code value for the object.
|
||||
*/
|
||||
@Suppress("DuplicatedCode")
|
||||
override fun hashCode(): Int {
|
||||
var result = userIp.hashCode()
|
||||
result = 31 * result + userAgent.hashCode()
|
||||
result = 31 * result + referrer.hashCode()
|
||||
result = 31 * result + permalink.hashCode()
|
||||
result = 31 * result + type.hashCode()
|
||||
result = 31 * result + author.hashCode()
|
||||
result = 31 * result + authorEmail.hashCode()
|
||||
result = 31 * result + authorUrl.hashCode()
|
||||
result = 31 * result + content.hashCode()
|
||||
result = 31 * result + dateGmt.hashCode()
|
||||
result = 31 * result + postModifiedGmt.hashCode()
|
||||
result = 31 * result + blogLang.hashCode()
|
||||
result = 31 * result + blogCharset.hashCode()
|
||||
result = 31 * result + userRole.hashCode()
|
||||
result = 31 * result + isTest.hashCode()
|
||||
result = 31 * result + recheckReason.hashCode()
|
||||
result = 31 * result + serverEnv.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|||
import org.mockito.Mockito
|
||||
import org.testng.Assert.assertEquals
|
||||
import org.testng.Assert.assertFalse
|
||||
import org.testng.Assert.assertNotEquals
|
||||
import org.testng.Assert.assertTrue
|
||||
import org.testng.Assert.expectThrows
|
||||
import org.testng.annotations.BeforeClass
|
||||
|
@ -179,12 +180,37 @@ class AkismetTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmptyResponse() {
|
||||
assertTrue(
|
||||
akismet.executeMethod(
|
||||
"https://postman-echo.com/status/200".toHttpUrlOrNull(), FormBody.Builder().build(), true
|
||||
)
|
||||
)
|
||||
val expected = "{\"status\":200}"
|
||||
assertEquals(akismet.response, expected, expected)
|
||||
assertTrue(akismet.errorMessage.contains(expected), "errorMessage contains $expected")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testProTipResponse() {
|
||||
assertFalse(
|
||||
akismet.executeMethod(
|
||||
"https://postman-echo.com/response-headers?x-akismet-pro-tip=test".toHttpUrlOrNull(),
|
||||
FormBody.Builder().build()
|
||||
)
|
||||
)
|
||||
assertEquals(akismet.proTip, "test")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resetTest() {
|
||||
akismet.reset()
|
||||
|
||||
//@TODO fix
|
||||
assertTrue(
|
||||
akismet.debugHelp == "" && akismet.httpStatusCode == 0 && !akismet.isDiscard &&
|
||||
!akismet.isVerifiedKey && akismet.proTip == "" && akismet.response == ""
|
||||
akismet.debugHelp == "" && akismet.errorMessage == "" && akismet.httpStatusCode == 0 &&
|
||||
!akismet.isDiscard && !akismet.isVerifiedKey && akismet.proTip == "" && akismet.response == ""
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -225,18 +251,25 @@ class AkismetTest {
|
|||
assertTrue(akismet.submitSpam(mockComment), "submitHam(request)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJsonComment() {
|
||||
val jsonComment = akismet.jsonComment(mockComment.toString())
|
||||
|
||||
assertEquals(jsonComment, mockComment, "equals")
|
||||
assertEquals(jsonComment.hashCode(), mockComment.hashCode(), "hashcode")
|
||||
|
||||
assertNotEquals(jsonComment, comment, "json is different")
|
||||
assertNotEquals(jsonComment.hashCode(), comment.hashCode(), "json hashcode is different")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBuildUserAgent() {
|
||||
val libAgent = "${GeneratedVersion.PROJECT}/${GeneratedVersion.VERSION}"
|
||||
assertEquals(
|
||||
akismet.buildUserAgent(), libAgent, "libAgent"
|
||||
)
|
||||
akismet.applicationName = "My App"
|
||||
assertEquals(akismet.buildUserAgent(), libAgent, "libAgent")
|
||||
|
||||
akismet.applicationName = "My App"
|
||||
assertEquals(akismet.buildUserAgent(), libAgent, "libAgent, no app")
|
||||
|
||||
assertEquals(
|
||||
akismet.buildUserAgent(), libAgent, "libAgent, no app"
|
||||
)
|
||||
|
||||
akismet.applicationVersion = "1.0-test"
|
||||
assertEquals(
|
||||
akismet.buildUserAgent(), "${akismet.applicationName}/${akismet.applicationVersion} | $libAgent",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue