Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate files to kotlin #10

Merged
merged 14 commits into from
Oct 11, 2023
Merged
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ We are breaking down the various parts that we need to migrate to Swift in the s
|---------------------|--------------------------------------------------|--------|
| Files | `@featurevisor/types` ➡️ `Types.kt` | ✅ |
| | SDK's `bucket.ts` ➡️ `Bucket.kt` | ✅ |
| | SDK's `conditions.ts` ➡️ `Conditions.kt` | 🟠 |
| | SDK's `datafileReader.ts` ➡️ `DatafileReader.kt` | |
| | SDK's `emitter.ts` ➡️ `Emitter.kt` | |
| | SDK's `feature.ts` ➡️ `Emitter.kt` | |
| | SDK's `instance.ts` ➡️ `Instance.kt` | |
| | SDK's `conditions.ts` ➡️ `Conditions.kt` | |
| | SDK's `datafileReader.ts` ➡️ `DatafileReader.kt` | |
| | SDK's `emitter.ts` ➡️ `Emitter.kt` | |
| | SDK's `feature.ts` ➡️ `Feature.kt` | |
| | SDK's `instance.ts` ➡️ `Instance.kt` | 🟠 |
| | SDK's `logger.ts` ➡️ `Logger.kt` | |
| | SDK's `segments.ts` ➡️ `segments.kt` | |
| | SDK's `segments.ts` ➡️ `Segments.kt` | |
| | | |
| Constructor options | `bucketKeySeparator` | |
| | `configureBucketKey` | |
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ dependencies {
// Use the JUnit 5 integration.
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2")
// Uncomment when needed
// testImplementation("io.mockk:mockk:1.13.8")
testImplementation("io.mockk:mockk:1.13.8")
testImplementation("io.kotest:kotest-assertions-core:5.7.2")

testRuntimeOnly("org.junit.platform:junit-platform-launcher")
Expand Down
34 changes: 34 additions & 0 deletions src/main/kotlin/com/featurevisor/sdk/DataFileReader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.featurevisor.sdk

import com.featurevisor.types.Attribute
import com.featurevisor.types.AttributeKey
import com.featurevisor.types.DatafileContent
import com.featurevisor.types.Feature
import com.featurevisor.types.FeatureKey
import com.featurevisor.types.Segment
import com.featurevisor.types.SegmentKey

class DataFileReader constructor(
datafileJson: DatafileContent,
) {
private val schemaVersion: String = datafileJson.schemaVersion
private val revision: String = datafileJson.revision
private val attributes: List<Attribute> = datafileJson.attributes
private val segments: List<Segment> = datafileJson.segments
private val features: List<Feature> = datafileJson.features

fun getRevision(): String = revision

fun getSchemaVersion(): String = schemaVersion

fun getAllAttributes(): List<Attribute> = attributes

fun getAttribute(attributeKey: AttributeKey): Attribute? =
attributes.find { attribute -> attribute.key == attributeKey }

fun getSegment(segmentKey: SegmentKey): Segment? =
segments.find { segment -> segment.key == segmentKey }

fun getFeature(featureKey: FeatureKey): Feature? =
features.find { feature -> feature.key == featureKey }
}
23 changes: 23 additions & 0 deletions src/main/kotlin/com/featurevisor/sdk/Emitter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.featurevisor.sdk

import com.featurevisor.types.EventName

class Emitter {
private val listeners = mutableMapOf<EventName, () -> Unit>()

fun addListener(event: EventName, listener: () -> Unit) {
listeners.putIfAbsent(event, listener)
}

fun removeListener(event: EventName) {
listeners.remove(event)
}

fun removeAllListeners() {
listeners.clear()
}

operator fun invoke(event: EventName) {
listeners.getOrDefault(event, null)?.invoke()
}
}
81 changes: 81 additions & 0 deletions src/main/kotlin/com/featurevisor/types/DataFile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.featurevisor.types

/**
* Datafile-only types
*/
// 0 to 100,000
typealias Percentage = Int

data class Range(
val start: Percentage,
val end: Percentage,
)

data class Allocation(
val variation: VariationValue,
val range: Range,
)

data class Traffic(
val key: RuleKey,
val segments: GroupSegment,
val percentage: Percentage,

val enabled: Boolean?,
val variation: VariationValue?,
val variables: VariableValues?,

val allocation: List<Allocation>,
)

typealias PlainBucketBy = String

typealias AndBucketBy = List<BucketBy>

data class OrBucketBy(
val or: List<String>,
)

sealed class BucketBy {
data class Single(val bucketBy: PlainBucketBy) : BucketBy()
data class And(val bucketBy: AndBucketBy) : BucketBy()
data class Or(val bucketBy: OrBucketBy) : BucketBy()
}

data class RequiredWithVariation(
val key: FeatureKey,
val variation: VariationValue,
)

sealed class Required {
data class FeatureKey(val required: FeatureKey) : Required()
data class WithVariation(val required: RequiredWithVariation) : Required()
}

data class Feature(
val key: FeatureKey,
val deprecated: Boolean?,
val variablesSchema: List<VariableSchema>?,
val variations: List<Variation>?,
val bucketBy: BucketBy,
val required: List<Required>?,
val traffic: List<Traffic>,
val force: List<Force>?,

// if in a Group (mutex), these are available slot ranges
val ranges: List<Range>?,
)

data class DatafileContent(
val schemaVersion: String,
val revision: String,
val attributes: List<Attribute>,
val segments: List<Segment>,
val features: List<Feature>,
)

data class OverrideFeature(
val enabled: Boolean,
val variation: VariationValue?,
val variables: VariableValues?,
)
8 changes: 8 additions & 0 deletions src/main/kotlin/com/featurevisor/types/EventName.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.featurevisor.types

enum class EventName {
READY,
REFRESH,
UPDATE,
ACTIVATION,
}
32 changes: 32 additions & 0 deletions src/main/kotlin/com/featurevisor/types/Segment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.featurevisor.types

typealias SegmentKey = String

data class Segment(
val archived: Boolean?,
val key: SegmentKey,
val conditions: Condition,
)

typealias PlainGroupSegment = SegmentKey

data class AndGroupSegment(
val and: List<GroupSegment>,
)

data class OrGroupSegment(
val or: List<GroupSegment>,
)

data class NotGroupSegment(
val not: List<GroupSegment>,
)

sealed class GroupSegment {
data class Plain(val segment: PlainGroupSegment) : GroupSegment()
data class Multiple(val segments: List<GroupSegment>) : GroupSegment()

data class And(val segment: AndGroupSegment) : GroupSegment()
data class Or(val segment: OrGroupSegment) : GroupSegment()
data class Not(val segment: NotGroupSegment) : GroupSegment()
}
111 changes: 0 additions & 111 deletions src/main/kotlin/com/featurevisor/types/Types.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,6 @@ package com.featurevisor.types

typealias Context = Map<AttributeKey, AttributeValue>

typealias SegmentKey = String

data class Segment(
val archived: Boolean?,
val key: SegmentKey,
val conditions: Condition,
)

typealias PlainGroupSegment = SegmentKey

data class AndGroupSegment(
val and: List<GroupSegment>,
)

data class OrGroupSegment(
val or: List<GroupSegment>,
)

data class NotGroupSegment(
val not: List<GroupSegment>,
)

sealed class GroupSegment {
data class Plain(val segment: PlainGroupSegment) : GroupSegment()
data class Multiple(val segments: List<GroupSegment>) : GroupSegment()

data class And(val segment: AndGroupSegment) : GroupSegment()
data class Or(val segment: OrGroupSegment) : GroupSegment()
data class Not(val segment: NotGroupSegment) : GroupSegment()
}

typealias VariationValue = String

typealias VariableKey = String
Expand Down Expand Up @@ -124,86 +93,6 @@ typealias BucketKey = String
// 0 to 100,000
typealias BucketValue = Int

/**
* Datafile-only types
*/
// 0 to 100,000
typealias Percentage = Int

data class Range(
val start: Percentage,
val end: Percentage,
)

data class Allocation(
val variation: VariationValue,
val range: Range,
)

data class Traffic(
val key: RuleKey,
val segments: GroupSegment,
val percentage: Percentage,

val enabled: Boolean?,
val variation: VariationValue?,
val variables: VariableValues?,

val allocation: List<Allocation>,
)

typealias PlainBucketBy = String

typealias AndBucketBy = List<BucketBy>

data class OrBucketBy(
val or: List<String>,
)

sealed class BucketBy {
data class Single(val bucketBy: PlainBucketBy) : BucketBy()
data class And(val bucketBy: AndBucketBy) : BucketBy()
data class Or(val bucketBy: OrBucketBy) : BucketBy()
}

data class RequiredWithVariation(
val key: FeatureKey,
val variation: VariationValue,
)

sealed class Required {
data class FeatureKey(val required: FeatureKey) : Required()
data class WithVariation(val required: RequiredWithVariation) : Required()
}

data class Feature(
val key: FeatureKey,
val deprecated: Boolean?,
val variablesSchema: List<VariableSchema>?,
val variations: List<Variation>?,
val bucketBy: BucketBy,
val required: List<Required>?,
val traffic: List<Traffic>,
val force: List<Force>?,

// if in a Group (mutex), these are available slot ranges
val ranges: List<Range>?,
)

data class DatafileContent(
val schemaVersion: String,
val revision: String,
val attributes: List<Attribute>,
val segments: List<Segment>,
val features: List<Feature>,
)

data class OverrideFeature(
val enabled: Boolean,
val variation: VariationValue?,
val variables: VariableValues?,
)

typealias StickyFeatures = Map<FeatureKey, OverrideFeature>

typealias InitialFeatures = Map<FeatureKey, VariationValue>
Expand Down
49 changes: 49 additions & 0 deletions src/test/kotlin/com/featurevisor/sdk/DataFileReaderTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.featurevisor.sdk

import com.featurevisor.sdk.factory.DatafileContentFactory
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test

class DataFileReaderTest {

private val systemUnderTest = DataFileReader(
datafileJson = DatafileContentFactory.get()
)

@Test
fun `getRevision() returns correct value`() {
systemUnderTest.getRevision() shouldBe "revision"
}

@Test
fun `getSchemaVersion returns correct value`() {
systemUnderTest.getSchemaVersion() shouldBe "schemaVersion"
}

@Test
fun `getAllAttributes() returns correct list`() {
systemUnderTest.getAllAttributes() shouldBe DatafileContentFactory.getAttributes()
}

@Test
fun `getAttribute() returns correct value`() {
systemUnderTest.getAttribute("browser_type") shouldBe DatafileContentFactory.getAttributes().first()
}

@Test
fun `getSegment() returns correct value`() {
systemUnderTest.getSegment("netherlands") shouldBe DatafileContentFactory.getSegments().first()
}

@Test
fun `getFeature() returns correct value`() {
systemUnderTest.getFeature("landing_page") shouldBe DatafileContentFactory.getFeatures().first()
}

@Test
fun `return null if key not present in collection`() {
systemUnderTest.getAttribute("country") shouldBe null
systemUnderTest.getSegment("germany") shouldBe null
systemUnderTest.getFeature("key_moments") shouldBe null
}
}
Loading