Improved and cleaned up the configuration builders [breaking]

This commit is contained in:
Erik C. Thauvin 2024-05-22 20:23:48 -07:00
parent b6178a6dea
commit 68b4f847bd
Signed by: erik
GPG key ID: 776702A6A2DA330E
6 changed files with 192 additions and 38 deletions

View file

@ -37,6 +37,8 @@ import net.thauvin.erik.bitly.Utils.removeHttp
import net.thauvin.erik.bitly.Utils.toEndPoint import net.thauvin.erik.bitly.Utils.toEndPoint
import net.thauvin.erik.bitly.config.CreateConfig import net.thauvin.erik.bitly.config.CreateConfig
import net.thauvin.erik.bitly.config.UpdateConfig import net.thauvin.erik.bitly.config.UpdateConfig
import net.thauvin.erik.bitly.deeplinks.CreateDeeplinks
import net.thauvin.erik.bitly.deeplinks.UpdateDeeplinks
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import java.net.URLEncoder import java.net.URLEncoder
@ -48,6 +50,7 @@ import java.util.logging.Level
* *
* See the [Bitly API](https://dev.bitly.com/api-reference) for more information. * See the [Bitly API](https://dev.bitly.com/api-reference) for more information.
*/ */
@Suppress("LocalVariableName")
open class Bitlinks(private val accessToken: String) { open class Bitlinks(private val accessToken: String) {
/** /**
* The last API call response. * The last API call response.
@ -110,7 +113,7 @@ open class Bitlinks(private val accessToken: String) {
config.group_guid, config.group_guid,
config.title, config.title,
config.tags, config.tags,
config.deepLinks, config.deeplinks,
config.toJson config.toJson
) )
} }
@ -127,13 +130,14 @@ open class Bitlinks(private val accessToken: String) {
* @return The shortened URL or an empty string on error. * @return The shortened URL or an empty string on error.
*/ */
@Synchronized @Synchronized
@JvmOverloads
fun create( fun create(
long_url: String, long_url: String,
domain: String = Constants.EMPTY, domain: String = Constants.EMPTY,
group_guid: String = Constants.EMPTY, group_guid: String = Constants.EMPTY,
title: String = Constants.EMPTY, title: String = Constants.EMPTY,
tags: Array<String> = emptyArray(), tags: Array<String> = emptyArray(),
deeplinks: Array<Map<String, String>> = emptyArray(), deeplinks: CreateDeeplinks = CreateDeeplinks(),
toJson: Boolean = false toJson: Boolean = false
): String { ): String {
var link = if (toJson) Constants.EMPTY_JSON else Constants.EMPTY var link = if (toJson) Constants.EMPTY_JSON else Constants.EMPTY
@ -146,7 +150,7 @@ open class Bitlinks(private val accessToken: String) {
if (group_guid.isNotBlank()) put("group_guid", group_guid) if (group_guid.isNotBlank()) put("group_guid", group_guid)
if (title.isNotBlank()) put("title", title) if (title.isNotBlank()) put("title", title)
if (tags.isNotEmpty()) put("tags", tags) if (tags.isNotEmpty()) put("tags", tags)
if (deeplinks.isNotEmpty()) put("deeplinks", deeplinks) if (deeplinks.isNotEmpty()) put("deeplinks", arrayOf(deeplinks.links()))
} }
) )
link = parseJsonResponse(lastCallResponse, "link", link, toJson) link = parseJsonResponse(lastCallResponse, "link", link, toJson)
@ -258,7 +262,7 @@ open class Bitlinks(private val accessToken: String) {
config.title, config.title,
config.archived, config.archived,
config.tags, config.tags,
config.deepLinks, config.deeplinks,
config.toJson config.toJson
) )
} }
@ -273,12 +277,13 @@ open class Bitlinks(private val accessToken: String) {
* @return [Constants.TRUE] if the update was successful, [Constants.FALSE] otherwise. * @return [Constants.TRUE] if the update was successful, [Constants.FALSE] otherwise.
*/ */
@Synchronized @Synchronized
@JvmOverloads
fun update( fun update(
bitlink: String, bitlink: String,
title: String = Constants.EMPTY, title: String = Constants.EMPTY,
archived: Boolean = false, archived: Boolean = false,
tags: Array<String> = emptyArray(), tags: Array<String> = emptyArray(),
deeplinks: Array<Map<String, String>> = emptyArray(), deeplinks: UpdateDeeplinks = UpdateDeeplinks(),
toJson: Boolean = false toJson: Boolean = false
): String { ): String {
var result = if (toJson) Constants.EMPTY_JSON else Constants.FALSE var result = if (toJson) Constants.EMPTY_JSON else Constants.FALSE
@ -288,7 +293,7 @@ open class Bitlinks(private val accessToken: String) {
if (title.isNotBlank()) put("title", title) if (title.isNotBlank()) put("title", title)
if (archived) put("archived", true) if (archived) put("archived", true)
if (tags.isNotEmpty()) put("tags", tags) if (tags.isNotEmpty()) put("tags", tags)
if (deeplinks.isNotEmpty()) put("deeplinks", deeplinks) if (deeplinks.isNotEmpty()) put("deeplinks", arrayOf(deeplinks.links()))
}, },
Methods.PATCH Methods.PATCH
) )

