diff --git a/README.md b/README.md index 262bc60..8b753a1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ poster.addPin("http://www.example.com/foo", "This is a test") poster.deletePin("http:///www.example.com/bar") ``` -[View Example](https://github.com/ethauvin/pinboard-poster/blob/master/src/main/kotlin/net/thauvin/erik/pinboard/PinboardPoster.kt#L203) +[View Example](https://github.com/ethauvin/pinboard-poster/blob/master/src/main/kotlin/net/thauvin/erik/pinboard/PinboardPoster.kt#L219) ### Java ```java @@ -116,6 +116,46 @@ logger.setLevel(Level.FINE); or using a logging properties file. +## API Authentication Token + +The token can also be located in a [properties file](https://en.wikipedia.org/wiki/.properties) or environment variable. + +### Local Property + +For example, using the default `PINBOARD_API_TOKEN` key value from a `local.properties` file: + +```ini +# local.properties +PINBOARD_API_TOKEN=user\:TOKEN +``` + +```kotlin +val poster = PinboardPoster(Paths.get("local.properties")) +``` + +To specify your own key: + +```ini +# my.properties +my.api.key=user\:TOKEN +``` + +```kotlin +val poster = PinboardPoster(Paths.get("my.properties"), "my.api.key") +``` + +### Environment Variable + +If no arguments are passed to the constructor, the `PINBOARD_API_TOKEN` environment variable will be used, if any. + +```sh +export PINBOARD_API_TOKEN="user:TOKEN" +``` + +```kotlin +val poster = PinboardPoster() +``` + ## API End Point The API end point is automatically configured to `https://api.pinboard.in/v1/`. Since Pinboard uses the `del.ico.us` API, the library could potentially be used with another compatible service. To configure the API end point, use: diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index d030b58..03e6f1d 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -11,9 +11,6 @@ import org.apache.maven.model.Developer import org.apache.maven.model.License import org.apache.maven.model.Model import org.apache.maven.model.Scm -import java.io.File -import java.io.FileInputStream -import java.util.* val bs = buildScript { plugins("net.thauvin.erik:kobalt-versioneye:", "net.thauvin.erik:kobalt-maven-local:") @@ -26,12 +23,6 @@ val p = project { artifactId = name version = "0.9.2" - val localProperties = Properties().apply { - val f = "local.properties" - if (File(f).exists()) FileInputStream(f).use { fis -> load(fis) } - } - val apiToken = localProperties.getProperty("pinboard-api-token", "") - pom = Model().apply { description = project.description url = "https://github.com/ethauvin/pinboard-poster" @@ -67,13 +58,13 @@ val p = project { application { mainClass = "net.thauvin.erik.pinboard.PinboardPosterKt" - args(apiToken) + ignoreErrorStream = true } application { taskName = "runJava" mainClass = "net.thauvin.erik.pinboard.JavaExample" - args(apiToken) + ignoreErrorStream = true } install { diff --git a/src/main/java/net/thauvin/erik/pinboard/JavaExample.java b/src/main/java/net/thauvin/erik/pinboard/JavaExample.java index 214e3ef..d5b4ff4 100644 --- a/src/main/java/net/thauvin/erik/pinboard/JavaExample.java +++ b/src/main/java/net/thauvin/erik/pinboard/JavaExample.java @@ -31,15 +31,52 @@ */ package net.thauvin.erik.pinboard; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + public class JavaExample { public static void main(String[] args) { final String url = "http://www.example.com/pinboard"; - final PinboardPoster poster = new PinboardPoster(args[0]); + final Path properties = Paths.get("local.properties"); + final PinboardPoster poster; + if (args.length == 1) { + // API Token is an argument + poster = new PinboardPoster(args[0]); + } else if (Files.exists(properties)) { + // API Token is in local.properties (PINBOARD_API_TOKEN) + final Properties p = new Properties(); + try (final InputStream stream = Files.newInputStream(properties)) { + p.load(stream); + } catch (IOException ignore) { + ; + } + poster = new PinboardPoster(p); + } else { + // API Token is an environment variable (PINBOARD_API_TOKEN) or empty + poster = new PinboardPoster(); + } + + // Set logging levels + final ConsoleHandler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.FINE); + final Logger logger = poster.getLogger(); + logger.addHandler(consoleHandler); + logger.setLevel(Level.FINE); + + // Add Pin if (poster.addPin(url, "Testing", "Extended test", "test kotlin")) { System.out.println("Added: " + url); } + // Delete Pin if (poster.deletePin(url)) { System.out.println("Deleted: " + url); } diff --git a/src/main/kotlin/net/thauvin/erik/pinboard/PinboardPoster.kt b/src/main/kotlin/net/thauvin/erik/pinboard/PinboardPoster.kt index 9fef784..191c796 100644 --- a/src/main/kotlin/net/thauvin/erik/pinboard/PinboardPoster.kt +++ b/src/main/kotlin/net/thauvin/erik/pinboard/PinboardPoster.kt @@ -31,12 +31,16 @@ */ package net.thauvin.erik.pinboard +import net.thauvin.erik.pinboard.Constants.ENV_API_TOKEN import okhttp3.HttpUrl import okhttp3.OkHttpClient import okhttp3.Request import org.xml.sax.InputSource import java.io.StringReader import java.net.URL +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* import java.util.logging.ConsoleHandler import java.util.logging.Level import java.util.logging.Logger @@ -46,9 +50,21 @@ object Constants { const val API_ENDPOINT = "https://api.pinboard.in/v1/" const val AUTH_TOKEN = "auth_token" const val DONE = "done" + const val ENV_API_TOKEN = "PINBOARD_API_TOKEN" } -open class PinboardPoster(var apiToken: String) { +open class PinboardPoster() { + constructor(apiToken: String) : this() { + this.apiToken = apiToken + } + + @JvmOverloads + constructor(properties: Properties, key: String = ENV_API_TOKEN) : this() { + this.apiToken = properties.getProperty(key, "") + } + + var apiToken: String = if (System.getenv(ENV_API_TOKEN).isNullOrBlank()) "" else System.getenv(ENV_API_TOKEN) + var apiEndPoint: String = Constants.API_ENDPOINT val logger: Logger by lazy { Logger.getLogger(PinboardPoster::class.java.simpleName) } @@ -201,23 +217,37 @@ open class PinboardPoster(var apiToken: String) { } fun main(args: Array) { - if (args.size == 1) { - val url = "http://www.example.com/pinboard" - val poster = PinboardPoster(args[0]) + val url = "http://www.example.com/pinboard" + val properties = Paths.get("local.properties") + val poster = when { + args.size == 1 -> + // API Token is an argument + PinboardPoster(args[0]) + Files.exists(properties) -> + // API Token is in a local.properties (PINBOARD_API_TOKEN) + PinboardPoster( + Properties().apply { + Files.newInputStream(properties).use { fis -> load(fis) } + }.getProperty(ENV_API_TOKEN, "") + ) + else -> + // API Token is an environment variable (PINBOARD_API_TOKEN) or empty; + PinboardPoster() + } - with(poster.logger) { - addHandler(ConsoleHandler().apply { level = Level.FINE }) - level = Level.FINE - } + // Set logging levels + with(poster.logger) { + addHandler(ConsoleHandler().apply { level = Level.FINE }) + level = Level.FINE + } - if (poster.addPin(url, "Testing", "Extended test", "test kotlin")) { - println("Added: $url") - } + // Add Pin + if (poster.addPin(url, "Testing", "Extended test", "test kotlin")) { + println("Added: $url") + } - if (poster.deletePin(url)) { - println("Deleted: $url") - } - } else { - println("Please specify a valid API token. (eg. user:TOKEN)") + // Delete Pin + if (poster.deletePin(url)) { + println("Deleted: $url") } } \ No newline at end of file diff --git a/src/test/kotlin/net/thauvin/erik/pinboard/PinboardPosterTest.kt b/src/test/kotlin/net/thauvin/erik/pinboard/PinboardPosterTest.kt index 255a5d4..bfd72e8 100644 --- a/src/test/kotlin/net/thauvin/erik/pinboard/PinboardPosterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/pinboard/PinboardPosterTest.kt @@ -41,16 +41,10 @@ class PinboardPosterTest { private val url = "http://www.foo.com/" private val desc = "This is a test." private val localProps = Paths.get("local.properties") - private val apiToken = if (Files.exists(localProps)) { - val p = Properties().apply { Files.newInputStream(localProps).use { fis -> load(fis) } } - p.getProperty("pinboard-api-token", "") - } else { - System.getenv("PINBOARD_API_TOKEN") - } @Test fun testAddPin() { - val poster = PinboardPoster("") + var poster = PinboardPoster("") Assert.assertFalse(poster.addPin(url, desc), "apiToken: ") @@ -60,13 +54,13 @@ class PinboardPosterTest { //poster.apiToken = "foo:TESTING" //Assert.assertFalse(poster.addPin(url, desc), "apiToken: ${poster.apiToken}") - poster.apiToken = apiToken - Assert.assertTrue(poster.addPin(url, desc), "apiToken: $apiToken") + poster = pinboardPosterInstance() + Assert.assertTrue(poster.addPin(url, desc), "apiToken: ${Constants.ENV_API_TOKEN}") } @Test fun testDeletePin() { - val poster = PinboardPoster(apiToken) + val poster = pinboardPosterInstance() poster.apiEndPoint = "" Assert.assertFalse(poster.deletePin(url), "apiEndPoint: ") @@ -76,4 +70,12 @@ class PinboardPosterTest { Assert.assertFalse(poster.deletePin("foo.com"), "url: foo.com") } + + private fun pinboardPosterInstance(): PinboardPoster { + return if (Files.exists(localProps)) { + PinboardPoster(Properties().apply { Files.newInputStream(localProps).use { fis -> load(fis) } }) + } else { + PinboardPoster() + } + } }