GithubHelp home page GithubHelp logo

patxibocos / poetimizely Goto Github PK

View Code? Open in Web Editor NEW
19.0 4.0 1.0 884 KB

Generate Kotlin type safe accessors for Optimizely experiments and features

License: MIT License

Kotlin 100.00%
optimizely kotlin typesafe kotlinpoet gradle-plugin maven-plugin

poetimizely's Introduction

poetimizely's People

Contributors

patxibocos avatar ralphpina avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

hazer

poetimizely's Issues

Support feature variables

In addition to the current support for features that we already have, variables for features should also be included.

Some Optimizely docs regarding Feature variables:

A feature variable is a property tied to a specific feature. They can be of 4 different types: boolean, string, double and integer.

The proposal is to have an API that would allow the following:

val variable1String: String? = optimizely.getFeatureVariable(Features.Feature1, Feature1.Variables.Variable1, userId)

or:

val variable1Boolean: Boolean? = optimizely.getFeatureVariable(Features.Feature1, Feature1.Variables.Variable2, userId)

For this we need to represent a feature with an interface as now we need to make it generic:

interface BaseFeature<V : BaseVariable<*, *>> {
    val key: String
}

and then a variable:

interface BaseVariable<F, T> {
    val key: String
}

A variable is doubled parameterized, first with the type of the feature that belongs to, and secondly with the type of the variable (String, Int, Boolean or Double).

A feature is now also parameterized with the variable type that will hold.

This would allow defining features as objects:

object Feature1 : BaseFeature<Feature1Variables<*>> {
    override val key: String = "Feature1"
}

and variables too:

sealed class Feature1Variables<T> : BaseVariable<Features.Feature1, T> {
    object Variable1 : Feature1Variables<String>() {
        override val key: String = "feature1"
    }

    object Variable2 : Feature1Variables<Boolean>() {
        override val key: String = "feature2"
    }
}

With all of the above, 4 new extension functions could be defined:

fun <V : BaseVariable<F, *>, F : BaseFeature<V>> Optimizely.getFeatureVariable(
    feature: BaseFeature<V>,
    variable: BaseVariable<F, Boolean>,
    userId: String,
    attributes: Map<String, Any> = emptyMap()
): Boolean? {
    return this.getFeatureVariableBoolean(feature.key, variable.key, userId, attributes)
}

fun <V : BaseVariable<F, *>, F : BaseFeature<V>> Optimizely.getFeatureVariable(
    feature: BaseFeature<V>,
    variable: BaseVariable<F, String>,
    userId: String,
    attributes: Map<String, Any> = emptyMap()
): String? {
    return this.getFeatureVariableString(feature.key, variable.key, userId, attributes)
}

fun <V : BaseVariable<F, *>, F : BaseFeature<V>> Optimizely.getFeatureVariable(
    feature: BaseFeature<V>,
    variable: BaseVariable<F, Double>,
    userId: String,
    attributes: Map<String, Any> = emptyMap()
): Double? {
    return this.getFeatureVariableDouble(feature.key, variable.key, userId, attributes)
}

fun <V : BaseVariable<F, *>, F : BaseFeature<V>> Optimizely.getFeatureVariable(
    feature: BaseFeature<V>,
    variable: BaseVariable<F, Int>,
    userId: String,
    attributes: Map<String, Any> = emptyMap()
): Int? {
    return this.getFeatureVariableInteger(feature.key, variable.key, userId, attributes)
}

The great benefits of this approach:

  • We just have a single entry point (overloaded) for querying feature variables values
  • Compiler enforces that we cannot mix a given feature with variables from a different feature:
optimizely.getFeatureVariable(Features.Feature1, Feature2Variables.Variable2, userId)

πŸ” It will not compile because the type of the first argument is BaseFeature<Feature1Variables> and the second is Feature2Variables<Feature1Variables, Boolean>

Test with an Optimizely project

