From dd5dc3913bc1aa06a1769d49ab44082bb5efe384 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 13:57:34 +0000 Subject: [PATCH 01/21] Remove unnecessary gitignore file --- .gitignore | 2 +- FlagsmithClient/.gitignore | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 FlagsmithClient/.gitignore diff --git a/.gitignore b/.gitignore index 557c14a..a6005d0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store -/build +build /captures .externalNativeBuild .cxx diff --git a/FlagsmithClient/.gitignore b/FlagsmithClient/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/FlagsmithClient/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build From 300911f3ce459510f4c88e94f54e1b46cbce7a3d Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 14:07:31 +0000 Subject: [PATCH 02/21] Update deprecated junit imports --- .../src/test/java/com/flagsmith/FeatureFlagTests.kt | 6 +++++- FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt b/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt index 6411029..66f6718 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt @@ -1,7 +1,11 @@ package com.flagsmith -import junit.framework.Assert.* import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue import org.junit.Test class FeatureFlagTests { diff --git a/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt b/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt index fc0617e..5ece204 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt @@ -1,8 +1,10 @@ package com.flagsmith import com.flagsmith.entities.Trait -import junit.framework.Assert.* import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue import org.junit.Test class TraitsTests { From 477b2afc4a9be7c9df6683cda1240afb0f5ee2fa Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 14:43:05 +0000 Subject: [PATCH 03/21] Fix incorrect way round "expected" and "actual" values --- .../src/test/java/com/flagsmith/FeatureFlagTests.kt | 8 ++++---- .../src/test/java/com/flagsmith/TraitsTests.kt | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt b/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt index 66f6718..232197d 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt @@ -38,7 +38,7 @@ class FeatureFlagTests { val found = result.getOrThrow().find { flag -> flag.feature.name == "with-value" } assertNotNull(found) - assertEquals(found?.featureStateValue, 7.0) + assertEquals(7.0, found?.featureStateValue) } } @@ -50,7 +50,7 @@ class FeatureFlagTests { val found = result.getOrThrow().find { flag -> flag.feature.name == "with-value" } assertNotNull(found) - assertEquals(found?.featureStateValue, 756.0) + assertEquals(756.0, found?.featureStateValue) } } @@ -59,7 +59,7 @@ class FeatureFlagTests { runBlocking { val result = flagsmith.getValueForFeatureSync("with-value", identity = null) assertTrue(result.isSuccess) - assertEquals(result.getOrThrow(), 7.0) + assertEquals(7.0, result.getOrThrow()) } } @@ -68,7 +68,7 @@ class FeatureFlagTests { runBlocking { val result = flagsmith.getValueForFeatureSync("with-value", identity = "person") assertTrue(result.isSuccess) - assertEquals(result.getOrThrow(), 756.0) + assertEquals(756.0, result.getOrThrow()) } } diff --git a/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt b/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt index 5ece204..0db3ad5 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt @@ -17,7 +17,7 @@ class TraitsTests { val result = flagsmith.getTraitsSync("person") assertTrue(result.isSuccess) assertTrue(result.getOrThrow().isNotEmpty()) - assertEquals(result.getOrThrow().find { trait -> trait.key == "favourite-colour" }?.value, "electric pink") + assertEquals("electric pink", result.getOrThrow().find { trait -> trait.key == "favourite-colour" }?.value) } } @@ -36,7 +36,7 @@ class TraitsTests { runBlocking { val result = flagsmith.getTraitSync("favourite-colour", "person") assertTrue(result.isSuccess) - assertEquals(result.getOrThrow()?.value, "electric pink") + assertEquals("electric pink", result.getOrThrow()?.value) } } @@ -54,9 +54,9 @@ class TraitsTests { runBlocking { val result = flagsmith.setTraitSync(Trait(key = "set-from-client", value = "12345"), "person") assertTrue(result.isSuccess) - assertEquals(result.getOrThrow().key, "set-from-client") - assertEquals(result.getOrThrow().value, "12345") - assertEquals(result.getOrThrow().identity.identifier, "person") + assertEquals("set-from-client", result.getOrThrow().key) + assertEquals("12345", result.getOrThrow().value) + assertEquals("person", result.getOrThrow().identity.identifier) } } @@ -67,7 +67,7 @@ class TraitsTests { assertTrue(result.isSuccess) assertTrue(result.getOrThrow().traits.isNotEmpty()) assertTrue(result.getOrThrow().flags.isNotEmpty()) - assertEquals(result.getOrThrow().traits.find { trait -> trait.key == "favourite-colour" }?.value, "electric pink") + assertEquals("electric pink", result.getOrThrow().traits.find { trait -> trait.key == "favourite-colour" }?.value) } } } \ No newline at end of file From 1a308573dccc36a2217af7f6001d3b17a10b5448 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 17:43:48 +0000 Subject: [PATCH 04/21] Mock responses to convert them to actual unit tests --- FlagsmithClient/build.gradle | 3 +- .../java/com/flagsmith/FeatureFlagTests.kt | 38 +++++- .../test/java/com/flagsmith/TraitsTests.kt | 42 ++++++- .../flagsmith/mockResponses/MockResponses.kt | 109 ++++++++++++++++++ 4 files changed, 184 insertions(+), 8 deletions(-) create mode 100644 FlagsmithClient/src/test/java/com/flagsmith/mockResponses/MockResponses.kt diff --git a/FlagsmithClient/build.gradle b/FlagsmithClient/build.gradle index 0766ccb..5ccb0df 100644 --- a/FlagsmithClient/build.gradle +++ b/FlagsmithClient/build.gradle @@ -45,9 +45,10 @@ dependencies { implementation 'com.github.kittinunf.fuel:fuel-gson:2.3.1' testImplementation 'junit:junit:4.13.2' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1' + testImplementation 'org.mock-server:mockserver-netty-no-dependencies:5.14.0' androidTestImplementation 'androidx.test.ext:junit:1.1.4' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1' } afterEvaluate { diff --git a/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt b/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt index 232197d..dc3945f 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/FeatureFlagTests.kt @@ -1,19 +1,41 @@ package com.flagsmith +import com.flagsmith.mockResponses.MockEndpoint +import com.flagsmith.mockResponses.mockResponseFor import kotlinx.coroutines.runBlocking +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue +import org.junit.Before import org.junit.Test +import org.mockserver.integration.ClientAndServer class FeatureFlagTests { - private val flagsmith = Flagsmith(environmentKey = System.getenv("ENVIRONMENT_KEY") ?: "", enableAnalytics = false) + private lateinit var mockServer: ClientAndServer + private lateinit var flagsmith: Flagsmith + + @Before + fun setup() { + mockServer = ClientAndServer.startClientAndServer() + flagsmith = Flagsmith( + environmentKey = System.getenv("ENVIRONMENT_KEY") ?: "", + baseUrl = "http://localhost:${mockServer.localPort}", + enableAnalytics = false + ) + } + + @After + fun tearDown() { + mockServer.stop() + } @Test fun testHasFeatureFlagWithFlag() { + mockServer.mockResponseFor(MockEndpoint.GET_FLAGS) runBlocking { val result = flagsmith.hasFeatureFlagSync("no-value") assertTrue(result.isSuccess) @@ -23,6 +45,7 @@ class FeatureFlagTests { @Test fun testHasFeatureFlagWithoutFlag() { + mockServer.mockResponseFor(MockEndpoint.GET_FLAGS) runBlocking { val result = flagsmith.hasFeatureFlagSync("doesnt-exist") assertTrue(result.isSuccess) @@ -32,6 +55,7 @@ class FeatureFlagTests { @Test fun testGetFeatureFlags() { + mockServer.mockResponseFor(MockEndpoint.GET_FLAGS) runBlocking { val result = flagsmith.getFeatureFlagsSync() assertTrue(result.isSuccess) @@ -44,6 +68,7 @@ class FeatureFlagTests { @Test fun testGetFeatureFlagsWithIdentity() { + mockServer.mockResponseFor(MockEndpoint.GET_IDENTITIES) runBlocking { val result = flagsmith.getFeatureFlagsSync(identity = "person") assertTrue(result.isSuccess) @@ -56,6 +81,7 @@ class FeatureFlagTests { @Test fun testGetValueForFeatureExisting() { + mockServer.mockResponseFor(MockEndpoint.GET_FLAGS) runBlocking { val result = flagsmith.getValueForFeatureSync("with-value", identity = null) assertTrue(result.isSuccess) @@ -65,6 +91,7 @@ class FeatureFlagTests { @Test fun testGetValueForFeatureExistingOverriddenWithIdentity() { + mockServer.mockResponseFor(MockEndpoint.GET_IDENTITIES) runBlocking { val result = flagsmith.getValueForFeatureSync("with-value", identity = "person") assertTrue(result.isSuccess) @@ -74,6 +101,7 @@ class FeatureFlagTests { @Test fun testGetValueForFeatureNotExisting() { + mockServer.mockResponseFor(MockEndpoint.GET_FLAGS) runBlocking { val result = flagsmith.getValueForFeatureSync("not-existing", identity = null) assertTrue(result.isSuccess) @@ -83,8 +111,10 @@ class FeatureFlagTests { @Test fun testHasFeatureForNoIdentity() { + mockServer.mockResponseFor(MockEndpoint.GET_FLAGS) runBlocking { - val result = flagsmith.hasFeatureFlagSync("with-value-just-person-enabled", identity = null) + val result = + flagsmith.hasFeatureFlagSync("with-value-just-person-enabled", identity = null) assertTrue(result.isSuccess) assertFalse(result.getOrThrow()) } @@ -92,8 +122,10 @@ class FeatureFlagTests { @Test fun testHasFeatureWithIdentity() { + mockServer.mockResponseFor(MockEndpoint.GET_IDENTITIES) runBlocking { - val result = flagsmith.hasFeatureFlagSync("with-value-just-person-enabled", identity = "person") + val result = + flagsmith.hasFeatureFlagSync("with-value-just-person-enabled", identity = "person") assertTrue(result.isSuccess) assertTrue(result.getOrThrow()) } diff --git a/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt b/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt index 0db3ad5..73f0c16 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt @@ -1,28 +1,54 @@ package com.flagsmith import com.flagsmith.entities.Trait +import com.flagsmith.mockResponses.MockEndpoint +import com.flagsmith.mockResponses.mockResponseFor import kotlinx.coroutines.runBlocking +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue +import org.junit.Before import org.junit.Test +import org.mockserver.integration.ClientAndServer class TraitsTests { - private val flagsmith = Flagsmith(environmentKey = System.getenv("ENVIRONMENT_KEY") ?: "", enableAnalytics = false) + private lateinit var mockServer: ClientAndServer + private lateinit var flagsmith: Flagsmith + + @Before + fun setup() { + mockServer = ClientAndServer.startClientAndServer() + flagsmith = Flagsmith( + environmentKey = "", + baseUrl = "http://localhost:${mockServer.localPort}", + enableAnalytics = false + ) + } + + @After + fun tearDown() { + mockServer.stop() + } @Test fun testGetTraitsDefinedForPerson() { + mockServer.mockResponseFor(MockEndpoint.GET_IDENTITIES) runBlocking { val result = flagsmith.getTraitsSync("person") assertTrue(result.isSuccess) assertTrue(result.getOrThrow().isNotEmpty()) - assertEquals("electric pink", result.getOrThrow().find { trait -> trait.key == "favourite-colour" }?.value) + assertEquals( + "electric pink", + result.getOrThrow().find { trait -> trait.key == "favourite-colour" }?.value + ) } } @Test fun testGetTraitsNotDefinedForPerson() { + mockServer.mockResponseFor(MockEndpoint.GET_IDENTITIES) runBlocking { val result = flagsmith.getTraitsSync("person") assertTrue(result.isSuccess) @@ -33,6 +59,7 @@ class TraitsTests { @Test fun testGetTraitById() { + mockServer.mockResponseFor(MockEndpoint.GET_IDENTITIES) runBlocking { val result = flagsmith.getTraitSync("favourite-colour", "person") assertTrue(result.isSuccess) @@ -42,6 +69,7 @@ class TraitsTests { @Test fun testGetUndefinedTraitById() { + mockServer.mockResponseFor(MockEndpoint.GET_IDENTITIES) runBlocking { val result = flagsmith.getTraitSync("favourite-cricketer", "person") assertTrue(result.isSuccess) @@ -51,8 +79,10 @@ class TraitsTests { @Test fun testSetTrait() { + mockServer.mockResponseFor(MockEndpoint.SET_TRAIT) runBlocking { - val result = flagsmith.setTraitSync(Trait(key = "set-from-client", value = "12345"), "person") + val result = + flagsmith.setTraitSync(Trait(key = "set-from-client", value = "12345"), "person") assertTrue(result.isSuccess) assertEquals("set-from-client", result.getOrThrow().key) assertEquals("12345", result.getOrThrow().value) @@ -62,12 +92,16 @@ class TraitsTests { @Test fun testGetIdentity() { + mockServer.mockResponseFor(MockEndpoint.GET_IDENTITIES) runBlocking { val result = flagsmith.getIdentitySync("person") assertTrue(result.isSuccess) assertTrue(result.getOrThrow().traits.isNotEmpty()) assertTrue(result.getOrThrow().flags.isNotEmpty()) - assertEquals("electric pink", result.getOrThrow().traits.find { trait -> trait.key == "favourite-colour" }?.value) + assertEquals( + "electric pink", + result.getOrThrow().traits.find { trait -> trait.key == "favourite-colour" }?.value + ) } } } \ No newline at end of file diff --git a/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/MockResponses.kt b/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/MockResponses.kt new file mode 100644 index 0000000..0d5ff91 --- /dev/null +++ b/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/MockResponses.kt @@ -0,0 +1,109 @@ +package com.flagsmith.mockResponses + +import org.mockserver.integration.ClientAndServer +import org.mockserver.model.HttpRequest.request +import org.mockserver.model.HttpResponse.response +import org.mockserver.model.MediaType + +enum class MockEndpoint(val path: String, val body: String) { + GET_IDENTITIES("/identities", MockResponses.getIdentities), + GET_FLAGS("/flags", MockResponses.getFlags), + SET_TRAIT("/traits", MockResponses.setTrait) +} + +fun ClientAndServer.mockResponseFor(endpoint: MockEndpoint) { + `when`(request().withPath(endpoint.path)) + .respond( + response() + .withContentType(MediaType.APPLICATION_JSON) + .withBody(endpoint.body) + ) +} + +object MockResponses { + val getIdentities = """ + { + "flags": [ + { + "feature_state_value": null, + "feature": { + "type": "STANDARD", + "name": "no-value", + "id": 35506 + }, + "enabled": true + }, + { + "feature_state_value": 756, + "feature": { + "type": "STANDARD", + "name": "with-value", + "id": 35507 + }, + "enabled": true + }, + { + "feature_state_value": "", + "feature": { + "type": "STANDARD", + "name": "with-value-just-person-enabled", + "id": 35508 + }, + "enabled": true + } + ], + "traits": [ + { + "trait_value": "12345", + "trait_key": "set-from-client" + }, + { + "trait_value": "electric pink", + "trait_key": "favourite-colour" + } + ] + } + """.trimIndent() + + val getFlags = """ + [ + { + "enabled": true, + "feature": { + "type": "STANDARD", + "id": 35506, + "name": "no-value" + }, + "feature_state_value": null + }, + { + "enabled": true, + "feature": { + "type": "STANDARD", + "id": 35507, + "name": "with-value" + }, + "feature_state_value": 7 + }, + { + "enabled": false, + "feature": { + "type": "STANDARD", + "id": 35508, + "name": "with-value-just-person-enabled" + }, + "feature_state_value": null + } + ] + """.trimIndent() + + val setTrait = """ + { + "trait_key": "set-from-client", + "trait_value": "12345", + "identity": { + "identifier": "person" + } + } + """.trimIndent() +} \ No newline at end of file From 785742909e55e5ec260382463c5364a9af6a053c Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 19:14:40 +0000 Subject: [PATCH 05/21] Refactor to share folding logic --- .../src/main/java/com/flagsmith/Flagsmith.kt | 90 +++++++++---------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt index e5c2463..7da476d 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt @@ -5,6 +5,8 @@ import com.flagsmith.internal.FlagsmithApi import com.flagsmith.internal.* import com.flagsmith.entities.* import com.github.kittinunf.fuel.Fuel +import kotlin.reflect.KProperty1 +import com.github.kittinunf.result.Result as FuelResult /** * Flagsmith @@ -21,7 +23,7 @@ import com.github.kittinunf.fuel.Fuel class Flagsmith constructor( private val environmentKey: String, private val baseUrl: String? = null, - val context: Context? = null, + private val context: Context? = null, private val enableAnalytics: Boolean = DEFAULT_ENABLE_ANALYTICS, private val analyticsFlushPeriod: Int = DEFAULT_ANALYTICS_FLUSH_PERIOD_SECONDS ) { @@ -45,97 +47,91 @@ class Flagsmith constructor( fun getFeatureFlags(identity: String? = null, result: (Result>) -> Unit) { if (identity != null) { - Fuel.request( - FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) + Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> - res.fold( - success = { value -> result(Result.success(value.flags)) }, - failure = { err -> result(Result.failure(err)) } - ) + result(res.convertToResult(IdentityFlagsAndTraits::flags)) } } else { Fuel.request(FlagsmithApi.getFlags()) .responseObject(FlagListDeserializer()) { _, _, res -> - res.fold( - success = { value -> result(Result.success(value)) }, - failure = { err -> result(Result.failure(err)) } - ) + result(res.convertToResult()) } } } - fun hasFeatureFlag(forFeatureId: String, identity: String? = null, result:(Result) -> Unit) { + fun hasFeatureFlag( + forFeatureId: String, + identity: String? = null, + result: (Result) -> Unit + ) { getFeatureFlags(identity) { res -> res.fold( onSuccess = { flags -> - val foundFlag = flags.find { flag -> flag.feature.name == forFeatureId && flag.enabled } + val foundFlag = + flags.find { flag -> flag.feature.name == forFeatureId && flag.enabled } analytics?.trackEvent(forFeatureId) result(Result.success(foundFlag != null)) }, - onFailure = { err -> result(Result.failure(err))} + onFailure = { err -> result(Result.failure(err)) } ) } } - fun getValueForFeature(searchFeatureId: String, identity: String? = null, result: (Result) -> Unit) { + fun getValueForFeature( + searchFeatureId: String, + identity: String? = null, + result: (Result) -> Unit + ) { getFeatureFlags(identity) { res -> res.fold( onSuccess = { flags -> - val foundFlag = flags.find { flag -> flag.feature.name == searchFeatureId && flag.enabled } + val foundFlag = + flags.find { flag -> flag.feature.name == searchFeatureId && flag.enabled } analytics?.trackEvent(searchFeatureId) result(Result.success(foundFlag?.featureStateValue)) }, - onFailure = { err -> result(Result.failure(err))} + onFailure = { err -> result(Result.failure(err)) } ) } } fun getTrait(id: String, identity: String, result: (Result) -> Unit) { - Fuel.request( - FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) + Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> - res.fold( - success = { value -> - val trait = value.traits.find { it.key == id } - result(Result.success(trait)) - }, - failure = { err -> result(Result.failure(err)) } - ) + result(res.convertToResult { value -> value.traits.find { it.key == id } }) } } fun getTraits(identity: String, result: (Result>) -> Unit) { - Fuel.request( - FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) + Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> - res.fold( - success = { value -> result(Result.success(value.traits)) }, - failure = { err -> result(Result.failure(err)) } - ) + result(res.convertToResult(IdentityFlagsAndTraits::traits)) } } fun setTrait(trait: Trait, identity: String, result: (Result) -> Unit) { - Fuel.request( - FlagsmithApi.setTrait(trait = trait, identity = identity)) + Fuel.request(FlagsmithApi.setTrait(trait = trait, identity = identity)) .responseObject(TraitWithIdentityDeserializer()) { _, _, res -> - res.fold( - success = { value -> result(Result.success(value)) }, - failure = { err -> result(Result.failure(err)) } - ) + result(res.convertToResult()) } } - fun getIdentity(identity: String, result: (Result) -> Unit){ - Fuel.request( - FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) + fun getIdentity(identity: String, result: (Result) -> Unit) { + Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> - res.fold( - success = { value -> - result(Result.success(value)) - }, - failure = { err -> result(Result.failure(err)) } - ) + result(res.convertToResult()) } } + + private fun FuelResult.convertToResult(): Result = + convertToResult { it } + + private fun FuelResult.convertToResult(prop: KProperty1): Result = + convertToResult { prop(it) } + + private fun FuelResult.convertToResult(map: (A) -> O): Result = + fold( + success = { value -> Result.success(map(value)) }, + failure = { err -> Result.failure(err) } + ) } \ No newline at end of file From 7c3c3d27cb66764361146794e0325c05002135e5 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 19:47:06 +0000 Subject: [PATCH 06/21] Refactor to share get identity and traits request --- .../src/main/java/com/flagsmith/Flagsmith.kt | 89 ++++++++----------- 1 file changed, 36 insertions(+), 53 deletions(-) diff --git a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt index 7da476d..21c2fa5 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt @@ -47,10 +47,9 @@ class Flagsmith constructor( fun getFeatureFlags(identity: String? = null, result: (Result>) -> Unit) { if (identity != null) { - Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) - .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> - result(res.convertToResult(IdentityFlagsAndTraits::flags)) - } + getIdentityFlagsAndTraits(identity) { res -> + result(res.map { it.flags }) + } } else { Fuel.request(FlagsmithApi.getFlags()) .responseObject(FlagListDeserializer()) { _, _, res -> @@ -63,51 +62,37 @@ class Flagsmith constructor( forFeatureId: String, identity: String? = null, result: (Result) -> Unit - ) { - getFeatureFlags(identity) { res -> - res.fold( - onSuccess = { flags -> - val foundFlag = - flags.find { flag -> flag.feature.name == forFeatureId && flag.enabled } - analytics?.trackEvent(forFeatureId) - result(Result.success(foundFlag != null)) - }, - onFailure = { err -> result(Result.failure(err)) } - ) - } + ) = getFeatureFlags(identity) { res -> + result(res.map { flags -> + val foundFlag = + flags.find { flag -> flag.feature.name == forFeatureId && flag.enabled } + analytics?.trackEvent(forFeatureId) + foundFlag != null + }) } fun getValueForFeature( searchFeatureId: String, identity: String? = null, result: (Result) -> Unit - ) { - getFeatureFlags(identity) { res -> - res.fold( - onSuccess = { flags -> - val foundFlag = - flags.find { flag -> flag.feature.name == searchFeatureId && flag.enabled } - analytics?.trackEvent(searchFeatureId) - result(Result.success(foundFlag?.featureStateValue)) - }, - onFailure = { err -> result(Result.failure(err)) } - ) - } + ) = getFeatureFlags(identity) { res -> + result(res.map { flags -> + val foundFlag = + flags.find { flag -> flag.feature.name == searchFeatureId && flag.enabled } + analytics?.trackEvent(searchFeatureId) + foundFlag?.featureStateValue + }) } - fun getTrait(id: String, identity: String, result: (Result) -> Unit) { - Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) - .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> - result(res.convertToResult { value -> value.traits.find { it.key == id } }) - } - } + fun getTrait(id: String, identity: String, result: (Result) -> Unit) = + getIdentityFlagsAndTraits(identity) { res -> + result(res.map { value -> value.traits.find { it.key == id } }) + } - fun getTraits(identity: String, result: (Result>) -> Unit) { - Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) - .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> - result(res.convertToResult(IdentityFlagsAndTraits::traits)) - } - } + fun getTraits(identity: String, result: (Result>) -> Unit) = + getIdentityFlagsAndTraits(identity) { res -> + result(res.map { it.traits }) + } fun setTrait(trait: Trait, identity: String, result: (Result) -> Unit) { Fuel.request(FlagsmithApi.setTrait(trait = trait, identity = identity)) @@ -116,22 +101,20 @@ class Flagsmith constructor( } } - fun getIdentity(identity: String, result: (Result) -> Unit) { - Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) - .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> - result(res.convertToResult()) - } - } - - private fun FuelResult.convertToResult(): Result = - convertToResult { it } + fun getIdentity(identity: String, result: (Result) -> Unit) = + getIdentityFlagsAndTraits(identity, result) - private fun FuelResult.convertToResult(prop: KProperty1): Result = - convertToResult { prop(it) } + private fun getIdentityFlagsAndTraits( + identity: String, + result: (Result) -> Unit + ) = Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) + .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> + result(res.convertToResult()) + } - private fun FuelResult.convertToResult(map: (A) -> O): Result = + private fun FuelResult.convertToResult(): Result = fold( - success = { value -> Result.success(map(value)) }, + success = { value -> Result.success(value) }, failure = { err -> Result.failure(err) } ) } \ No newline at end of file From 7ece258ee5c1e7ff1732090dbce8105c21a2f72f Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 19:50:11 +0000 Subject: [PATCH 07/21] Refactor to share get feature flag logic --- .../src/main/java/com/flagsmith/Flagsmith.kt | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt index 21c2fa5..d36a46d 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt @@ -5,7 +5,6 @@ import com.flagsmith.internal.FlagsmithApi import com.flagsmith.internal.* import com.flagsmith.entities.* import com.github.kittinunf.fuel.Fuel -import kotlin.reflect.KProperty1 import com.github.kittinunf.result.Result as FuelResult /** @@ -59,29 +58,19 @@ class Flagsmith constructor( } fun hasFeatureFlag( - forFeatureId: String, + featureId: String, identity: String? = null, result: (Result) -> Unit - ) = getFeatureFlags(identity) { res -> - result(res.map { flags -> - val foundFlag = - flags.find { flag -> flag.feature.name == forFeatureId && flag.enabled } - analytics?.trackEvent(forFeatureId) - foundFlag != null - }) + ) = getFeatureFlag(featureId, identity) { res -> + result(res.map { flag -> flag != null }) } fun getValueForFeature( - searchFeatureId: String, + featureId: String, identity: String? = null, result: (Result) -> Unit - ) = getFeatureFlags(identity) { res -> - result(res.map { flags -> - val foundFlag = - flags.find { flag -> flag.feature.name == searchFeatureId && flag.enabled } - analytics?.trackEvent(searchFeatureId) - foundFlag?.featureStateValue - }) + ) = getFeatureFlag(featureId, identity) { res -> + result(res.map { flag -> flag?.featureStateValue }) } fun getTrait(id: String, identity: String, result: (Result) -> Unit) = @@ -104,6 +93,18 @@ class Flagsmith constructor( fun getIdentity(identity: String, result: (Result) -> Unit) = getIdentityFlagsAndTraits(identity, result) + private fun getFeatureFlag( + featureId: String, + identity: String?, + result: (Result) -> Unit + ) = getFeatureFlags(identity) { res -> + result(res.map { flags -> + val foundFlag = flags.find { flag -> flag.feature.name == featureId && flag.enabled } + analytics?.trackEvent(featureId) + foundFlag + }) + } + private fun getIdentityFlagsAndTraits( identity: String, result: (Result) -> Unit From 7964e8a42eeb6f61b35e883bf33b37c014dd2520 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 19:58:29 +0000 Subject: [PATCH 08/21] Use default baseUrl rather than allowing nullable string --- FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt index d36a46d..990139a 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt @@ -21,7 +21,7 @@ import com.github.kittinunf.result.Result as FuelResult */ class Flagsmith constructor( private val environmentKey: String, - private val baseUrl: String? = null, + private val baseUrl: String = "https://edge.api.flagsmith.com/api/v1", private val context: Context? = null, private val enableAnalytics: Boolean = DEFAULT_ENABLE_ANALYTICS, private val analyticsFlushPeriod: Int = DEFAULT_ANALYTICS_FLUSH_PERIOD_SECONDS @@ -35,7 +35,7 @@ class Flagsmith constructor( if (enableAnalytics && context == null) { throw IllegalArgumentException("Flagsmith requires a context to use the analytics feature") } - FlagsmithApi.baseUrl = baseUrl ?: "https://edge.api.flagsmith.com/api/v1" + FlagsmithApi.baseUrl = baseUrl FlagsmithApi.environmentKey = environmentKey } From a36335926e0dc564fc4e32bbf31c7ad071fb8df1 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 20:06:59 +0000 Subject: [PATCH 09/21] Combine analytics creation logic --- .../src/main/java/com/flagsmith/Flagsmith.kt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt index 990139a..deb316d 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt @@ -26,15 +26,12 @@ class Flagsmith constructor( private val enableAnalytics: Boolean = DEFAULT_ENABLE_ANALYTICS, private val analyticsFlushPeriod: Int = DEFAULT_ANALYTICS_FLUSH_PERIOD_SECONDS ) { - private var analytics: FlagsmithAnalytics? = null + private val analytics: FlagsmithAnalytics? = + if (!enableAnalytics) null + else if (context != null) FlagsmithAnalytics(context, analyticsFlushPeriod) + else throw IllegalArgumentException("Flagsmith requires a context to use the analytics feature") init { - if (enableAnalytics && context != null) { - this.analytics = FlagsmithAnalytics(context, analyticsFlushPeriod) - } - if (enableAnalytics && context == null) { - throw IllegalArgumentException("Flagsmith requires a context to use the analytics feature") - } FlagsmithApi.baseUrl = baseUrl FlagsmithApi.environmentKey = environmentKey } From d98737fdf028d19c82c000ca09b7c541aef2cfc0 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 27 Dec 2022 20:30:54 +0000 Subject: [PATCH 10/21] Neaten api endpoints enum --- .../src/main/java/com/flagsmith/Flagsmith.kt | 6 +- .../flagsmith/internal/FlagsmithAnalytics.kt | 5 +- .../com/flagsmith/internal/FlagsmithApi.kt | 71 +++++++++---------- 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt index deb316d..65b4e27 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt @@ -47,7 +47,7 @@ class Flagsmith constructor( result(res.map { it.flags }) } } else { - Fuel.request(FlagsmithApi.getFlags()) + Fuel.request(FlagsmithApi.GetFlags) .responseObject(FlagListDeserializer()) { _, _, res -> result(res.convertToResult()) } @@ -81,7 +81,7 @@ class Flagsmith constructor( } fun setTrait(trait: Trait, identity: String, result: (Result) -> Unit) { - Fuel.request(FlagsmithApi.setTrait(trait = trait, identity = identity)) + Fuel.request(FlagsmithApi.SetTrait(trait = trait, identity = identity)) .responseObject(TraitWithIdentityDeserializer()) { _, _, res -> result(res.convertToResult()) } @@ -105,7 +105,7 @@ class Flagsmith constructor( private fun getIdentityFlagsAndTraits( identity: String, result: (Result) -> Unit - ) = Fuel.request(FlagsmithApi.getIdentityFlagsAndTraits(identity = identity)) + ) = Fuel.request(FlagsmithApi.GetIdentityFlagsAndTraits(identity = identity)) .responseObject(IdentityFlagsAndTraitsDeserializer()) { _, _, res -> result(res.convertToResult()) } diff --git a/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithAnalytics.kt b/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithAnalytics.kt index 1a516bd..fef5c85 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithAnalytics.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithAnalytics.kt @@ -5,7 +5,6 @@ import android.content.SharedPreferences import android.os.Handler import android.os.Looper import android.util.Log -import com.flagsmith.Flagsmith import com.github.kittinunf.fuel.Fuel import org.json.JSONException import org.json.JSONObject @@ -21,7 +20,7 @@ class FlagsmithAnalytics constructor( private val timerRunnable = object : Runnable { override fun run() { if (currentEvents.isNotEmpty()) { - Fuel.request(FlagsmithApi.postAnalytics(eventMap = currentEvents)) + Fuel.request(FlagsmithApi.PostAnalytics(eventMap = currentEvents)) .response { _, _, res -> res.fold( success = { resetMap() }, @@ -44,7 +43,7 @@ class FlagsmithAnalytics constructor( /// Counts the instances of a `Flag` being queried. fun trackEvent(flagName: String) { val currentFlagCount = currentEvents[flagName] ?: 0 - currentEvents[flagName] = currentFlagCount + 1; + currentEvents[flagName] = currentFlagCount + 1 // Update events cache setMap(currentEvents) diff --git a/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithApi.kt b/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithApi.kt index d7bd788..a88210e 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithApi.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithApi.kt @@ -9,11 +9,11 @@ import com.github.kittinunf.fuel.core.Parameters import com.github.kittinunf.fuel.util.FuelRouting import com.google.gson.Gson -sealed class FlagsmithApi: FuelRouting { - class getIdentityFlagsAndTraits(val identity: String): FlagsmithApi() - class getFlags(): FlagsmithApi() - class setTrait(val trait: Trait, val identity: String): FlagsmithApi() - class postAnalytics(val eventMap: Map): FlagsmithApi() +sealed class FlagsmithApi : FuelRouting { + class GetIdentityFlagsAndTraits(val identity: String) : FlagsmithApi() + object GetFlags : FlagsmithApi() + class SetTrait(val trait: Trait, val identity: String) : FlagsmithApi() + class PostAnalytics(val eventMap: Map) : FlagsmithApi() companion object { var environmentKey: String? = null @@ -29,52 +29,49 @@ sealed class FlagsmithApi: FuelRouting { } } override val body: String? - get() { - return when(this) { - is setTrait -> Gson().toJson(TraitWithIdentity(key = trait.key, value = trait.value, identity = Identity(identity))) - is postAnalytics -> Gson().toJson(eventMap) - else -> null - } + get() = when (this) { + is SetTrait -> Gson().toJson( + TraitWithIdentity( + key = trait.key, + value = trait.value, + identity = Identity(identity) + ) + ) + is PostAnalytics -> Gson().toJson(eventMap) + else -> null } - override val bytes: ByteArray? - get() = null + override val bytes: ByteArray? = null override val headers: Map? - get() { - val headers = mutableMapOf("X-Environment-Key" to listOf(environmentKey ?: "")) + get() = mutableMapOf( + "X-Environment-Key" to listOf(environmentKey ?: "") + ).apply { if (method == Method.POST) { - headers["Content-Type"] = listOf("application/json") + this += "Content-Type" to listOf("application/json") } - return headers } override val method: Method - get() { - return when(this) { - is getIdentityFlagsAndTraits -> Method.GET - is getFlags -> Method.GET - is setTrait -> Method.POST - is postAnalytics -> Method.POST - } + get() = when (this) { + is GetIdentityFlagsAndTraits -> Method.GET + is GetFlags -> Method.GET + is SetTrait -> Method.POST + is PostAnalytics -> Method.POST } override val params: Parameters? - get() { - return when(this) { - is getIdentityFlagsAndTraits -> listOf("identifier" to this.identity) - is setTrait -> listOf("identifier" to this.identity) - else -> null - } + get() = when (this) { + is GetIdentityFlagsAndTraits -> listOf("identifier" to this.identity) + is SetTrait -> listOf("identifier" to this.identity) + else -> null } override val path: String - get() { - return when(this) { - is getIdentityFlagsAndTraits -> "/identities" - is getFlags -> "/flags" - is setTrait -> "/traits" - is postAnalytics -> "/analytics/flags" - } + get() = when (this) { + is GetIdentityFlagsAndTraits -> "/identities" + is GetFlags -> "/flags" + is SetTrait -> "/traits" + is PostAnalytics -> "/analytics/flags" } } \ No newline at end of file From 9ab02ddcb3276aab02506277b85f3a384b655dc4 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Mon, 2 Jan 2023 04:01:27 +0000 Subject: [PATCH 11/21] Add android verification workflow --- .github/workflows/android-verfication.yml | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/android-verfication.yml diff --git a/.github/workflows/android-verfication.yml b/.github/workflows/android-verfication.yml new file mode 100644 index 0000000..9fee467 --- /dev/null +++ b/.github/workflows/android-verfication.yml @@ -0,0 +1,28 @@ +name: Android PR & Main Branch Verification CI + +on: + push: + branches: + - "main" + pull_request: + branches: + - "main" + +jobs: + test: + name: Run Unit Tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Unit tests + run: ./gradlew test --stacktrace \ No newline at end of file From 77b7dfcd71c1b72c731388428b6c4aba94c8e085 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Mon, 2 Jan 2023 04:58:32 +0000 Subject: [PATCH 12/21] Update gradle version --- gradle/wrapper/gradle-wrapper.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661..85ea6d8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Mon Jan 02 04:33:47 GMT 2023 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME From 7c9da2be8096f1e9381cf41c64a7adcf19362ba0 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Mon, 2 Jan 2023 05:08:48 +0000 Subject: [PATCH 13/21] Change gradle settings to kotlin --- settings.gradle => settings.gradle.kts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename settings.gradle => settings.gradle.kts (92%) diff --git a/settings.gradle b/settings.gradle.kts similarity index 92% rename from settings.gradle rename to settings.gradle.kts index f8f39ef..8e5106a 100644 --- a/settings.gradle +++ b/settings.gradle.kts @@ -7,6 +7,7 @@ pluginManagement { mavenCentral() } } + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { @@ -14,6 +15,7 @@ dependencyResolutionManagement { mavenCentral() } } + rootProject.name = "Flagsmith" -include ':FlagsmithClient' +include(":FlagsmithClient") \ No newline at end of file From f5a9dc99534d188b7671194a33377d33cf094882 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Mon, 2 Jan 2023 05:17:43 +0000 Subject: [PATCH 14/21] Require repositories to be declared in global gradle settings not gradle project settings --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 8e5106a..5b231ab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,7 +9,7 @@ pluginManagement { } dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() From 3c8c620bf3acc8964fcf85ef6b7526ac0df5778a Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Mon, 2 Jan 2023 05:25:08 +0000 Subject: [PATCH 15/21] Update global gradle build file to kotlin --- build.gradle | 10 ---------- build.gradle.kts | 9 +++++++++ 2 files changed, 9 insertions(+), 10 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts diff --git a/build.gradle b/build.gradle deleted file mode 100644 index ce9450e..0000000 --- a/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -plugins { - id 'com.android.application' version '7.3.0' apply false - id 'com.android.library' version '7.3.0' apply false - id 'org.jetbrains.kotlin.android' version '1.7.20' apply false -} - -task clean(type: Delete) { - delete rootProject.buildDir -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..b6d5519 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("com.android.application").version("7.3.0").apply(false) + id("com.android.library").version("7.3.0").apply(false) + kotlin("android").version("1.7.20").apply(false) +} + +val clean by tasks.registering(Delete::class) { + delete(rootProject.buildDir) +} \ No newline at end of file From a765639625c3f3dc00bdc9060b61249b904c7c7d Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Mon, 2 Jan 2023 07:18:28 +0000 Subject: [PATCH 16/21] Update library gradle build file to kotlin --- FlagsmithClient/build.gradle | 66 -------------------------------- FlagsmithClient/build.gradle.kts | 65 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 66 deletions(-) delete mode 100644 FlagsmithClient/build.gradle create mode 100644 FlagsmithClient/build.gradle.kts diff --git a/FlagsmithClient/build.gradle b/FlagsmithClient/build.gradle deleted file mode 100644 index 5ccb0df..0000000 --- a/FlagsmithClient/build.gradle +++ /dev/null @@ -1,66 +0,0 @@ -plugins { - id 'com.android.library' - id 'org.jetbrains.kotlin.android' - id 'maven-publish' -} - -android { - compileSdk 33 - - defaultConfig { - minSdk 21 - targetSdk 33 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - - versionCode 2 - versionName "1.0.0" - - namespace 'com.flagsmith.kotlin' - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 - } - kotlinOptions { - jvmTarget = '1.8' - } - - testOptions { - unitTests.returnDefaultValues = true - } -} - -dependencies { - implementation 'com.google.code.gson:gson:2.9.0' - implementation 'com.github.kittinunf.fuel:fuel:2.3.1' - implementation 'com.github.kittinunf.fuel:fuel-gson:2.3.1' - - testImplementation 'junit:junit:4.13.2' - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1' - testImplementation 'org.mock-server:mockserver-netty-no-dependencies:5.14.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.4' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' -} - -afterEvaluate { - publishing { - publications { - release(MavenPublication) { - from components.release - - groupId = 'com.github.Flagsmith' - artifactId = 'flagsmith-kotlin-android-client' - version = '1.0.0' - } - } - } -} \ No newline at end of file diff --git a/FlagsmithClient/build.gradle.kts b/FlagsmithClient/build.gradle.kts new file mode 100644 index 0000000..a325ad7 --- /dev/null +++ b/FlagsmithClient/build.gradle.kts @@ -0,0 +1,65 @@ +plugins { + id("com.android.library") + kotlin("android") + id("maven-publish") +} + +android { + compileSdk = 33 + + defaultConfig { + minSdk = 21 + targetSdk = 33 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + version = "1.0.0" + namespace = "com.flagsmith.kotlin" + } + + buildTypes { + getByName("release") { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } + + testOptions { + unitTests.isReturnDefaultValues = true + } +} + +dependencies { + implementation("com.google.code.gson:gson:2.9.0") + implementation("com.github.kittinunf.fuel:fuel:2.3.1") + implementation("com.github.kittinunf.fuel:fuel-gson:2.3.1") + + testImplementation("junit:junit:4.13.2") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1") + testImplementation("org.mock-server:mockserver-netty-no-dependencies:5.14.0") + androidTestImplementation("androidx.test.ext:junit:1.1.4") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") +} + +publishing { + publications { + register("release") { + groupId = "com.github.Flagsmith" + artifactId = "flagsmith-kotlin-android-client" + version = "1.0.0" + + afterEvaluate { + from(components["release"]) + } + } + } +} \ No newline at end of file From ed06768b1444460f4cf7101fd34ae14243ae4b01 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Mon, 2 Jan 2023 23:15:00 +0000 Subject: [PATCH 17/21] Improve gradle task test reporting --- FlagsmithClient/build.gradle.kts | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/FlagsmithClient/build.gradle.kts b/FlagsmithClient/build.gradle.kts index a325ad7..134d4d9 100644 --- a/FlagsmithClient/build.gradle.kts +++ b/FlagsmithClient/build.gradle.kts @@ -1,3 +1,6 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent + plugins { id("com.android.library") kotlin("android") @@ -50,6 +53,45 @@ dependencies { androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") } +tasks.withType(Test::class) { + testLogging { + events( + TestLogEvent.FAILED, + TestLogEvent.PASSED, + TestLogEvent.SKIPPED, + TestLogEvent.STANDARD_OUT + ) + showExceptions = true + showCauses = true + showStackTraces = true + showStandardStreams = true + exceptionFormat = TestExceptionFormat.FULL + + debug { + events( + TestLogEvent.STARTED, + TestLogEvent.FAILED, + TestLogEvent.PASSED, + TestLogEvent.SKIPPED, + TestLogEvent.STANDARD_ERROR, + TestLogEvent.STANDARD_OUT + ) + exceptionFormat = TestExceptionFormat.FULL + } + info.events = debug.events + info.exceptionFormat = debug.exceptionFormat + + afterSuite(KotlinClosure2({ desc: TestDescriptor, result: TestResult -> + if (desc.parent == null) { + val output = + "| Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped) |" + val repeatLength = output.length + println("\n${"-".repeat(repeatLength)}\n$output\n${"-".repeat(repeatLength)}") + } + })) + } +} + publishing { publications { register("release") { From f6ebb156e597c895f4c1799edc0badd40776d6ec Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 3 Jan 2023 01:28:51 +0000 Subject: [PATCH 18/21] Improve gradle task test reporting --- FlagsmithClient/build.gradle.kts | 52 ++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/FlagsmithClient/build.gradle.kts b/FlagsmithClient/build.gradle.kts index 134d4d9..44fe4de 100644 --- a/FlagsmithClient/build.gradle.kts +++ b/FlagsmithClient/build.gradle.kts @@ -1,5 +1,7 @@ +import groovy.time.TimeCategory import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent +import java.util.Date plugins { id("com.android.library") @@ -80,18 +82,50 @@ tasks.withType(Test::class) { } info.events = debug.events info.exceptionFormat = debug.exceptionFormat - - afterSuite(KotlinClosure2({ desc: TestDescriptor, result: TestResult -> - if (desc.parent == null) { - val output = - "| Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped) |" - val repeatLength = output.length - println("\n${"-".repeat(repeatLength)}\n$output\n${"-".repeat(repeatLength)}") - } - })) } + + afterSuite(KotlinClosure2({ desc: TestDescriptor, result: TestResult -> + if (desc.parent == null) { + val summary = "Results: ${result.resultType} " + + "(" + + "${result.testCount} tests, " + + "${result.successfulTestCount} passed, " + + "${result.failedTestCount} failed, " + + "${result.skippedTestCount} skipped" + + ")" + val fullSummaryLine = summary.contentLine(summary.length) + val lineLength = fullSummaryLine.length + val suiteDescription = "${this.project.name}:${this.name}" + val duration = "in ${TimeCategory.minus(Date(result.endTime), Date(result.startTime))}" + val separator = tableLine(lineLength, "│", "│") + println(""" + ${tableLine(lineLength, "┌", "┐")} + ${suiteDescription.contentLine(lineLength)} + $separator + $fullSummaryLine + $separator + ${duration.contentLine(lineLength)} + ${tableLine(lineLength, "└", "┘")} + Report file: ./${this.reports.html.entryPoint.relativeTo(rootProject.rootDir)} + """.trimIndent() + ) + } + })) } +fun String.padToLength(length: Int) = + this + " ".repeat(maxOf(length - this.length, 0)) + +fun String.wrapWith(leading: String, trailing: String = leading) = + "$leading$this$trailing" + +fun String.contentLine(length: Int, extraPadding: String = " ") = + "$extraPadding$this$extraPadding".padToLength(length - 2) + .wrapWith("│") + +fun tableLine(length: Int, leading: String, trailing: String) = + "─".repeat(length - 2).wrapWith(leading, trailing) + publishing { publications { register("release") { From 7ec7e62f36b5d93620e629f95ab53745e03c1538 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Tue, 3 Jan 2023 23:54:41 +0000 Subject: [PATCH 19/21] Bump plugin versions --- build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b6d5519..328306d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ plugins { - id("com.android.application").version("7.3.0").apply(false) - id("com.android.library").version("7.3.0").apply(false) - kotlin("android").version("1.7.20").apply(false) + id("com.android.application").version("7.3.1").apply(false) + id("com.android.library").version("7.3.1").apply(false) + kotlin("android").version("1.8.0").apply(false) } val clean by tasks.registering(Delete::class) { From febc9191a7f712d13a455b263944c5f62c54a758 Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Wed, 4 Jan 2023 00:05:53 +0000 Subject: [PATCH 20/21] Bump dependency versions --- FlagsmithClient/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FlagsmithClient/build.gradle.kts b/FlagsmithClient/build.gradle.kts index 44fe4de..c2d99a5 100644 --- a/FlagsmithClient/build.gradle.kts +++ b/FlagsmithClient/build.gradle.kts @@ -44,12 +44,12 @@ android { } dependencies { - implementation("com.google.code.gson:gson:2.9.0") + implementation("com.google.code.gson:gson:2.10") implementation("com.github.kittinunf.fuel:fuel:2.3.1") implementation("com.github.kittinunf.fuel:fuel-gson:2.3.1") testImplementation("junit:junit:4.13.2") - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") testImplementation("org.mock-server:mockserver-netty-no-dependencies:5.14.0") androidTestImplementation("androidx.test.ext:junit:1.1.4") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") From 966746dee6e9def3df1189aa363453a5817de39c Mon Sep 17 00:00:00 2001 From: Robert Valdes Date: Fri, 6 Jan 2023 23:51:17 +0000 Subject: [PATCH 21/21] Minor update to improve names --- .github/workflows/{android-verfication.yml => verify.yml} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename .github/workflows/{android-verfication.yml => verify.yml} (75%) diff --git a/.github/workflows/android-verfication.yml b/.github/workflows/verify.yml similarity index 75% rename from .github/workflows/android-verfication.yml rename to .github/workflows/verify.yml index 9fee467..c8c3976 100644 --- a/.github/workflows/android-verfication.yml +++ b/.github/workflows/verify.yml @@ -1,4 +1,4 @@ -name: Android PR & Main Branch Verification CI +name: Verify on: push: @@ -15,11 +15,11 @@ jobs: steps: - uses: actions/checkout@v3 - - name: set up JDK 11 + - name: Setup JDK 11 uses: actions/setup-java@v3 with: - java-version: '11' - distribution: 'temurin' + java-version: "11" + distribution: "temurin" cache: gradle - name: Grant execute permission for gradlew