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

Java SSE example #43

Merged
merged 7 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
}
}
Loading