How do we get access to test Optimizely? It seems we can create an account for free, see screenshot below.

  1. create an account
  2. write a sample project (maybe a pure Kotlin app, and an Android one?)
  3. Integrate poetimizely in the samples.
  4. maybe these samples allow you to toggle experiments and feature rollouts in app?
  5. deploy prod sample for others to use (unless we're cool leaking our test Optimizely keys, which sounds like a bad idea πŸ˜‰)

Screenshot_20200330-075319

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/build_test.yml
  • actions/checkout v4
  • actions/setup-java v4
  • codecov/codecov-action v4
.github/workflows/release.yml
  • actions/checkout v4
  • actions/setup-java v4
gradle
poetimizely-core/gradle.properties
poetimizely-core/settings.gradle.kts
poetimizely-core/build.gradle.kts
poetimizely-core/gradle/libs.versions.toml
  • com.optimizely.ab:core-api 4.1.1
  • io.kotest:kotest-runner-junit5-jvm 5.9.1
  • io.kotest:kotest-assertions-core-jvm 5.9.1
  • dev.zacsweers.kctfork:core 0.5.1
  • org.jetbrains.kotlinx:kotlinx-coroutines-core 1.8.1
  • org.jetbrains.kotlinx:kotlinx-serialization-json 1.7.1
  • com.squareup:kotlinpoet 1.18.1
  • com.pinterest.ktlint:ktlint-core 0.49.1
  • com.pinterest.ktlint:ktlint-ruleset-standard 1.3.1
  • com.pinterest.ktlint:ktlint-rule-engine 1.3.1
  • com.pinterest.ktlint:ktlint-rule-engine-core 1.3.1
  • io.ktor:ktor-client-content-negotiation 2.3.12
  • io.ktor:ktor-client-core 2.3.12
  • io.ktor:ktor-client-cio 2.3.12
  • io.ktor:ktor-serialization-kotlinx-json 2.3.12
  • io.mockk:mockk 1.13.12
  • net.lingala.zip4j:zip4j 2.11.5
  • org.jetbrains.kotlin.jvm 2.0.10
  • org.jetbrains.kotlin.plugin.serialization 2.0.10
  • io.github.gradle-nexus.publish-plugin 2.0.0
  • com.diffplug.spotless 6.25.0
poetimizely-gradle-plugin/settings.gradle.kts
poetimizely-gradle-plugin/build.gradle.kts
poetimizely-gradle-plugin/gradle/libs.versions.toml
  • io.kotest:kotest-runner-junit5-jvm 5.9.1
  • io.kotest:kotest-assertions-core-jvm 5.9.1
  • io.github.patxibocos:poetimizely-core 1.0.8
  • org.jetbrains.kotlin.jvm 2.0.10
  • org.jetbrains.kotlin.plugin.serialization 2.0.10
  • io.github.gradle-nexus.publish-plugin 2.0.0
  • com.diffplug.spotless 6.25.0
  • com.gradle.plugin-publish 1.2.1
samples/gradle-plugin/gradle.properties
samples/gradle-plugin/settings.gradle.kts
samples/gradle-plugin/build.gradle.kts
  • org.jetbrains.kotlin.jvm 2.0.10
  • io.github.patxibocos.poetimizely 1.0.8
  • com.optimizely.ab:core-api 4.1.1
  • com.optimizely.ab:core-httpclient-impl 4.1.1
gradle-wrapper
poetimizely-core/gradle/wrapper/gradle-wrapper.properties
  • gradle 8.10
poetimizely-gradle-plugin/gradle/wrapper/gradle-wrapper.properties
  • gradle 8.10
samples/gradle-plugin/gradle/wrapper/gradle-wrapper.properties
  • gradle 8.10
maven
poetimizely-maven-plugin/pom.xml
  • org.jetbrains.kotlin:kotlin-maven-plugin 2.0.10
  • org.apache.maven.plugins:maven-plugin-plugin 3.13.1
  • com.diffplug.spotless:spotless-maven-plugin 2.43.0
  • org.apache.maven.plugins:maven-source-plugin 3.3.1
  • org.apache.maven.plugins:maven-surefire-plugin 3.3.1
  • org.sonatype.plugins:nexus-staging-maven-plugin 1.7.0
  • org.simplify4u.plugins:sign-maven-plugin 1.1.0
  • org.jacoco:jacoco-maven-plugin 0.8.12
  • org.apache.maven:maven-compat 3.9.8
  • org.apache.maven.plugin-testing:maven-plugin-testing-harness 4.0.0-alpha-2
  • org.apache.maven:maven-plugin-api 3.9.8
  • org.apache.maven.plugin-tools:maven-plugin-annotations 3.13.1
  • org.apache.maven:maven-project 2.2.1
  • org.junit.jupiter:junit-jupiter-api 5.11.0
  • org.junit.jupiter:junit-jupiter-engine 5.11.0
  • io.mockk:mockk-jvm 1.13.12
samples/maven-plugin/pom.xml
  • com.optimizely.ab:core-api 4.1.1
  • com.optimizely.ab:core-httpclient-impl 4.1.1
  • org.jetbrains.kotlin:kotlin-stdlib 2.0.10
  • io.github.patxibocos:poetimizely-maven-plugin 1.0.8
  • org.jetbrains.kotlin:kotlin-maven-plugin 2.0.10
maven-wrapper
poetimizely-maven-plugin/.mvn/wrapper/maven-wrapper.properties
  • maven 3.9.8
samples/maven-plugin/.mvn/wrapper/maven-wrapper.properties
  • maven 3.9.8

  • Check this box to trigger a request for Renovate to run again on this repository

Create extension functions instead of wrapper clients

I think it would be better to have this extension functions instead of wrapper clients:

fun <V : BaseVariation> Optimizely.getVariationForExperiment(
    experiment: BaseExperiment<out V>, userId: String,
    attributes: Map<String, Any> = emptyMap()
): V? {
    val variation = this.activate(experiment.key, userId, attributes)
    return experiment.variations.find { it.key == variation?.key }
}
fun Optimizely.isFeatureEnabled(feature: Features, userId: String): Boolean =
    this.isFeatureEnabled(feature.key, userId)

Make the library a Gradle plugin (standalone project)

As a POC this library is a regular Java application, but for convenience it should be a Gradle plugin
Developers will apply the plugin which will create a new Gradle task to generate the code + allow some configuration such as destination package, experiments to exclude, etc...

Document how to include, configure, and use library

Consumers should know how to:

  • include the library in their projects
  • how to configure it so it generates the code they want
  • how to access and use the generated code

We have already started documenting this in various Google Docs.

  • including and configuration docs are started here.
  • features is documented here.

The docs should probably be in the README. Should we also setup GitHub pages for a fancy website?

Failure finding main SourceSet

Version: 1.0.0-beta02

The ./gradlew poetimize task fails with the error message:

Execution failed for task ':app:poetimize'.
> SourceSet with name 'main' not found.

The ./gradlew sourceSets task indicates a main exists:

main
----
Compile configuration: compile
build.gradle name: android.sourceSets.main
Java sources: [app/src/main/java, app/src/main/kotlin]
Manifest file: app/src/main/AndroidManifest.xml
Android resources: [app/src/main/res]
Assets: [app/src/main/assets]
AIDL sources: [app/src/main/aidl]
RenderScript sources: [app/src/main/rs]
JNI sources: [app/src/main/jni]
JNI libraries: [app/src/main/jniLibs]
Java-style resources: [app/src/main/resources]

Let me know if there's other info I may provide to help.

Show Optimizely Dev advocates

Share project with Optimizely once we have some adoption (maybe just us?). Maybe they can give feedback, help improve it, share their roadmap, etc. Maybe they can help promote it to their user base?

Configure Actions to publish the plugin when a release is created in GitHub

GitHub actions' workflows can be triggered when a new release is done
https://help.github.com/en/actions/reference/events-that-trigger-workflows#release-event-release
So we should:

  • Create a GitHub workflow to run the publishPlugins task
  • Store Gradle publish key and secret as secrets in the repo
  • Run the task: ./gradlew publishPlugins -Pgradle.publish.key=<GRADLE_PUBLISH_KEY> -Pgradle.publish.secret=<GRADLE_PUBLISH_SECRET>

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.