From f1e0b3101c33c406b3cd021d586ec6781ad87953 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 22 May 2024 20:16:52 -0700 Subject: [PATCH] Implemented DSL for deeplinks when creating or updating Bitlinks. Closes #16 --- .../net/thauvin/erik/bitly/Constants.kt | 6 + .../erik/bitly/deeplinks/CreateDeeplinks.kt | 86 ++++++++++ .../erik/bitly/deeplinks/InstallType.kt | 43 +++++ .../net/thauvin/erik/bitly/deeplinks/Os.kt | 42 +++++ .../erik/bitly/deeplinks/UpdateDeeplinks.kt | 150 ++++++++++++++++++ .../erik/bitly/deeplinks/DeeplinksTest.kt | 130 +++++++++++++++ 6 files changed, 457 insertions(+) create mode 100644 src/main/kotlin/net/thauvin/erik/bitly/deeplinks/CreateDeeplinks.kt create mode 100644 src/main/kotlin/net/thauvin/erik/bitly/deeplinks/InstallType.kt create mode 100644 src/main/kotlin/net/thauvin/erik/bitly/deeplinks/Os.kt create mode 100644 src/main/kotlin/net/thauvin/erik/bitly/deeplinks/UpdateDeeplinks.kt create mode 100644 src/test/kotlin/net/thauvin/erik/bitly/deeplinks/DeeplinksTest.kt diff --git a/src/main/kotlin/net/thauvin/erik/bitly/Constants.kt b/src/main/kotlin/net/thauvin/erik/bitly/Constants.kt index 81cfc2b..a94aba3 100644 --- a/src/main/kotlin/net/thauvin/erik/bitly/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/bitly/Constants.kt @@ -31,6 +31,9 @@ package net.thauvin.erik.bitly +import java.time.format.DateTimeFormatter +import java.util.* + /** Provides the constants for this package. */ object Constants { /** The Bitly API base URL. */ @@ -50,4 +53,7 @@ object Constants { /** True */ const val TRUE = true.toString() + + /** ISO Timestamp format **/ + val ISO_TIMESTAMP: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZZ", Locale.US) } diff --git a/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/CreateDeeplinks.kt b/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/CreateDeeplinks.kt new file mode 100644 index 0000000..ced3ccd --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/CreateDeeplinks.kt @@ -0,0 +1,86 @@ +/* + * CreateDeeplinks.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.deeplinks + +import java.util.* + +/** + * Configures deeplinks used when creating [Bitlinks][net.thauvin.erik.bitly.Bitlinks]. + * + * See the [Bit.ly API](https://dev.bitly.com/api-reference#createFullBitlink) for more information. + * + * @since 2.0 + */ +@Suppress("FunctionName", "LocalVariableName") +class CreateDeeplinks { + private val map = mutableMapOf() + + fun app_id(app_id: String) { + map["app_id"] = app_id + } + + fun app_id(): String? = map["app_id"] + + fun app_uri_path(app_uri_path: String) { + map["app_uri_path"] = app_uri_path + } + + fun app_uri_path(): String? = map["app_uri_path"] + + fun install_url(install_url: String) { + map["install_url"] = install_url + } + + fun install_url(): String? = map["install_url"] + + fun install_type(install_type: InstallType) { + map["install_type"] = install_type.type + } + + fun install_type(): InstallType? { + val type = map["install_type"] + if (type != null) { + return InstallType.valueOf(type.uppercase(Locale.getDefault())) + } + return null + } + + /** + * Returns `true` if there are defined links. + */ + fun isNotEmpty(): Boolean = map.isNotEmpty() + + /** + * Returns the links. + */ + fun links(): Map = map +} diff --git a/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/InstallType.kt b/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/InstallType.kt new file mode 100644 index 0000000..ae24f76 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/InstallType.kt @@ -0,0 +1,43 @@ +/* + * InstallType.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.deeplinks + +/** + * Defines the installation types. + * + * @since 2.0 + */ +enum class InstallType(val type: String) { + NO_INSTALL("no_install"), + AUTO_INSTALL("auto_install"), + PROMOTE_INSTALL("promote_install") +} diff --git a/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/Os.kt b/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/Os.kt new file mode 100644 index 0000000..94e2c64 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/Os.kt @@ -0,0 +1,42 @@ +/* + * InstallType.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.deeplinks + +/** + * Defines the operating system types. + * + * @since 2.0 + */ +enum class Os(val type: String) { + IOS("ios"), + ANDROID("android") +} diff --git a/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/UpdateDeeplinks.kt b/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/UpdateDeeplinks.kt new file mode 100644 index 0000000..e1c8447 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/bitly/deeplinks/UpdateDeeplinks.kt @@ -0,0 +1,150 @@ +/* + * CreateDeeplinks.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.deeplinks + +import net.thauvin.erik.bitly.Constants +import java.time.ZonedDateTime +import java.util.* + +/** + * Configures deeplinks used when updating [Bitlinks][net.thauvin.erik.bitly.Bitlinks]. + * + * See the [Bit.ly API](https://dev.bitly.com/api-reference#updateBitlink) for more information. + * + * @since 2.0 + */ +@Suppress("FunctionName", "LocalVariableName") +class UpdateDeeplinks { + private val map = mutableMapOf() + + fun guid(guid: String) { + map["guid"] = guid + } + + fun guid(): String? = map["guid"] + + fun bitlink(bitlink: String) { + map["bitlink"] = bitlink + } + + fun bitlink(): String? = map["bitlink"] + + fun app_uri_path(app_uri_path: String) { + map["app_uri_path"] = app_uri_path + } + + fun app_uri_path(): String? = map["app_uri_path"] + + fun install_url(install_url: String) { + map["install_url"] = install_url + } + + fun install_url(): String? = map["install_url"] + + fun app_guid(app_guid: String) { + map["app_guid"] = app_guid + } + + fun app_guid(): String? = map["app_guid"] + + fun os(os: Os) { + map["os"] = os.type + } + + fun os(): Os? { + val os = map["os"] + if (os != null) { + return Os.valueOf(os.uppercase(Locale.getDefault())) + } + return null + } + + fun install_type(install_type: InstallType) { + map["install_type"] = install_type.type + } + + fun install_type(): InstallType? { + val type = map["install_type"] + if (type != null) { + return InstallType.valueOf(type.uppercase(Locale.getDefault())) + } + return null + } + + /** + * ISO timestamp. + */ + fun created(created: String) { + map["created"] = created + } + + /** + * ISO timestamp. + */ + fun created(created: ZonedDateTime) { + map["created"] = Constants.ISO_TIMESTAMP.format(created) + } + + fun created(): String? = map["created"] + + /** + * ISO timestamp. + */ + fun modified(modified: String) { + map["modified"] = modified + } + + /** + * ISO timestamp. + */ + fun modified(modified: ZonedDateTime) { + map["modified"] = Constants.ISO_TIMESTAMP.format(modified) + } + + fun modified(): String? = map["modified"] + + fun brand_guid(brand_guid: String) { + map["brand_guid"] = brand_guid + } + + fun brand_guid(): String? = map["brand_guid"] + + /** + * Returns `true` if there are defined links. + */ + fun isNotEmpty(): Boolean = map.isNotEmpty() + + /** + * Returns the links. + */ + fun links(): Map = map +} diff --git a/src/test/kotlin/net/thauvin/erik/bitly/deeplinks/DeeplinksTest.kt b/src/test/kotlin/net/thauvin/erik/bitly/deeplinks/DeeplinksTest.kt new file mode 100644 index 0000000..6629a27 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/bitly/deeplinks/DeeplinksTest.kt @@ -0,0 +1,130 @@ +/* + * CreateDeeplinksTest.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.deeplinks + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.doesNotContain +import assertk.assertions.isEqualTo +import assertk.assertions.isNull +import org.json.JSONObject +import org.junit.Test +import java.time.ZoneId +import java.time.ZonedDateTime + +class DeeplinksTest { + @Test + fun `create test`() { + val deeplinks = CreateDeeplinks().apply { + app_uri_path("app_uri_path") + install_type(InstallType.NO_INSTALL) + } + + assertThat(deeplinks.install_url()).isNull() + deeplinks.install_url("install_url") + + assertThat(deeplinks.app_uri_path()).isEqualTo("app_uri_path") + assertThat(deeplinks.install_type()).isEqualTo(InstallType.NO_INSTALL) + + assertThat(JSONObject(deeplinks.links()).toString()).isEqualTo( + """ + {"app_uri_path":"app_uri_path","install_type":"no_install","install_url":"install_url"} + """.trimIndent() + ) + + deeplinks.install_type(InstallType.PROMOTE_INSTALL) + deeplinks.app_id("app_id") + + assertThat(deeplinks.app_id()).isEqualTo("app_id") + + assertThat(JSONObject(deeplinks.links()).toString()).apply { + doesNotContain(InstallType.NO_INSTALL.type) + contains(InstallType.PROMOTE_INSTALL.type) + contains("\"app_id\":\"app_id\"") + } + } + + @Test + fun `update test`() { + val deeplinks = UpdateDeeplinks().apply { + app_guid("app_guid") + os(Os.IOS) + install_type(InstallType.NO_INSTALL) + guid("guid") + install_url("install_url") + app_uri_path("app_uri_path") + created("created") + modified("modified") + } + + assertThat(deeplinks.brand_guid()).isNull() + deeplinks.brand_guid("brand_guid") + + assertThat(deeplinks.app_uri_path()).isEqualTo("app_uri_path") + assertThat(deeplinks.install_url()).isEqualTo("install_url") + + assertThat(deeplinks.os()).isEqualTo(Os.IOS) + assertThat(deeplinks.install_type()).isEqualTo(InstallType.NO_INSTALL) + assertThat(deeplinks.app_guid()).isEqualTo("app_guid") + assertThat(deeplinks.modified()).isEqualTo("modified") + assertThat(deeplinks.brand_guid()).isEqualTo("brand_guid") + + + assertThat(JSONObject(deeplinks.links()).toString()).isEqualTo( + """ + {"app_guid":"app_guid","install_url":"install_url","os":"ios","app_uri_path":"app_uri_path","created":"created","brand_guid":"brand_guid","guid":"guid","modified":"modified","install_type":"no_install"} + """.trimIndent() + ) + + deeplinks.install_type(InstallType.PROMOTE_INSTALL) + deeplinks.os(Os.ANDROID) + deeplinks.bitlink("bitlink") + + val zdt = ZonedDateTime.of(1997, 8, 29, 2, 14, 0, 0, ZoneId.of("US/Eastern")) + deeplinks.modified(zdt) + deeplinks.created(zdt) + + assertThat(deeplinks.bitlink()).isEqualTo("bitlink") + assertThat(deeplinks.created()).isEqualTo("1997-08-29T02:14:00-0400") + assertThat(deeplinks.modified()).isEqualTo("1997-08-29T02:14:00-0400") + + assertThat(JSONObject(deeplinks.links()).toString()).apply { + doesNotContain(InstallType.NO_INSTALL.type) + contains(InstallType.PROMOTE_INSTALL.type) + + doesNotContain(Os.IOS.type) + contains("\"os\":\"android\"") + + contains("\"bitlink\":\"bitlink\"") + } + } +}