diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index eef517b..251e704 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -5,22 +5,28 @@ on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-latest
+
env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m"
SONAR_JDK: "11"
+
strategy:
matrix:
java-version: [ 1.8, 11, 15 ]
+
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
+
- name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java-version }}
+
- name: Grant execute permission for gradlew
run: chmod +x gradlew
+
- name: Cache SonarCloud packages
if: matrix.java-version == env.SONAR_JDK
uses: actions/cache@v1
@@ -28,6 +34,7 @@ jobs:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
+
- name: Cache Gradle packages
uses: actions/cache@v2
with:
@@ -37,14 +44,17 @@ jobs:
key: ${{ runner.os }}-gradle-${{ matrix.java-version }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-${{ matrix.java-version }}-
+
- name: Test with Gradle
run: ./gradlew build check --stacktrace
+
- name: SonarCloud
if: success() && matrix.java-version == env.SONAR_JDK
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: ./gradlew sonarqube
+
- name: Cleanup Gradle Cache
run: |
rm -f ~/.gradle/caches/modules-2/modules-2.lock
diff --git a/README.md b/README.md
index 2ffab64..3727d81 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,24 @@ dependencies {
```
Instructions for using with Maven, Ivy, etc. can be found on [Maven Central](https://search.maven.org/artifact/net.thauvin.erik/isgd-shorten/0.9.2/jar).
+### Errors
+
+An `IsgdException` is thrown when an API error occurs. The error message (text, XML or JSON) and HTTP status code can be retrieved as follows:
+
+```kotlin
+try {
+ Isgd.shorten("http://is.gd/Pt2sET") // already shorten
+} catch (e: IsgdException)
+ println("Status Code: ${e.statusCode}")
+ println("${e.message})
+}
+```
+
+```
+Status Code: 400
+Error: Sorry, the URL you entered is on our internal blacklist. It may have been used abusively in the past, or it may link to another URL redirection service.
+```
+
### v.gd
Additionally, link can be shortened using [v.gd](https://v.gd/) by setting the `isVgd` flag:
diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml
index 013dc30..f7b49af 100644
--- a/config/detekt/baseline.xml
+++ b/config/detekt/baseline.xml
@@ -3,5 +3,7 @@
LongParameterList:Isgd.kt$Isgd.Companion$( url: String, shorturl: String = "", callback: String = "", logstats: Boolean = false, format: Format = Format.SIMPLE, isVgd: Boolean = false )
+ MagicNumber:Isgd.kt$Isgd.Companion$200
+ MagicNumber:Isgd.kt$Isgd.Companion$399
diff --git a/examples/src/main/java/com/example/IsgdSample.java b/examples/src/main/java/com/example/IsgdSample.java
index 2328776..2f431cb 100644
--- a/examples/src/main/java/com/example/IsgdSample.java
+++ b/examples/src/main/java/com/example/IsgdSample.java
@@ -1,15 +1,20 @@
package com.example;
import net.thauvin.erik.isgd.Isgd;
+import net.thauvin.erik.isgd.IsgdException;
public final class IsgdSample {
public static void main(final String[] args) {
if (args.length > 0) {
for (final String arg : args) {
- if (arg.contains("is.gd")) {
- System.out.println(arg + " <-- " + Isgd.lookup(arg));
- } else {
- System.out.println(arg + " --> " + Isgd.shorten(arg));
+ try {
+ if (arg.contains("is.gd")) {
+ System.out.println(arg + " <-- " + Isgd.lookup(arg));
+ } else {
+ System.out.println(arg + " --> " + Isgd.shorten(arg));
+ }
+ } catch (IsgdException e) {
+ System.out.println(e.getMessage());
}
}
} else {
diff --git a/examples/src/main/kotlin/com/example/IsgdExample.kt b/examples/src/main/kotlin/com/example/IsgdExample.kt
index 5acaad5..fe721a0 100644
--- a/examples/src/main/kotlin/com/example/IsgdExample.kt
+++ b/examples/src/main/kotlin/com/example/IsgdExample.kt
@@ -1,15 +1,20 @@
package com.example
import net.thauvin.erik.isgd.Isgd
+import net.thauvin.erik.isgd.IsgdException
import kotlin.system.exitProcess
fun main(args: Array) {
if (args.isNotEmpty()) {
args.forEach {
- if (it.contains("is.gd"))
- println(it + " <-- " + Isgd.lookup(it))
- else
- println(it + " --> " + Isgd.shorten(it))
+ try {
+ if (it.contains("is.gd"))
+ println(it + " <-- " + Isgd.lookup(it))
+ else
+ println(it + " --> " + Isgd.shorten(it))
+ } catch (e: IsgdException) {
+ println(e.message)
+ }
}
} else {
println("Try specifying one or more URLs as arguments.")
diff --git a/pom.xml b/pom.xml
index 5692130..9012136 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,11 +35,22 @@
GitHub
https://github.com/ethauvin/isgd-shorten/issues
+
+
+
+ org.jetbrains.kotlin
+ kotlin-bom
+ 1.5.10
+ pom
+ import
+
+
+
org.jetbrains.kotlin
kotlin-stdlib-jdk8
- 1.5.0
+ 1.5.10
compile
diff --git a/src/main/kotlin/net/thauvin/erik/isgd/Isgd.kt b/src/main/kotlin/net/thauvin/erik/isgd/Isgd.kt
index c812161..0770277 100644
--- a/src/main/kotlin/net/thauvin/erik/isgd/Isgd.kt
+++ b/src/main/kotlin/net/thauvin/erik/isgd/Isgd.kt
@@ -38,7 +38,7 @@ import java.net.URLEncoder
import java.nio.charset.StandardCharsets
/**
- * See the [is.gd API](https://is.gd/apishorteningreference.php)
+ * See the [is.gd API](https://is.gd/apishorteningreference.php).
*/
enum class Format(val type: String) {
WEB("web"), SIMPLE("simple"), XML("xml"), JSON("json")
@@ -48,18 +48,25 @@ fun String.encode(): String {
return URLEncoder.encode(this, StandardCharsets.UTF_8.name())
}
+/**
+ * Implements the [is.gd API](https://is.gd/developers.php).
+ */
class Isgd private constructor() {
companion object {
private fun callApi(url: String): String {
val connection = URL(url).openConnection() as HttpURLConnection
connection.setRequestProperty(
"User-Agent",
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0"
+ "Mozilla/5.0 (Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"
)
- return connection.inputStream.bufferedReader().readText()
+ if (connection.responseCode in 200..399) {
+ return connection.inputStream.bufferedReader().readText()
+ } else {
+ throw IsgdException(connection.responseCode, connection.errorStream.bufferedReader().readText())
+ }
}
- private fun host(isVgd: Boolean = false): String {
+ private fun getHost(isVgd: Boolean = false): String {
return if (isVgd) "v.gd" else "is.gd"
}
@@ -68,6 +75,7 @@ class Isgd private constructor() {
*/
@JvmStatic
@JvmOverloads
+ @Throws(IsgdException::class)
fun lookup(
shorturl: String,
callback: String = "",
@@ -78,7 +86,7 @@ class Isgd private constructor() {
throw IllegalArgumentException("Please specify a valid short URL to lookup.")
}
- val sb = StringBuilder("https://${host(isVgd)}/forward.php?shorturl=${shorturl.encode()}")
+ val sb = StringBuilder("https://${getHost(isVgd)}/forward.php?shorturl=${shorturl.encode()}")
if (callback.isNotEmpty()) {
sb.append("&callback=${callback.encode()}")
@@ -94,6 +102,7 @@ class Isgd private constructor() {
*/
@JvmStatic
@JvmOverloads
+ @Throws(IsgdException::class)
fun shorten(
url: String,
shorturl: String = "",
@@ -106,7 +115,7 @@ class Isgd private constructor() {
throw IllegalArgumentException("Please enter a valid URL to shorten.")
}
- val sb = StringBuilder("https://${host(isVgd)}/create.php?url=${url.encode()}")
+ val sb = StringBuilder("https://${getHost(isVgd)}/create.php?url=${url.encode()}")
if (shorturl.isNotEmpty()) {
sb.append("&shorturl=${shorturl.encode()}")
diff --git a/src/main/kotlin/net/thauvin/erik/isgd/IsgdException.kt b/src/main/kotlin/net/thauvin/erik/isgd/IsgdException.kt
new file mode 100644
index 0000000..2fabf3f
--- /dev/null
+++ b/src/main/kotlin/net/thauvin/erik/isgd/IsgdException.kt
@@ -0,0 +1,45 @@
+/*
+ * IsgdException.kt
+ *
+ * Copyright (c) 2020-2021, 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.isgd
+
+/**
+ * Thrown when an exceptional condition has occurred.
+ *
+ * @property statusCode The HTTP status code.
+ * @property message The error message.
+ */
+class IsgdException(val statusCode: Int, message: String) : Exception(message) {
+ companion object {
+ private const val serialVersionUID = 1L
+ }
+}
diff --git a/src/test/kotlin/net/thauvin/erik/isgd/IsgdTest.kt b/src/test/kotlin/net/thauvin/erik/isgd/IsgdTest.kt
index b6ecd6b..1a1fd39 100644
--- a/src/test/kotlin/net/thauvin/erik/isgd/IsgdTest.kt
+++ b/src/test/kotlin/net/thauvin/erik/isgd/IsgdTest.kt
@@ -34,6 +34,7 @@ package net.thauvin.erik.isgd
import kotlin.test.Test
import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class IsgdTest {
@@ -41,6 +42,21 @@ class IsgdTest {
private val shortUrl = "https://is.gd/Pt2sET"
private val shortVgdUrl = "https://v.gd/2z2ncj"
+ @Test
+ fun testException() {
+ assertFailsWith(
+ message = "URL is already shorten",
+ exceptionClass = IsgdException::class,
+ block = { Isgd.shorten(shortUrl) }
+ )
+
+ try {
+ Isgd.shorten(shortUrl)
+ } catch (e: IsgdException) {
+ assertTrue(e.statusCode == 400, "status code == 400")
+ }
+ }
+
@Test
fun testLookupDefault() {
assertEquals(url, Isgd.lookup(shortUrl))