Skip to content

Commit

Permalink
Java SSE example (#43)
Browse files Browse the repository at this point in the history
* WIP jvm example SSE

* jvm example: add SSE error branches to enum checks

* add greeting to SSE example

* update version in examples

* cleanup

* bump growthbook version in the android project
  • Loading branch information
tinahollygb authored Aug 2, 2023
1 parent 4dfceae commit 735ca66
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 24 deletions.
2 changes: 1 addition & 1 deletion android-example/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dependencies {
implementation 'androidx.navigation:navigation-fragment:2.5.0'
implementation 'androidx.navigation:navigation-ui:2.5.0'

implementation 'com.github.growthbook:growthbook-sdk-java:0.7.1'
implementation 'com.github.growthbook:growthbook-sdk-java:0.9.0'

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
Expand Down
2 changes: 1 addition & 1 deletion jvm-kotlin-ktor-example/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ dependencies {
// GrowthBook SDK

// 2. Add the GrowthBook SDK
implementation("com.github.growthbook:growthbook-sdk-java:0.7.1")
implementation("com.github.growthbook:growthbook-sdk-java:0.9.0")

// Test dependencies
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import org.koin.dsl.module
val serviceModule = module {
single<AcmeDonutFeaturesRepository> {
AcmeDonutFeaturesRepository(
endpoint = "https://cdn.growthbook.io/api/features/java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8",
apiHost = "https://cdn.growthbook.io",
clientKey = "sdk-pGmC6LrsiUoEUcpZ",
ttlSeconds = 10,
).apply {
onFeaturesRefresh {
Expand All @@ -22,7 +23,8 @@ val serviceModule = module {

single<BasicEncryptedFeaturesRepository> {
BasicEncryptedFeaturesRepository(
endpoint = "https://cdn.growthbook.io/api/features/sdk-862b5mHcP9XPugqD",
apiHost = "https://cdn.growthbook.io",
clientKey = "sdk-862b5mHcP9XPugqD",
encryptionKey = "BhB1wORFmZLTDjbvstvS8w==",
ttlSeconds = 15
).apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.growthbook.example.plugins.growthbook

import growthbook.sdk.java.FeatureRefreshStrategy
import growthbook.sdk.java.GBFeaturesRepository

class AcmeDonutFeaturesRepository(
endpoint: String,
apiHost: String,
clientKey: String,
encryptionKey: String? = null,
ttlSeconds: Int
) : GBFeaturesRepository(endpoint, encryptionKey, ttlSeconds)
) : GBFeaturesRepository(apiHost, clientKey, encryptionKey, FeatureRefreshStrategy.STALE_WHILE_REVALIDATE, ttlSeconds)
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.growthbook.example.plugins.growthbook

import growthbook.sdk.java.FeatureRefreshStrategy
import growthbook.sdk.java.GBFeaturesRepository

class BasicEncryptedFeaturesRepository(
endpoint: String,
encryptionKey: String,
apiHost: String,
clientKey: String,
encryptionKey: String? = null,
ttlSeconds: Int
) : GBFeaturesRepository(endpoint, encryptionKey, ttlSeconds)
) : GBFeaturesRepository(apiHost, clientKey, encryptionKey, FeatureRefreshStrategy.STALE_WHILE_REVALIDATE, ttlSeconds)
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ fun Routing.acmeRoutes() {
.builder()
.featuresJson(acmeDonutFeaturesRepository.featuresJson)
.attributesJson(user.toJson())
.featureUsageCallback(object : FeatureUsageCallback {
override fun <ValueType : Any?> onFeatureUsage(featureKey: String?, result: FeatureResult<ValueType>?) {
application.log.info("🔵feature usage callback called. key = $featureKey , result = $result")
}
})
.trackingCallback(object : TrackingCallback {
override fun <ValueType : Any?> onTrack(
experiment: Experiment<ValueType>?,
Expand Down
2 changes: 1 addition & 1 deletion jvm-spring-web/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies {
// implementation 'com.github.growthbook:growthbook-sdk-java:main-SNAPSHOT'

// Option: specific release version
implementation 'com.github.growthbook:growthbook-sdk-java:0.7.1'
implementation 'com.github.growthbook:growthbook-sdk-java:0.9.0'

// This examples project includes examples for Gson and JSON deserialization
implementation 'com.google.code.gson:gson:2.10'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
System.out.println("Features forced by URL: http://localhost:8080/url-feature-force");
System.out.println("Features forced by URL (with URL overrides): http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!");
System.out.println("Using an interceptor: http://localhost:8080/interceptors");
System.out.println("Using SSE: http://localhost:8080/sse");
};
}
}
27 changes: 27 additions & 0 deletions jvm-spring-web/src/main/java/com/example/demo/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.example.demo.models.*;
import com.example.demo.services.AcmeDonutsFeatureService;
import com.example.demo.services.BasicEncryptedFeaturesService;
import com.example.demo.services.RealTimeSSEFeaturesService;
import growthbook.sdk.java.FeatureResult;
import growthbook.sdk.java.GBContext;
import growthbook.sdk.java.GrowthBook;
Expand Down Expand Up @@ -39,6 +40,9 @@ public class MainController {
@Autowired
AcmeDonutsFeatureService acmeDonutsFeatureService;

@Autowired
RealTimeSSEFeaturesService realTimeSSEFeaturesService;

// If you are managing the fetching of your own features, this class is doing that.
FeaturesRepository featuresRepository = new FeaturesRepository();

Expand Down Expand Up @@ -388,4 +392,27 @@ public ResponseEntity<HashMap<String, Object>> usingInterceptors(HttpServletRequ

return new ResponseEntity<>(res, HttpStatus.OK);
}

@GetMapping("/sse")
public ResponseEntity<HashMap<String, Object>> evalFeaturesFromSSE() {
// prepare response object
HashMap<String, Object> res = new HashMap<>();

// create GrowthBook instance with features from real-time SSE features service
GBContext context = GBContext.builder()
.featuresJson(realTimeSSEFeaturesService.getFeaturesJson())
.attributesJson("{\"version\": \"2.1.0\"}")
.build();
GrowthBook growthBook = new GrowthBook(context);

// get values
String appVersion = growthBook.getFeatureValue("app_name", "unknown app version");
String greeting = growthBook.getFeatureValue("greeting", "???");

// add values to response
res.put("app_version", appVersion);
res.put("greeting", greeting);

return new ResponseEntity<>(res, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import growthbook.sdk.java.FeatureFetchException;
import growthbook.sdk.java.FeatureRefreshCallback;
import growthbook.sdk.java.FeatureRefreshStrategy;
import growthbook.sdk.java.GBFeaturesRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Expand All @@ -11,8 +12,10 @@ public class AcmeDonutsFeatureService extends GBFeaturesRepository {
@Autowired
public AcmeDonutsFeatureService() {
super(
"https://cdn.growthbook.io/api/features/java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8",
"https://cdn.growthbook.io",
"java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8",
null,
FeatureRefreshStrategy.STALE_WHILE_REVALIDATE,
10
);

Expand All @@ -39,13 +42,13 @@ void handleError(FeatureFetchException e) {
// Handle NO_RESPONSE_ERROR
}

case SSE_CONNECTION_ERROR -> {
// SSE is not applicable for this service but this was added here for completion.
}

case CONFIGURATION_ERROR, UNKNOWN -> {
throw new RuntimeException(e);
}
}
}

private AcmeDonutsFeatureService(String endpoint, String encryptionKey, Integer ttlSeconds) {
super(endpoint, encryptionKey, ttlSeconds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import growthbook.sdk.java.FeatureFetchException;
import growthbook.sdk.java.FeatureRefreshCallback;
import growthbook.sdk.java.FeatureRefreshStrategy;
import growthbook.sdk.java.GBFeaturesRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Expand All @@ -10,11 +11,7 @@
public class BasicEncryptedFeaturesService extends GBFeaturesRepository {
@Autowired
public BasicEncryptedFeaturesService() {
super(
"https://cdn.growthbook.io/api/features/sdk-862b5mHcP9XPugqD",
"BhB1wORFmZLTDjbvstvS8w==",
15
);
super("https://cdn.growthbook.io", "sdk-862b5mHcP9XPugqD", "BhB1wORFmZLTDjbvstvS8w==", FeatureRefreshStrategy.STALE_WHILE_REVALIDATE, 15);

this.onFeaturesRefresh(new FeatureRefreshCallback() {
@Override
Expand All @@ -39,13 +36,13 @@ void handleError(FeatureFetchException e) {
// Handle NO_RESPONSE_ERROR
}

case SSE_CONNECTION_ERROR -> {
// SSE is not applicable for this service but this was added here for completion.
}

case CONFIGURATION_ERROR, UNKNOWN -> {
throw new RuntimeException(e);
}
}
}

private BasicEncryptedFeaturesService(String endpoint, String encryptionKey, Integer ttlSeconds) {
super(endpoint, encryptionKey, ttlSeconds);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.example.demo.services;

import growthbook.sdk.java.FeatureFetchException;
import growthbook.sdk.java.FeatureRefreshCallback;
import growthbook.sdk.java.FeatureRefreshStrategy;
import growthbook.sdk.java.GBFeaturesRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RealTimeSSEFeaturesService extends GBFeaturesRepository {
@Autowired
public RealTimeSSEFeaturesService() {
super(
"https://cdn.growthbook.io",
"sdk-pGmC6LrsiUoEUcpZ",
null,
FeatureRefreshStrategy.SERVER_SENT_EVENTS,
null
);

this.onFeaturesRefresh(new FeatureRefreshCallback() {
@Override
public void onRefresh(String featuresJson) {
System.out.println("🔵 RealTimeSSEFeaturesService -> Features have been refreshed");
System.out.println(featuresJson);
}
});

try {
this.initialize();
} catch (FeatureFetchException e) {
this.handleError(e);
}
}

void handleError(FeatureFetchException e) {
e.printStackTrace();

switch (e.getErrorCode()) {
case NO_RESPONSE_ERROR -> {
// Handle NO_RESPONSE_ERROR
}

case SSE_CONNECTION_ERROR -> {
// Handle SSE_CONNECTION_ERROR
}

case CONFIGURATION_ERROR, UNKNOWN -> {
throw new RuntimeException(e);
}
}
}
}

0 comments on commit 735ca66

Please sign in to comment.