Merge pull request 'FeatureFlag実装' (#64) from feature/feature_flag into main
Reviewed-on: #64
This commit is contained in:
commit
f9e8e78d84
|
@ -2,6 +2,7 @@ package one.nem.kidshift.feature.debug;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
@ -9,17 +10,22 @@ import dagger.hilt.EntryPoint;
|
|||
import dagger.hilt.InstallIn;
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
import dagger.hilt.android.components.FragmentComponent;
|
||||
import one.nem.kidshift.utils.FeatureFlag;
|
||||
import one.nem.kidshift.utils.KSLogger;
|
||||
import one.nem.kidshift.utils.models.LogModel;
|
||||
import one.nem.kidshift.utils.models.feature.FeatureFlagItemModel;
|
||||
|
||||
public class DebugCommandProcessor {
|
||||
|
||||
KSLogger ksLogger;
|
||||
FeatureFlag featureFlag;
|
||||
|
||||
public DebugCommandProcessor(
|
||||
KSLogger ksLogger
|
||||
KSLogger ksLogger,
|
||||
FeatureFlag featureFlag
|
||||
) {
|
||||
this.ksLogger = ksLogger;
|
||||
this.featureFlag = featureFlag;
|
||||
}
|
||||
|
||||
public String execute(String command) {
|
||||
|
@ -44,6 +50,8 @@ public class DebugCommandProcessor {
|
|||
return executeEcho(commandArray);
|
||||
case "log":
|
||||
return executeLog(commandArray);
|
||||
case "flag":
|
||||
return executeFlag(commandArray);
|
||||
default:
|
||||
throw new InvalidCommandException();
|
||||
}
|
||||
|
@ -93,6 +101,70 @@ public class DebugCommandProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
private String executeFlag(String[] commandArray) {
|
||||
commandArray = shiftArray(commandArray);
|
||||
switch (commandArray[0]) {
|
||||
case "get":
|
||||
commandArray = shiftArray(commandArray);
|
||||
if (Objects.equals(commandArray[0], "all")) {
|
||||
StringBuilder flagString = new StringBuilder();
|
||||
for (FeatureFlagItemModel featureFlagItemModel : featureFlag.getFeatureFlagMap().values()) {
|
||||
flagString.append(makeFeatureFlagResponse(featureFlagItemModel));
|
||||
flagString.append("\n");
|
||||
}
|
||||
return flagString.toString();
|
||||
}
|
||||
FeatureFlagItemModel featureFlagItemModel = featureFlag.getFeatureFlagMap().get(commandArray[0]);
|
||||
return makeFeatureFlagResponse(featureFlagItemModel);
|
||||
case "set":
|
||||
commandArray = shiftArray(commandArray);
|
||||
try {
|
||||
featureFlag.setOverride(commandArray[0], Boolean.parseBoolean(commandArray[1]));
|
||||
return "Flag set!";
|
||||
} catch (IllegalArgumentException e) {
|
||||
return e.getMessage();
|
||||
} catch (Exception e) {
|
||||
return "Something went wrong! \n" + e.getMessage();
|
||||
}
|
||||
case "reset":
|
||||
commandArray = shiftArray(commandArray);
|
||||
if (Objects.equals(commandArray[0], "all")) {
|
||||
featureFlag.resetAllOverrides();
|
||||
return "All flags reset!";
|
||||
}
|
||||
try {
|
||||
featureFlag.resetOverride(commandArray[0]);
|
||||
return "Flag reset!";
|
||||
} catch (IllegalArgumentException e) {
|
||||
return e.getMessage();
|
||||
} catch (Exception e) {
|
||||
return "Something went wrong! \n" + e.getMessage();
|
||||
}
|
||||
default:
|
||||
// debug
|
||||
if (this.featureFlag == null) {
|
||||
return "Feature Flag is null";
|
||||
} else {
|
||||
return "Feature Flag is not null";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String[] shiftArray(String[] array, int shift) {
|
||||
return Arrays.copyOfRange(array, shift, array.length);
|
||||
}
|
||||
|
||||
private String[] shiftArray(String[] array) {
|
||||
return shiftArray(array, 1);
|
||||
}
|
||||
|
||||
private String makeFeatureFlagResponse(FeatureFlagItemModel featureFlagItemModel) {
|
||||
return "Key: " + featureFlagItemModel.getKey() + "\n" +
|
||||
"\tValue: " + featureFlagItemModel.getValue() + "\n" +
|
||||
"\tDefault Value: " + featureFlagItemModel.getDefaultValue() + "\n" +
|
||||
"\tIs Override Allowed: " + featureFlagItemModel.getIsOverrideAllowed();
|
||||
}
|
||||
|
||||
private String executeEcho(String[] commandArray) {
|
||||
String[] echoArray = Arrays.copyOfRange(commandArray, 1, commandArray.length);
|
||||
return String.join(" ", echoArray);
|
||||
|
|
|
@ -19,6 +19,7 @@ import dagger.hilt.android.AndroidEntryPoint;
|
|||
import one.nem.kidshift.feature.debug.adapter.DebugCommandListItemAdapter;
|
||||
import one.nem.kidshift.feature.debug.adapter.DebugMenuListItemAdapter;
|
||||
import one.nem.kidshift.feature.debug.model.DebugCommandItemModel;
|
||||
import one.nem.kidshift.utils.FeatureFlag;
|
||||
import one.nem.kidshift.utils.KSLogger;
|
||||
|
||||
/**
|
||||
|
@ -32,6 +33,9 @@ public class DebugDebugConsoleFragment extends Fragment {
|
|||
@Inject
|
||||
KSLogger ksLogger;
|
||||
|
||||
@Inject
|
||||
FeatureFlag featureFlag;
|
||||
|
||||
private final List<DebugCommandItemModel> debugCommandItemModels = new ArrayList<>();
|
||||
DebugCommandListItemAdapter debugCommandItemAdapter;
|
||||
|
||||
|
@ -98,7 +102,7 @@ public class DebugDebugConsoleFragment extends Fragment {
|
|||
TextView debugCommandInput = view.findViewById(R.id.debugCommandEditText);
|
||||
view.findViewById(R.id.debugCommandExecuteButton).setOnClickListener(v -> {
|
||||
DebugCommandProcessor debugCommandProcessor = new DebugCommandProcessor(
|
||||
ksLogger);
|
||||
ksLogger, featureFlag);
|
||||
debugCommandItemModels.add(
|
||||
new DebugCommandItemModel(
|
||||
debugCommandInput.getText().toString(),
|
||||
|
|
18
utils/src/main/java/one/nem/kidshift/utils/FeatureFlag.java
Normal file
18
utils/src/main/java/one/nem/kidshift/utils/FeatureFlag.java
Normal file
|
@ -0,0 +1,18 @@
|
|||
package one.nem.kidshift.utils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import one.nem.kidshift.utils.models.feature.FeatureFlagItemModel;
|
||||
|
||||
public interface FeatureFlag {
|
||||
|
||||
public boolean isEnabled(String key);
|
||||
|
||||
public void setOverride(String key, boolean value) throws IllegalArgumentException;
|
||||
|
||||
public void resetOverride(String key) throws IllegalArgumentException;
|
||||
|
||||
public void resetAllOverrides();
|
||||
|
||||
public Map<String, FeatureFlagItemModel> getFeatureFlagMap();
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package one.nem.kidshift.utils.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext;
|
||||
import one.nem.kidshift.utils.models.feature.FeatureFlagItemModel;
|
||||
import one.nem.kidshift.utils.FeatureFlag;
|
||||
|
||||
public class FeatureFlagImpl implements FeatureFlag {
|
||||
|
||||
private final Context applicationContext;
|
||||
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
// ここを書き換えてプロファイルを書き換え
|
||||
private final Profile currentProfile = Profile.DEVELOP;
|
||||
|
||||
enum Profile {
|
||||
DEVELOP("develop"),
|
||||
BETA("beta"),
|
||||
PRODUCTION("production");
|
||||
|
||||
private final String name;
|
||||
|
||||
Profile(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
public FeatureFlagImpl(@ApplicationContext Context applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.sharedPreferences = applicationContext.getSharedPreferences("feat_flg", Context.MODE_PRIVATE);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
initBase();
|
||||
switch (currentProfile) {
|
||||
case DEVELOP:
|
||||
initDevelop();
|
||||
break;
|
||||
case BETA:
|
||||
initBeta();
|
||||
break;
|
||||
case PRODUCTION:
|
||||
break;
|
||||
}
|
||||
restoreOverride();
|
||||
}
|
||||
|
||||
private HashMap<String, FeatureFlagItemModel> featureFlagMap = new HashMap<>();
|
||||
|
||||
// init
|
||||
private void initBase() { // ベース, (= Production)
|
||||
setFlag("isBaseEnabled", true, false);
|
||||
setFlag("isBetaEnabled", false, false);
|
||||
setFlag("isDevelopEnabled", false, false);
|
||||
setFlag("overrideTest", false, true);
|
||||
}
|
||||
|
||||
private void initBeta() { // 上書き
|
||||
setFlag("isBaseEnabled", false, true);
|
||||
setFlag("isBetaEnabled", true, true);
|
||||
setFlag("isDevelopEnabled", false, true);
|
||||
}
|
||||
|
||||
private void initDevelop() { // 上書き
|
||||
setFlag("isBaseEnabled", false, true);
|
||||
setFlag("isBetaEnabled", false, true);
|
||||
setFlag("isDevelopEnabled", true, true);
|
||||
}
|
||||
|
||||
// utils
|
||||
private void setFlag(String key, boolean defaultValue, boolean isOverrideAllowed) {
|
||||
featureFlagMap.put(key, new FeatureFlagItemModel(key, defaultValue, isOverrideAllowed));
|
||||
}
|
||||
|
||||
// Restore override from shared preferences
|
||||
private void restoreOverride() {
|
||||
// 前回起動時からプロファイルが変わっている場合は、オーバーライドをリセット
|
||||
if (sharedPreferences.contains("last_profile") && !sharedPreferences.getString("last_profile", "").equals(currentProfile.getName())) {
|
||||
sharedPreferences.edit().clear().apply();
|
||||
sharedPreferences.edit().putString("last_profile", currentProfile.getName()).apply();
|
||||
return;
|
||||
}
|
||||
sharedPreferences.edit().putString("last_profile", currentProfile.getName()).apply();
|
||||
for (String key : featureFlagMap.keySet()) {
|
||||
Objects.requireNonNull(featureFlagMap.get(key))
|
||||
.setValue(sharedPreferences.getBoolean(key, Objects.requireNonNull(featureFlagMap.get(key)).getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, FeatureFlagItemModel> getFeatureFlagMap() {
|
||||
return featureFlagMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(String key) {
|
||||
return Objects.requireNonNull(featureFlagMap.get(key)).state();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverride(String key, boolean value) throws IllegalArgumentException {
|
||||
// 存在しないキーなら例外
|
||||
if (!featureFlagMap.containsKey(key)) {
|
||||
throw new IllegalArgumentException("Invalid key");
|
||||
}
|
||||
if (!Objects.requireNonNull(featureFlagMap.get(key)).getIsOverrideAllowed()) {
|
||||
throw new IllegalArgumentException("Not allowed to override");
|
||||
}
|
||||
Objects.requireNonNull(featureFlagMap.get(key)).setValue(value);
|
||||
sharedPreferences.edit().putBoolean(key, value).apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetOverride(String key) throws IllegalArgumentException {
|
||||
Objects.requireNonNull(featureFlagMap.get(key)).setValue(Objects.requireNonNull(featureFlagMap.get(key)).getDefaultValue());
|
||||
sharedPreferences.edit().putBoolean(key, Objects.requireNonNull(featureFlagMap.get(key)).getDefaultValue()).apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAllOverrides() {
|
||||
sharedPreferences.edit().clear().apply();
|
||||
init();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package one.nem.kidshift.utils.models.feature;
|
||||
|
||||
public class FeatureFlagItemModel {
|
||||
private String key;
|
||||
private boolean value;
|
||||
private boolean defaultValue;
|
||||
private boolean isOverrideAllowed;
|
||||
|
||||
public FeatureFlagItemModel(String key, boolean value, boolean defaultValue, boolean isOverrideAllowed) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.defaultValue = defaultValue;
|
||||
this.isOverrideAllowed = isOverrideAllowed;
|
||||
}
|
||||
|
||||
public FeatureFlagItemModel(String key, boolean defaultValue, boolean isOverrideAllowed) {
|
||||
this.key = key;
|
||||
this.defaultValue = defaultValue;
|
||||
this.isOverrideAllowed = isOverrideAllowed;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(boolean defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public boolean getIsOverrideAllowed() {
|
||||
return isOverrideAllowed;
|
||||
}
|
||||
|
||||
public void setIsOverrideAllowed(boolean isOverrideAllowed) {
|
||||
this.isOverrideAllowed = isOverrideAllowed;
|
||||
}
|
||||
|
||||
public boolean state() {
|
||||
return isOverrideAllowed ? value : defaultValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package one.nem.kidshift.utils.modules;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.hilt.InstallIn;
|
||||
import dagger.hilt.components.SingletonComponent;
|
||||
import one.nem.kidshift.utils.FeatureFlag;
|
||||
import one.nem.kidshift.utils.impl.FeatureFlagImpl;
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent.class)
|
||||
abstract public class FeatureFlagModule {
|
||||
|
||||
@Binds
|
||||
public abstract FeatureFlag bindFeatureFlag(FeatureFlagImpl featureFlagImpl);
|
||||
}
|
Loading…
Reference in New Issue
Block a user