All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.devcycle.sdk.server.cloud.api.DevCycleCloudClient Maven / Gradle / Ivy
package com.devcycle.sdk.server.cloud.api;
import com.devcycle.sdk.server.cloud.model.DevCycleCloudOptions;
import com.devcycle.sdk.server.common.api.IDevCycleApi;
import com.devcycle.sdk.server.common.api.IDevCycleClient;
import com.devcycle.sdk.server.common.exception.DevCycleException;
import com.devcycle.sdk.server.common.logging.DevCycleLogger;
import com.devcycle.sdk.server.common.model.*;
import com.devcycle.sdk.server.common.model.Variable.TypeEnum;
import com.devcycle.sdk.server.openfeature.DevCycleProvider;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import dev.openfeature.sdk.FeatureProvider;
import retrofit2.Call;
import retrofit2.Response;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public final class DevCycleCloudClient implements IDevCycleClient {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final IDevCycleApi api;
private final DevCycleCloudOptions dvcOptions;
private final DevCycleProvider openFeatureProvider;
public DevCycleCloudClient(String sdkKey) {
this(sdkKey, DevCycleCloudOptions.builder().build());
}
public DevCycleCloudClient(String sdkKey, DevCycleCloudOptions options) {
if (sdkKey == null || sdkKey.equals("")) {
throw new IllegalArgumentException("Missing environment key! Call initialize with a valid environment key");
}
if (!isValidServerKey(sdkKey)) {
throw new IllegalArgumentException("Invalid environment key provided. Please call initialize with a valid server environment key");
}
if (options.getCustomLogger() != null) {
DevCycleLogger.setCustomLogger(options.getCustomLogger());
}
this.dvcOptions = options;
api = new DevCycleCloudApiClient(sdkKey, options).initialize();
OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
this.openFeatureProvider = new DevCycleProvider(this);
}
/**
* Get all features for user data
*
* @param user (required)
* @return Map>String, Feature<
*/
public Map allFeatures(DevCycleUser user) throws DevCycleException {
validateUser(user);
Call> response = api.getFeatures(user, dvcOptions.getEnableEdgeDB());
return getResponse(response);
}
@Override
public boolean isInitialized() {
// Cloud Bucketing SDKs always count as initialized
return true;
}
/**
* Get variable value by key for user data
*
* @param user (required)
* @param key Feature key (required)
* @param defaultValue Default value to use if the variable could not be fetched
* (required)
* @return Variable value
* @throws IllegalArgumentException If there are any issues with the key or default value provided
*/
public T variableValue(DevCycleUser user, String key, T defaultValue) {
return variable(user, key, defaultValue).getValue();
}
/**
* Get variable by key for user data
*
* @param user (required)
* @param key Variable key (required)
* @param defaultValue Default value to use if the variable could not be fetched (required)
* @return Variable
* @throws IllegalArgumentException If there are any issues with the key or default value provided
*/
@SuppressWarnings("unchecked")
public Variable variable(DevCycleUser user, String key, T defaultValue) {
validateUser(user);
if (key == null || key.equals("")) {
ErrorResponse errorResponse = new ErrorResponse(500, "Missing parameter: key", null);
throw new IllegalArgumentException("Missing parameter: key");
}
if (defaultValue == null) {
ErrorResponse errorResponse = new ErrorResponse(500, "Missing parameter: defaultValue", null);
throw new IllegalArgumentException("Missing parameter: defaultValue");
}
TypeEnum variableType = TypeEnum.fromClass(defaultValue.getClass());
Variable variable;
try {
Call response = api.getVariableByKey(user, key, dvcOptions.getEnableEdgeDB());
variable = getResponseWithRetries(response, 5);
if (variable.getType() != variableType) {
throw new IllegalArgumentException("Variable type mismatch, returning default value");
}
variable.setIsDefaulted(false);
} catch (Exception exception) {
variable = (Variable) Variable.builder()
.key(key)
.type(variableType)
.value(defaultValue)
.defaultValue(defaultValue)
.isDefaulted(true)
.build();
}
return variable;
}
@Override
public void close() {
// no-op - Cloud SDKs don't hold onto any resources
}
/**
* @return the OpenFeature provider for this client.
*/
@Override
public FeatureProvider getOpenFeatureProvider() {
return this.openFeatureProvider;
}
@Override
public String getSDKPlatform() {
return "Cloud";
}
/**
* Get all variables by key for user data
*
* @param user (required)
* @return Map>String, BaseVariable<
*/
public Map allVariables(DevCycleUser user) throws DevCycleException {
validateUser(user);
Call> response = api.getVariables(user, dvcOptions.getEnableEdgeDB());
try {
Map variablesResponse = getResponse(response);
return variablesResponse;
} catch (DevCycleException exception) {
if (exception.getHttpResponseCode() == HttpResponseCode.SERVER_ERROR) {
return new HashMap<>();
}
throw exception;
}
}
/**
* Post events to DevCycle for user
*
* @param user (required)
* @param event (required)
*/
public void track(DevCycleUser user, DevCycleEvent event) throws DevCycleException {
validateUser(user);
if (event == null || event.getType() == null || event.getType().equals("")) {
throw new IllegalArgumentException("Invalid DevCycleEvent");
}
DevCycleUserAndEvents userAndEvents = DevCycleUserAndEvents.builder()
.user(user)
.events(Collections.singletonList(event))
.build();
Call response = api.track(userAndEvents, dvcOptions.getEnableEdgeDB());
getResponseWithRetries(response, 5);
}
private T getResponseWithRetries(Call call, int maxRetries) throws DevCycleException {
// attempt 0 is the initial request, attempt > 0 are all retries
int attempt = 0;
do {
try {
return getResponse(call);
} catch (DevCycleException e) {
attempt++;
// if out of retries or this is an unauthorized error, throw up exception
if (!e.isRetryable() || attempt > maxRetries) {
throw e;
}
try {
// exponential backoff
long waitIntervalMS = (long) (10 * Math.pow(2, attempt));
Thread.sleep(waitIntervalMS);
} catch (InterruptedException ex) {
// no-op
}
// prep the call for a retry
call = call.clone();
}
} while (attempt <= maxRetries);
// getting here should not happen, but is technically possible
ErrorResponse errorResponse = ErrorResponse.builder().build();
errorResponse.setMessage("Out of retry attempts");
throw new DevCycleException(HttpResponseCode.SERVER_ERROR, errorResponse);
}
private T getResponse(Call call) throws DevCycleException {
ErrorResponse errorResponse = ErrorResponse.builder().build();
Response response;
try {
response = call.execute();
} catch (MismatchedInputException mie) {
// got a badly formatted JSON response from the server
errorResponse.setMessage(mie.getMessage());
throw new DevCycleException(HttpResponseCode.NO_CONTENT, errorResponse);
} catch (IOException e) {
// issues reaching the server or reading the response
errorResponse.setMessage(e.getMessage());
throw new DevCycleException(HttpResponseCode.byCode(500), errorResponse);
}
HttpResponseCode httpResponseCode = HttpResponseCode.byCode(response.code());
errorResponse.setMessage("Unknown error");
if (response.errorBody() != null) {
try {
errorResponse = OBJECT_MAPPER.readValue(response.errorBody().string(), ErrorResponse.class);
} catch (IOException e) {
errorResponse.setMessage(e.getMessage());
throw new DevCycleException(httpResponseCode, errorResponse);
}
throw new DevCycleException(httpResponseCode, errorResponse);
}
if (response.body() == null) {
throw new DevCycleException(httpResponseCode, errorResponse);
}
if (response.isSuccessful()) {
return response.body();
} else {
if (httpResponseCode == HttpResponseCode.UNAUTHORIZED) {
errorResponse.setMessage("Invalid SDK Key");
} else if (!response.message().equals("")) {
try {
errorResponse = OBJECT_MAPPER.readValue(response.message(), ErrorResponse.class);
} catch (JsonProcessingException e) {
errorResponse.setMessage(e.getMessage());
throw new DevCycleException(httpResponseCode, errorResponse);
}
}
throw new DevCycleException(httpResponseCode, errorResponse);
}
}
private boolean isValidServerKey(String serverKey) {
return serverKey.startsWith("server") || serverKey.startsWith("dvc_server");
}
private void validateUser(DevCycleUser user) {
if (user == null) {
throw new IllegalArgumentException("DevCycleUser cannot be null");
}
if (user.getUserId().equals("")) {
throw new IllegalArgumentException("userId cannot be empty");
}
}
}