Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.lightswitch.sdk.client;

import org.lightswitch.sdk.user.LightSwitchUser;

public interface LightSwitchClient {

boolean getBooleanFlag(String key, boolean defaultValue, LightSwitchUser user);

int getIntFlag(String key, int defaultValue, LightSwitchUser user);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about why the flag restricts the number type to Int. It seems like there could be many cases where storing a Long would be necessary.


String getStringFlag(String key, String defaultValue, LightSwitchUser user);

void destroy();

boolean isEnabled(String key, LightSwitchUser user);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.lightswitch.sdk.client;

import org.lightswitch.sdk.exception.LightSwitchCastException;
import org.lightswitch.sdk.model.FeatureFlag;
import org.lightswitch.sdk.user.LightSwitchUser;

import java.util.HashMap;
import java.util.Map;

public class LightSwitchClientImpl implements LightSwitchClient {

// TODO develop FeatureFlagRepository, httpClient, SSEClient
private final Map<String, FeatureFlag> featureFlags; // Mock

public LightSwitchClientImpl() {
// Mock data
this.featureFlags = new HashMap<>();

featureFlags.put("new-dashboard", new FeatureFlag(
"new-dashboard",
false, // default
Map.of(
Map.of("region", "US"), true // active "US" user
)
));

featureFlags.put("beta-feature", new FeatureFlag(
"beta-feature",
false,
Map.of(
Map.of("plan", "premium"), true // active "premium" user
)
));

featureFlags.put("discount", new FeatureFlag(
"discount",
0, // default sale rates : 0%
Map.of(
Map.of("region", "US"), 100, // US user 100% sale
Map.of("region", "EU"), 50 // EU user 50% sale
)
));
}

private FeatureFlag getMockFlag(String key) {
return featureFlags.get(key);
}

private <T> T castValue(String key, Object value, Class<T> expectedType) {
if (!expectedType.isInstance(value)) {
throw new LightSwitchCastException(key, value, expectedType);
}
return expectedType.cast(value);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about move all private methods to the bottom?


@Override
public boolean getBooleanFlag(String key, boolean defaultValue, LightSwitchUser user) {
FeatureFlag flag = getMockFlag(key);
if (flag == null) return defaultValue;

Object value = flag.evaluate(user);
return castValue(key, value, Boolean.class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since 58~62 lines are duplicated to other methods too, it would be better to extract private method for these.

}

@Override
public int getIntFlag(String key, int defaultValue, LightSwitchUser user) {
FeatureFlag flag = getMockFlag(key);
if (flag == null) return defaultValue;

Object value = flag.evaluate(user);
return castValue(key, value, Integer.class);
}

@Override
public String getStringFlag(String key, String defaultValue, LightSwitchUser user) {
FeatureFlag flag = getMockFlag(key);
if (flag == null) return defaultValue;

Object value = flag.evaluate(user);
return castValue(key, value, String.class);
}

@Override
public void destroy() {
featureFlags.clear();
}

@Override
public boolean isEnabled(String key, LightSwitchUser user) {
return getBooleanFlag(key, false, user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.lightswitch.sdk.config;

import org.lightswitch.sdk.exception.LightSwitchConfigException;

public class LightSwitchConfig {

private final String serverUrl;
private final boolean useCache;
private final String sdkKey;
private final int reconnectDelay;
private final int connectionTimeout;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better if the client could customize the read timeout as well.


private LightSwitchConfig(Builder builder) {
this.serverUrl = builder.serverUrl;
this.useCache = builder.useCache;
this.sdkKey = builder.sdkKey;
this.reconnectDelay = builder.reconnectDelay;
this.connectionTimeout = builder.connectionTimeout;
}

public static class Builder {
private String serverUrl;
private boolean useCache = true;
private String sdkKey;
private int reconnectDelay = 5000;
private int connectionTimeout = 10000;

public Builder serverUrl(String serverUrl) throws LightSwitchConfigException {

if (serverUrl == null) {
throw new LightSwitchConfigException("Server URL cannot be null");
}
Comment on lines +27 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using StringUtils to check if the server URL is blank as well?


this.serverUrl = serverUrl;
return this;
}

public Builder useCache(boolean useCache) {
this.useCache = useCache;
return this;
}

public Builder sdkKey(String sdkKey) {
this.sdkKey = sdkKey;
return this;
}

public Builder reconnectDelay(int reconnectDelay) {
this.reconnectDelay = reconnectDelay;
return this;
}

public Builder connectionTimeout(int connectionTimeout) {
if (connectionTimeout < 0) {
//TODO automatic default setting log creation
return this;
}

this.connectionTimeout = connectionTimeout;
return this;
}

public LightSwitchConfig build() {
return new LightSwitchConfig(this);
}
}


public String getServerUrl() {
return serverUrl;
}

public boolean isUseCache() {
return useCache;
}

public String getSdkKey() {
return sdkKey;
}

public int getReconnectDelay() {
return reconnectDelay;
}

public int getConnectionTimeout() {
return connectionTimeout;
}
}