View file

@ -43,9 +43,10 @@ import java.util.*
* @constructor Creates new instance. * @constructor Creates new instance.
*/ */
open class Bitly() { open class Bitly() {
/** The API access token. /**
* The API access token.
* *
* See [Generic Access Token](https://bitly.is/accesstoken) or * See [Generic Access Token](https://app.bitly.com/settings/api/) or
* [Authentication](https://dev.bitly.com/docs/getting-started/authentication). * [Authentication](https://dev.bitly.com/docs/getting-started/authentication).
**/ **/
var accessToken: String = System.getenv(Constants.ENV_ACCESS_TOKEN) var accessToken: String = System.getenv(Constants.ENV_ACCESS_TOKEN)
@ -105,7 +106,7 @@ open class Bitly() {
/** /**
* Executes an API call. * Executes an API call.
* *
* @param endPoint The REST endpoint path. (eg. `shorten`, `expand`, etc.) * @param endPoint The REST endpoint path. (e.g. `shorten`, `expand`, etc.)
* @param params The request parameters key/value map. * @param params The request parameters key/value map.
* @param method The submission [Method][Methods]. * @param method The submission [Method][Methods].
* @return A [CallResponse] object. * @return A [CallResponse] object.

View file

@ -32,26 +32,29 @@
package net.thauvin.erik.bitly.config package net.thauvin.erik.bitly.config
import net.thauvin.erik.bitly.Constants import net.thauvin.erik.bitly.Constants
import net.thauvin.erik.bitly.deeplinks.CreateDeeplinks
/** /**
* Provides a builder to create a Bitlink. * Provides a configuration to create a [Bitlink][net.thauvin.erik.bitly.Bitlinks]
*
* See the [Bit.ly API](https://dev.bitly.com/api-reference#createFullBitlink) for more information.
*/ */
class CreateConfig private constructor(builder: Builder) { @Suppress("LocalVariableName", "PropertyName")
val domain: String class CreateConfig @JvmOverloads constructor(
val group_guid: String var long_url: String,
val title: String var domain: String = Constants.EMPTY,
val tags: Array<String> var group_guid: String = Constants.EMPTY,
val deepLinks: Array<Map<String, String>> var title: String = Constants.EMPTY,
val long_url: String var tags: Array<String> = emptyArray(),
val toJson: Boolean var deeplinks: CreateDeeplinks = CreateDeeplinks(),
var toJson: Boolean = false
init { ) {
constructor(builder: Builder) : this(builder.long_url) {
domain = builder.domain domain = builder.domain
group_guid = builder.group_guid group_guid = builder.group_guid
title = builder.title title = builder.title
tags = builder.tags tags = builder.tags
deepLinks = builder.deeplinks deeplinks = builder.deeplinks
long_url = builder.long_url
toJson = builder.toJson toJson = builder.toJson
} }
@ -65,7 +68,7 @@ class CreateConfig private constructor(builder: Builder) {
var group_guid: String = Constants.EMPTY var group_guid: String = Constants.EMPTY
var title: String = Constants.EMPTY var title: String = Constants.EMPTY
var tags: Array<String> = emptyArray() var tags: Array<String> = emptyArray()
var deeplinks: Array<Map<String, String>> = emptyArray() var deeplinks: CreateDeeplinks = CreateDeeplinks()
var toJson: Boolean = false var toJson: Boolean = false
/** /**
@ -82,7 +85,7 @@ class CreateConfig private constructor(builder: Builder) {
fun tags(tags: Array<String>): Builder = apply { this.tags = tags } fun tags(tags: Array<String>): Builder = apply { this.tags = tags }
fun deeplinks(deeplinks: Array<Map<String, String>>): Builder = apply { this.deeplinks = deeplinks } fun deeplinks(deeplinks: CreateDeeplinks): Builder = apply { this.deeplinks = deeplinks }
/** /**
* The long URL. * The long URL.

View file

@ -32,24 +32,26 @@
package net.thauvin.erik.bitly.config package net.thauvin.erik.bitly.config
import net.thauvin.erik.bitly.Constants import net.thauvin.erik.bitly.Constants
import net.thauvin.erik.bitly.deeplinks.UpdateDeeplinks
/** /**
* Provides a builder to update a Bitlink. * Provides a configuration to update a [Bitlink][net.thauvin.erik.bitly.Bitlinks].
*
* See the [Bit.ly API](https://dev.bitly.com/api-reference#updateBitlink) for more information.
*/ */
class UpdateConfig private constructor(builder: Builder) { class UpdateConfig @JvmOverloads constructor(
val bitlink: String var bitlink: String,
val title: String var title: String = Constants.EMPTY,
val archived: Boolean var archived: Boolean = false,
val tags: Array<String> var tags: Array<String> = emptyArray(),
val deepLinks: Array<Map<String, String>> var deeplinks: UpdateDeeplinks = UpdateDeeplinks(),
val toJson: Boolean var toJson: Boolean = false
) {
init { constructor(builder: Builder) : this(builder.bitlink) {
bitlink = builder.bitlink
title = builder.title title = builder.title
archived = builder.archived archived = builder.archived
tags = builder.tags tags = builder.tags
deepLinks = builder.deeplinks deeplinks = builder.deeplinks
toJson = builder.toJson toJson = builder.toJson
} }
@ -62,7 +64,7 @@ class UpdateConfig private constructor(builder: Builder) {
var title: String = Constants.EMPTY var title: String = Constants.EMPTY
var archived: Boolean = false var archived: Boolean = false
var tags: Array<String> = emptyArray() var tags: Array<String> = emptyArray()
var deeplinks: Array<Map<String, String>> = emptyArray() var deeplinks: UpdateDeeplinks = UpdateDeeplinks()
var toJson: Boolean = false var toJson: Boolean = false
/** /**
@ -73,7 +75,7 @@ class UpdateConfig private constructor(builder: Builder) {
fun title(title: String): Builder = apply { this.title = title } fun title(title: String): Builder = apply { this.title = title }
fun archived(archived: Boolean): Builder = apply { this.archived = archived } fun archived(archived: Boolean): Builder = apply { this.archived = archived }
fun tags(tags: Array<String>): Builder = apply { this.tags = tags } fun tags(tags: Array<String>): Builder = apply { this.tags = tags }
fun deepLinks(deepLinks: Array<Map<String, String>>): Builder = apply { this.deeplinks = deepLinks } fun deeplinks(deeplinks: UpdateDeeplinks): Builder = apply { this.deeplinks = deeplinks }
/** /**
* Returns the full JSON response if `true`. * Returns the full JSON response if `true`.

View file

@ -40,6 +40,10 @@ import net.thauvin.erik.bitly.Utils.removeHttp
import net.thauvin.erik.bitly.Utils.toEndPoint import net.thauvin.erik.bitly.Utils.toEndPoint
import net.thauvin.erik.bitly.config.CreateConfig import net.thauvin.erik.bitly.config.CreateConfig
import net.thauvin.erik.bitly.config.UpdateConfig import net.thauvin.erik.bitly.config.UpdateConfig
import net.thauvin.erik.bitly.deeplinks.CreateDeeplinks
import net.thauvin.erik.bitly.deeplinks.InstallType
import net.thauvin.erik.bitly.deeplinks.Os
import net.thauvin.erik.bitly.deeplinks.UpdateDeeplinks
import org.json.JSONObject import org.json.JSONObject
import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeAll
import java.io.File import java.io.File
@ -216,6 +220,24 @@ class BitlyTest {
) )
} }
@Test
fun `create bitlink with deeplinks`() {
val bl = bitly.bitlinks()
val dl = CreateDeeplinks().apply {
install_type(InstallType.NO_INSTALL)
app_uri_path("/store?id=123456")
install_url("https://play.google.com/store/apps/details?id=com.bitly.app&hl=en_US")
}
val config = CreateConfig.Builder(longUrl)
.domain("bit.ly")
.deeplinks(dl)
.build()
assertThat(bl.create(config)).isEqualTo(Constants.EMPTY)
assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
}
@Test @Test
fun `shorten with invalid domain`() { fun `shorten with invalid domain`() {
val bl = bitly.bitlinks() val bl = bitly.bitlinks()
@ -268,6 +290,21 @@ class BitlyTest {
assertThat(bl.update(config), "update(tags)").contains("\"tags\":[]") assertThat(bl.update(config), "update(tags)").contains("\"tags\":[]")
} }
@Test
fun `update bitlink with deeplinks`() {
val bl = bitly.bitlinks()
val dl = UpdateDeeplinks().apply {
os(Os.ANDROID)
brand_guid("Ba1bc23dE4F")
}
val config = UpdateConfig.Builder(shortUrl)
.deeplinks(dl)
.build()
assertThat(bl.update(config)).isEqualTo(Constants.FALSE)
assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
}
@Test @Test
fun `validate URL`() { fun `validate URL`() {
assertTrue("https://www.example.com".isValidUrl(), "valid url") assertTrue("https://www.example.com".isValidUrl(), "valid url")

View file

@ -0,0 +1,106 @@
/*
* ConfigTest.kt
*
* Copyright 2020-2024 Erik C. Thauvin (erik@thauvin.net)
*
* 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.bitly.config
import assertk.assertThat
import assertk.assertions.isEqualTo
import net.thauvin.erik.bitly.deeplinks.CreateDeeplinks
import net.thauvin.erik.bitly.deeplinks.InstallType
import net.thauvin.erik.bitly.deeplinks.Os
import net.thauvin.erik.bitly.deeplinks.UpdateDeeplinks
import org.json.JSONObject
import kotlin.test.Test
class ConfigTest {
@Test
fun `create config test`() {
val deeplinks = CreateDeeplinks().apply {
app_id("app_id")
install_type(InstallType.AUTO_INSTALL)
}
val config = CreateConfig(
"long_url",
"domain",
"group_guid",
"title",
arrayOf("tag", "tag2"),
deeplinks,
)
val map = mapOf(
"long_url" to config.long_url,
"domain" to config.domain,
"group_guid" to config.group_guid,
"title" to config.title,
"tags" to config.tags,
"deeplinks" to arrayOf(deeplinks.links())
)
assertThat(JSONObject(map).toString()).isEqualTo(
"""
{"group_guid":"group_guid","long_url":"long_url","title":"title","deeplinks":[{"app_id":"app_id","install_type":"auto_install"}],"domain":"domain","tags":["tag","tag2"]}
""".trimIndent()
)
}
@Test
fun `update config test`() {
val deeplinks = UpdateDeeplinks().apply {
os(Os.IOS)
install_type(InstallType.PROMOTE_INSTALL)
app_guid("app_guid")
}
val config = UpdateConfig(
"blink",
"title",
true,
arrayOf("tag", "tag2"),
deeplinks
)
val map = mapOf(
"bitlink" to config.bitlink,
"title" to config.title,
"archived" to config.archived,
"tags" to config.tags,
"deeplinks" to arrayOf(deeplinks.links())
)
assertThat(JSONObject(map).toString()).isEqualTo(
"""
{"archived":true,"bitlink":"blink","title":"title","deeplinks":[{"os":"ios","app_guid":"app_guid","install_type":"promote_install"}],"tags":["tag","tag2"]}
""".trimIndent()
)
}
}