Skip to content

Commit

Permalink
Release (#63)
Browse files Browse the repository at this point in the history
* SDK version was increased

* Issue #64

* add lombok plugin for delombok feature (#61)

Co-authored-by: Bohdan Akimenko <bohdan.akimenko@kevychsolutions.com>

* The Simple Logging Facade for Java was added (#62)

Co-authored-by: Bohdan Akimenko <bohdan.akimenko@kevychsolutions.com>

* In-code documentation was added (#66)

Co-authored-by: Bohdan Akimenko <bohdan.akimenko@kevychsolutions.com>

* method isFeatureEnabled() was added (#65)

Co-authored-by: Bohdan Akimenko <bohdan.akimenko@kevychsolutions.com>

---------

Co-authored-by: Bohdan Akimenko <bohdan.akimenko@kevychsolutions.com>
  • Loading branch information
vazarkevych and Bohdan-Kim committed Jun 7, 2024
1 parent 4a09609 commit 00fad04
Show file tree
Hide file tree
Showing 31 changed files with 697 additions and 154 deletions.
25 changes: 23 additions & 2 deletions lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,23 @@
* User Manual available at https://docs.gradle.org/7.5.1/userguide/building_java_projects.html
*/

buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "io.freefair.gradle:lombok-plugin:8.6"
}
}

plugins {
// Apply the java-library plugin for API and implementation separation.
id 'java-library'
id 'maven-publish'
id 'jacoco'
id "io.freefair.lombok" version "8.6"
}

sourceCompatibility = JavaVersion.VERSION_1_8
Expand Down Expand Up @@ -45,8 +57,13 @@ dependencies {
// Adds getter, setter and builder boilerplate
// https://projectlombok.org/
// https://mvnrepository.com/artifact/org.projectlombok/lombok
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
compileOnly 'org.projectlombok:lombok:1.18.32'
annotationProcessor 'org.projectlombok:lombok:1.18.32'

// logging
implementation 'org.slf4j:slf4j-api:2.0.7'
implementation 'com.github.tony19:logback-android:3.0.0'
testImplementation 'ch.qos.logback:logback-classic:1.2.11'
}

publishing {
Expand Down Expand Up @@ -95,3 +112,7 @@ jacocoTestReport {
html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
}
}

configurations.testImplementation {
exclude module: 'logback-android'
}
7 changes: 7 additions & 0 deletions lib/src/main/java/growthbook/sdk/java/BucketRange.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import lombok.Builder;
import lombok.Data;
import org.apache.commons.math3.util.Precision;

import java.util.Objects;

/**
Expand All @@ -24,6 +25,12 @@ public class BucketRange {
Float rangeStart;
Float rangeEnd;

/**
* This method help to convert BucketRange object from JsonElement
*
* @param jsonElement json element
* @return BucketRange object
*/
static BucketRange fromJson(JsonElement jsonElement) {
JsonArray array = (JsonArray) jsonElement;
float start = array.get(0).getAsFloat();
Expand Down
6 changes: 4 additions & 2 deletions lib/src/main/java/growthbook/sdk/java/ConditionEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import java.lang.reflect.Type;
import java.util.ArrayList;
Expand All @@ -18,6 +19,7 @@
/**
* <b>INTERNAL</b>: Implementation of condition evaluation
*/
@Slf4j
class ConditionEvaluator implements IConditionEvaluator {

private final GrowthBookJsonUtils jsonUtils = GrowthBookJsonUtils.getInstance();
Expand Down Expand Up @@ -69,7 +71,7 @@ public Boolean evaluateCondition(String attributesJsonString, String conditionJs

return true;
} catch (RuntimeException e) {
e.printStackTrace();
log.error(e.getMessage(), e);
return false;
}
}
Expand Down Expand Up @@ -467,7 +469,7 @@ Boolean arePrimitivesEqual(JsonPrimitive a, JsonPrimitive b, DataType dataType)
//
}

System.out.printf("\nUnsupported data type %s", dataType);
log.info("\nUnsupported data type {}", dataType);

return false;
}
Expand Down
7 changes: 6 additions & 1 deletion lib/src/main/java/growthbook/sdk/java/DecryptionUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import lombok.extern.slf4j.Slf4j;

/**
* INTERNAL: This class is used internally to decrypt an encrypted features response
*/
@Slf4j
class DecryptionUtils {

public static class DecryptionException extends Exception {
Expand All @@ -28,6 +30,7 @@ public DecryptionException(String errorMessage) {

public static String decrypt(String payload, String encryptionKey) throws DecryptionException {
if (!payload.contains(".")) {
log.error("DecryptionException: Invalid payload");
throw new DecryptionException("Invalid payload");
}

Expand All @@ -54,8 +57,10 @@ public static String decrypt(String payload, String encryptionKey) throws Decryp

return new String(plainText);
} catch (InvalidAlgorithmParameterException e) {
log.error("DecryptionException: Invalid payload", e);
throw new DecryptionException("Invalid payload");
} catch (InvalidKeyException e) {
log.error("DecryptionException: Invalid encryption key", e);
throw new DecryptionException("Invalid encryption key");
} catch (
NoSuchAlgorithmException
Expand All @@ -65,7 +70,7 @@ public static String decrypt(String payload, String encryptionKey) throws Decryp
| IllegalArgumentException
| BadPaddingException e
) {
e.printStackTrace();
log.error(e.getMessage(), e);
throw new DecryptionException(e.getMessage());
}
}
Expand Down
42 changes: 42 additions & 0 deletions lib/src/main/java/growthbook/sdk/java/Experiment.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

import javax.annotation.Nullable;
import java.util.ArrayList;

Expand Down Expand Up @@ -57,6 +58,9 @@ public class Experiment<ValueType> {
@Nullable
ArrayList<ParentCondition> parentConditions;

/**
* A tuple that contains the namespace identifier, plus a range of coverage for the experiment
*/
@Nullable
@Deprecated
Namespace namespace;
Expand All @@ -68,41 +72,79 @@ public class Experiment<ValueType> {

/**
* What user attribute should be used to assign variations (defaults to `id`)
* All users included in the experiment will be forced into the specific variation index
*/
@Builder.Default
String hashAttribute = "id";

//new properties v0.4.0
/**
* The hash version to use (default to 1)
*/
@Nullable
Integer hashVersion;

/**
* Array of ranges, one per variation
*/
@Nullable
ArrayList<BucketRange> ranges;

/**
* Meta info about the variations
*/
@Nullable
@SerializedName("meta")
ArrayList<VariationMeta> meta;

/**
* Array of filters to apply
*/
@Nullable
ArrayList<Filter> filters;

/**
* The hash seed to use
*/
@Nullable
String seed;

/**
* Human-readable name for the experiment
*/
@Nullable
String name;

/**
* Identifier of the current experiment phase
*/
@Nullable
String phase;

/**
* When using sticky bucketing, can be used as a fallback to assign variations
*/
@Nullable
String fallbackAttribute;

/**
* If true, sticky bucketing will be disabled for this experiment.
* (Note: sticky bucketing is only available
* if a StickyBucketingService is provided in the Context)
*/
@Nullable
Boolean disableStickyBucketing;

/**
* The sticky bucket version number that can be used to force a re-bucketing
* of users (default to 0)
*/
@Nullable
Integer bucketVersion;

/**
* Any users with a sticky bucket version less than this will be excluded from the experiment
*/
@Nullable
Integer minBucketVersion;

Expand Down
22 changes: 18 additions & 4 deletions lib/src/main/java/growthbook/sdk/java/ExperimentEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,29 @@
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;

/**
* <b>INTERNAL</b>: Implementation of experiment evaluation
*/
@Slf4j
class ExperimentEvaluator implements IExperimentEvaluator {

private final ConditionEvaluator conditionEvaluator = new ConditionEvaluator();
private final GrowthBookJsonUtils jsonUtils = GrowthBookJsonUtils.getInstance();


/**
* Takes Context & Experiment & returns Experiment Result
*
* @param experiment Experiment
* @param context GBContext
* @param featureId String(can be null)
* @param attributeOverrides JsonObject
* @return ExperimentResult
*/
@Override
public <ValueType> ExperimentResult<ValueType> evaluateExperiment(Experiment<ValueType> experiment,
GBContext context,
Expand Down Expand Up @@ -111,6 +122,7 @@ public <ValueType> ExperimentResult<ValueType> evaluateExperiment(Experiment<Val
if (!foundStickyBucket) {

List<Filter> filters = experiment.getFilters();
@Deprecated
Namespace namespace = experiment.getNamespace();

if (filters != null) {
Expand Down Expand Up @@ -156,8 +168,10 @@ public <ValueType> ExperimentResult<ValueType> evaluateExperiment(Experiment<Val
)
);

if (parentResult.source.equals(FeatureResultSource.CYCLIC_PREREQUISITE)) {
return getExperimentResult(context, experiment, -1, false, featureId, null, null, attributeOverrides);
if (parentResult.getSource() != null) {
if (parentResult.getSource().equals(FeatureResultSource.CYCLIC_PREREQUISITE)) {
return getExperimentResult(context, experiment, -1, false, featureId, null, null, attributeOverrides);
}
}

Map<String, Object> evalObj = new HashMap<>();
Expand All @@ -173,7 +187,7 @@ public <ValueType> ExperimentResult<ValueType> evaluateExperiment(Experiment<Val

// blocking prerequisite eval failed: feature evaluation fails
if (!evalCondition) {
System.out.println("Feature blocked by prerequisite");
log.info("Feature blocked by prerequisite");
return getExperimentResult(context, experiment, -1, false, featureId, null, null, attributeOverrides);
}
}
Expand Down
Loading

0 comments on commit 00fad04

Please sign in to comment.