diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4e27f2a..c2e1758 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,6 +13,8 @@ jobs:
steps:
- uses: actions/checkout@v3
+ with:
+ submodules: 'recursive'
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
diff --git a/.gitmodules b/.gitmodules
index bf7d22b..478d3d6 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
-[submodule "src/test/resources/test/server-sdk-specification"]
- path = src/test/resources/test/server-sdk-specification
- url = https://github.com/FeatureProbe/server-sdk-specification.git
+
+[submodule "src/test/resources/test"]
+ path = src/test/resources/test
+ url = git@github.com:FeatureProbe/server-sdk-specification.git
diff --git a/pom.xml b/pom.xml
index c73b298..6290496 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.featureprobe
server-sdk-java
- 2.1.0-SNAPSHOT
+ 2.1.0
server-sdk-java
FeatureProbe Server Side SDK for Java
@@ -218,6 +218,9 @@
**/*.md
+
+ src/test/resources/test/**
+
@@ -297,6 +300,21 @@
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.3.0
+
+
+ analyze-only-in-package
+
+ analyze-only
+
+ package
+
+
+
+
org.apache.maven.plugins
maven-javadoc-plugin
@@ -330,34 +348,6 @@
-
- org.apache.maven.plugins
- maven-assembly-plugin
- 3.4.1
-
- false
-
- jar-with-dependencies
-
-
-
- true
- lib/
- com.featureprobe.sdk.example.FeatureProbeDemo
-
-
-
-
-
- make-assembly
-
- single
-
- package
-
-
-
-
diff --git a/src/main/java/com/featureprobe/sdk/server/FPConfig.java b/src/main/java/com/featureprobe/sdk/server/FPConfig.java
index 840574b..023e8d5 100644
--- a/src/main/java/com/featureprobe/sdk/server/FPConfig.java
+++ b/src/main/java/com/featureprobe/sdk/server/FPConfig.java
@@ -32,6 +32,8 @@ public final class FPConfig {
static final Long DEFAULT_START_WAIT = TimeUnit.SECONDS.toNanos(5);
+ static final Integer DEFAULT_MAX_DEPENDENT_DEEP = 20;
+
protected static final FPConfig DEFAULT = new Builder().build();
final Duration refreshInterval;
@@ -40,6 +42,8 @@ public final class FPConfig {
final URI remoteUri;
+ final Integer prerequisiteDeep;
+
URL synchronizerUrl;
URL eventUrl;
@@ -71,6 +75,8 @@ protected FPConfig(Builder builder) {
this.eventUrl = builder.eventUrl;
this.realtimeUri = builder.realtimeUri;
this.startWait = builder.startWait == null ? DEFAULT_START_WAIT : builder.startWait;
+ this.prerequisiteDeep =
+ builder.prerequisiteDeep == null ? DEFAULT_MAX_DEPENDENT_DEEP : builder.prerequisiteDeep;
}
public static Builder builder() {
@@ -99,6 +105,8 @@ public static class Builder {
private Long startWait;
+ private Integer prerequisiteDeep;
+
public Builder() {
}
@@ -177,6 +185,11 @@ public Builder startWait(Long startWaitTime, TimeUnit unit) {
return this;
}
+ public Builder prerequisiteDeep(Integer prerequisiteDeep) {
+ this.prerequisiteDeep = prerequisiteDeep;
+ return this;
+ }
+
public FPConfig build() {
return new FPConfig(this);
}
diff --git a/src/main/java/com/featureprobe/sdk/server/FeatureProbe.java b/src/main/java/com/featureprobe/sdk/server/FeatureProbe.java
index 682deed..ed5cfcc 100644
--- a/src/main/java/com/featureprobe/sdk/server/FeatureProbe.java
+++ b/src/main/java/com/featureprobe/sdk/server/FeatureProbe.java
@@ -19,6 +19,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.featureprobe.sdk.server.exceptions.PrerequisitesDeepOverflowException;
import com.featureprobe.sdk.server.model.Segment;
import com.featureprobe.sdk.server.model.Toggle;
import com.google.common.annotations.VisibleForTesting;
@@ -58,10 +59,14 @@ public final class FeatureProbe {
@VisibleForTesting
EventProcessor eventProcessor;
+ @VisibleForTesting
+ FPConfig config;
+
@VisibleForTesting
private FeatureProbe(DataRepository dataRepository) {
this.dataRepository = dataRepository;
FPConfig config = FPConfig.DEFAULT;
+ this.config = config;
final FPContext context = new FPContext("test", config);
eventProcessor = config.eventProcessorFactory.createEventProcessor(context);
}
@@ -86,6 +91,7 @@ public FeatureProbe(String serverSDKKey, FPConfig config) {
throw new IllegalArgumentException("serverSDKKey must not be blank");
}
final FPContext context = new FPContext(serverSDKKey, config);
+ this.config = config;
this.eventProcessor = config.eventProcessorFactory.createEventProcessor(context);
this.dataRepository = config.dataRepositoryFactory.createDataRepository(context);
this.synchronizer = config.synchronizerFactory.createSynchronizer(context, dataRepository);
@@ -255,8 +261,10 @@ private T jsonEvaluate(String toggleKey, FPUser user, T defaultValue, Class<
try {
Toggle toggle = dataRepository.getToggle(toggleKey);
Map segments = dataRepository.getAllSegment();
+ Map toggles = dataRepository.getAllToggle();
if (Objects.nonNull(toggle)) {
- EvaluationResult evalResult = toggle.eval(user, segments, defaultValue);
+ EvaluationResult evalResult = toggle.eval(user, toggles, segments, defaultValue,
+ config.prerequisiteDeep);
String value = mapper.writeValueAsString(evalResult.getValue());
eventProcessor.push(buildAccessEvent(toggle, evalResult, user));
return mapper.readValue(value, clazz);
@@ -273,8 +281,10 @@ private T genericEvaluate(String toggleKey, FPUser user, T defaultValue, Cla
try {
Toggle toggle = dataRepository.getToggle(toggleKey);
Map segments = dataRepository.getAllSegment();
+ Map toggles = dataRepository.getAllToggle();
if (Objects.nonNull(toggle)) {
- EvaluationResult evalResult = toggle.eval(user, segments, defaultValue);
+ EvaluationResult evalResult = toggle.eval(user, toggles, segments, defaultValue,
+ config.prerequisiteDeep);
eventProcessor.push(buildAccessEvent(toggle, evalResult, user));
return clazz.cast(evalResult.getValue());
}
@@ -308,6 +318,9 @@ private FPDetail genericEvaluateDetail(String toggleKey, FPUser user, T d
} catch (ClassCastException | JsonProcessingException e) {
logger.error(LOG_CONVERSION_ERROR, toggleKey, e);
detail.setReason(REASON_TYPE_MISMATCH);
+ } catch (PrerequisitesDeepOverflowException e) {
+ logger.error(e.getMessage(), toggleKey, e);
+ detail.setReason(e.getMessage());
} catch (Exception e) {
logger.error(LOG_HANDLE_ERROR, toggleKey, e);
detail.setReason(REASON_HANDLE_ERROR);
@@ -323,8 +336,10 @@ private FPDetail getEvaluateDetail(String toggleKey, FPUser user, T defau
if (this.dataRepository.initialized()) {
Toggle toggle = dataRepository.getToggle(toggleKey);
Map segments = dataRepository.getAllSegment();
+ Map toggles = dataRepository.getAllToggle();
if (Objects.nonNull(toggle)) {
- EvaluationResult evalResult = toggle.eval(user, segments, defaultValue);
+ EvaluationResult evalResult = toggle.eval(user, toggles, segments, defaultValue,
+ config.prerequisiteDeep);
if (isJson) {
String res = mapper.writeValueAsString(evalResult.getValue());
detail.setValue(mapper.readValue(res, clazz));
diff --git a/src/main/java/com/featureprobe/sdk/server/exceptions/PrerequisitesDeepOverflowException.java b/src/main/java/com/featureprobe/sdk/server/exceptions/PrerequisitesDeepOverflowException.java
new file mode 100644
index 0000000..ec6cd4a
--- /dev/null
+++ b/src/main/java/com/featureprobe/sdk/server/exceptions/PrerequisitesDeepOverflowException.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.featureprobe.sdk.server.exceptions;
+
+public class PrerequisitesDeepOverflowException extends RuntimeException {
+
+ public PrerequisitesDeepOverflowException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/com/featureprobe/sdk/server/model/Split.java b/src/main/java/com/featureprobe/sdk/server/model/Split.java
index 7c1b791..65c4225 100644
--- a/src/main/java/com/featureprobe/sdk/server/model/Split.java
+++ b/src/main/java/com/featureprobe/sdk/server/model/Split.java
@@ -96,7 +96,10 @@ private int hash(String hashKey, String hashSalt, int bucketSize) {
}
private String getHashSalt(String toggleKey) {
- return StringUtils.defaultString(salt, toggleKey);
+ if (StringUtils.isNotBlank(salt)) {
+ return salt;
+ }
+ return toggleKey;
}
public List>> getDistribution() {
diff --git a/src/main/java/com/featureprobe/sdk/server/model/Toggle.java b/src/main/java/com/featureprobe/sdk/server/model/Toggle.java
index a656b0a..a740f37 100644
--- a/src/main/java/com/featureprobe/sdk/server/model/Toggle.java
+++ b/src/main/java/com/featureprobe/sdk/server/model/Toggle.java
@@ -20,9 +20,11 @@
import com.featureprobe.sdk.server.EvaluationResult;
import com.featureprobe.sdk.server.FPUser;
import com.featureprobe.sdk.server.HitResult;
+import com.featureprobe.sdk.server.exceptions.PrerequisitesDeepOverflowException;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
public final class Toggle {
@@ -49,13 +51,23 @@ public final class Toggle {
private Boolean forClient;
- public EvaluationResult eval(FPUser user, Map segments, Object defaultValue) {
+ public EvaluationResult eval(FPUser user, Map toggles, Map segments,
+ Object defaultValue, int deep) {
+
String warning = "";
if (!enabled) {
return createDisabledResult(user, this.key, defaultValue);
}
+ if (deep <= 0) {
+ throw new PrerequisitesDeepOverflowException("prerequisite deep overflow");
+ }
+
+ if (!prerequisite(user, toggles, segments, deep)) {
+ return createDefaultResult(user, key, defaultValue, warning);
+ }
+
if (rules != null && rules.size() > 0) {
for (int i = 0; i < rules.size(); i++) {
Rule rule = rules.get(i);
@@ -84,6 +96,27 @@ private EvaluationResult createDefaultResult(FPUser user, String toggleKey, Obje
return defaultResult;
}
+ private boolean prerequisite(FPUser user, Map toggles, Map segments, int deep) {
+ if (Objects.isNull(prerequisites) || prerequisites.isEmpty()) {
+ return true;
+ }
+ try {
+ for (Prerequisite prerequisite : prerequisites) {
+ Toggle toggle = toggles.get(prerequisite.getKey());
+ if (Objects.isNull(toggle))
+ return false;
+ EvaluationResult eval = toggle.eval(user, toggles, segments, null, deep - 1);
+ if (Objects.isNull(eval.getValue()))
+ return false;
+ if (!eval.getValue().equals(prerequisite.getValue()))
+ return false;
+ }
+ } catch (PrerequisitesDeepOverflowException e) {
+ throw e;
+ }
+ return true;
+ }
+
private EvaluationResult hitValue(HitResult hitResult, Object defaultValue, Optional ruleIndex) {
EvaluationResult res = new EvaluationResult(defaultValue, ruleIndex, hitResult.getIndex(),
this.version, hitResult.getReason().orElse(""));
diff --git a/src/test/groovy/com/featureprobe/sdk/server/FeatureProbeSpec.groovy b/src/test/groovy/com/featureprobe/sdk/server/FeatureProbeSpec.groovy
index 65ec0f9..ea854ea 100644
--- a/src/test/groovy/com/featureprobe/sdk/server/FeatureProbeSpec.groovy
+++ b/src/test/groovy/com/featureprobe/sdk/server/FeatureProbeSpec.groovy
@@ -10,7 +10,7 @@ import java.nio.charset.Charset
class FeatureProbeSpec extends Specification {
- def test_data_local = "test/server-sdk-specification/spec/toggle_simple_spec.json";
+ def test_data_local = "test/spec/toggle_simple_spec.json";
def FeatureProbe featureProbe
def ObjectMapper mapper
def JsonNode testCase
diff --git a/src/test/groovy/com/featureprobe/sdk/server/SplitSpec.groovy b/src/test/groovy/com/featureprobe/sdk/server/SplitSpec.groovy
index a5dce9b..979d475 100644
--- a/src/test/groovy/com/featureprobe/sdk/server/SplitSpec.groovy
+++ b/src/test/groovy/com/featureprobe/sdk/server/SplitSpec.groovy
@@ -27,6 +27,13 @@ class SplitSpec extends Specification {
}
}
+ def "Get hash key" () {
+ when:
+ def hash = split.hash("13", "tutorial_rollout", 10000)
+ then:
+ hash == 9558
+ }
+
def "User not has key"() {
when:
user = new FPUser()
diff --git a/src/test/resources/test b/src/test/resources/test
new file mode 160000
index 0000000..4c31394
--- /dev/null
+++ b/src/test/resources/test
@@ -0,0 +1 @@
+Subproject commit 4c31394c23852033e43396543293da0a139d607b
diff --git a/src/test/resources/test/server-sdk-specification/spec/toggle_simple_spec.json b/src/test/resources/test/server-sdk-specification/spec/toggle_simple_spec.json
deleted file mode 100644
index be9d8a9..0000000
--- a/src/test/resources/test/server-sdk-specification/spec/toggle_simple_spec.json
+++ /dev/null
@@ -1,1038 +0,0 @@
-{
- "tests": [
- {
- "scenario": "Get none exist toggle scenarios",
- "fixture": {
- "segments": {},
- "toggles": {}
- },
- "cases": [
- {
- "name": "test bool_value.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "city",
- "value": "1"
- },
- {
- "key": "email",
- "value": "test@a.com"
- }
- ]
- },
- "function": {
- "name": "bool_value",
- "toggle": "none_exit_toggle",
- "default": true
- },
- "expectResult": {
- "value": true
- }
- },
- {
- "name": "test string_value.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "city",
- "value": "1"
- },
- {
- "key": "email",
- "value": "test@a.com"
- }
- ]
- },
- "function": {
- "name": "string_value",
- "toggle": "none_exit_toggle",
- "default": ""
- },
- "expectResult": {
- "value": ""
- }
- },
- {
- "name": "test number_value.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "city",
- "value": "1"
- },
- {
- "key": "email",
- "value": "test@a.com"
- }
- ]
- },
- "function": {
- "name": "number_value",
- "toggle": "none_exit_toggle",
- "default": 0
- },
- "expectResult": {
- "value": 0
- }
- },
- {
- "name": "test json_value.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "city",
- "value": "1"
- },
- {
- "key": "email",
- "value": "test@a.com"
- }
- ]
- },
- "function": {
- "name": "json_value",
- "toggle": "none_exit_toggle",
- "default": {}
- },
- "expectResult": {
- "value": {}
- }
- },
- {
- "name": "test bool_detail",
- "user": {
- "key": "user id",
- "customValues": [
- ]
- },
- "function": {
- "name": "bool_detail",
- "toggle": "none_exit_toggle",
- "default": false
- },
- "expectResult": {
- "value": false,
- "reason": "not exist",
- "ruleIndex": 0,
- "conditionIndex": 0
- }
- },
- {
- "name": "test number_detail",
- "user": {
- "key": "user id",
- "customValues": [
- ]
- },
- "function": {
- "name": "number_detail",
- "toggle": "none_exit_toggle",
- "default": 0
- },
- "expectResult": {
- "value": 0,
- "reason": "not exist",
- "ruleIndex": 0,
- "conditionIndex": 0
- }
- },
- {
- "name": "test json_detail",
- "user": {
- "key": "user id",
- "customValues": [
- ]
- },
- "function": {
- "name": "json_detail",
- "toggle": "none_exit_toggle",
- "default": {}
- },
- "expectResult": {
- "value": {},
- "reason": "not exist",
- "ruleIndex": 0,
- "conditionIndex": 0
- }
- },
- {
- "name": "test string_detail",
- "user": {
- "key": "user id",
- "customValues": [
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "none_exit_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "",
- "reason": "not exist",
- "ruleIndex": 0,
- "conditionIndex": 0
- }
- }
- ]
- },
- {
- "scenario": "Get disabled toggle scenarios",
- "fixture": {
- "segments": {},
- "toggles": {
- "string_toggle": {
- "key": "string_toggle",
- "enabled": false,
- "forClient": true,
- "version": 11,
- "disabledServe": {
- "select": 4
- },
- "defaultServe": {
- "select": 3
- },
- "rules": [
- {
- "serve": {
- "select": 0
- },
- "conditions": [
- {
- "type": "string",
- "subject": "city",
- "predicate": "is one of",
- "objects": [
- "1",
- "2",
- "3"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 1
- },
- "conditions": [
- {
- "type": "string",
- "subject": "email",
- "predicate": "is one of",
- "objects": [
- "test@a.com"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 2
- },
- "conditions": [
- {
- "type": "string",
- "subject": "city",
- "predicate": "is one of",
- "objects": [
- "10"
- ]
- },
- {
- "type": "string",
- "subject": "email",
- "predicate": "is one of",
- "objects": [
- "another@a.com"
- ]
- }
- ]
- }
- ],
- "variations": [
- "1",
- "2",
- "3",
- "4",
- "5"
- ]
- }
- }
- },
- "cases": [
- {
- "name": "test string_value.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "city",
- "value": "1"
- },
- {
- "key": "email",
- "value": "test@a.com"
- }
- ]
- },
- "function": {
- "name": "string_value",
- "toggle": "string_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "5"
- }
- },
- {
- "name": "test string_detail",
- "user": {
- "key": "user id",
- "customValues": [
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": "6"
- },
- "expectResult": {
- "value": "5",
- "reason": "disabled",
- "no_rule_index": true,
- "version": 11
- }
- }
- ]
- },
- {
- "scenario": "Toggle split rollout scenarios",
- "fixture": {
- "segments": {},
- "toggles": {
- "name_toggle": {
- "key": "name_toggle",
- "enabled": true,
- "forClient": true,
- "version": 11,
- "disabledServe": {
- "select": 0
- },
- "defaultServe": {
- "split": {
- "distribution": [
- [
- [
- 0,
- 2647
- ]
- ],
- [
- [
- 2647,
- 2648
- ]
- ],
- [
- [
- 2648,
- 10000
- ]
- ]
- ],
- "bucketBy": "name",
- "salt": "salt"
- }
- },
- "rules": [
- ],
- "variations": [
- "1",
- "2",
- "3"
- ]
- },
- "key_toggle": {
- "key": "key_toggle",
- "enabled": true,
- "forClient": true,
- "version": 11,
- "disabledServe": {
- "select": 0
- },
- "defaultServe": {
- "split": {
- "distribution": [
- [
- [
- 0,
- 2647
- ]
- ],
- [
- [
- 2647,
- 2648
- ]
- ],
- [
- [
- 2648,
- 10000
- ]
- ]
- ],
- "salt": "salt"
- }
- },
- "rules": [
- ],
- "variations": [
- "1",
- "2",
- "3"
- ]
- }
- }
- },
- "cases": [
- {
- "name": "test bucket by name in exact bucket.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "name",
- "value": "key"
- }
- ]
- },
- "function": {
- "name": "string_value",
- "toggle": "name_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "2"
- }
- },
- {
- "name": "test bucket by a none exist attribute.",
- "user": {
- "key": "user id",
- "customValues": [
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "name_toggle",
- "default": ""
- },
- "expectResult": {
- "value": ""
- }
- },
- {
- "name": "test bucket by key in exact bucket",
- "user": {
- "key": "key",
- "customValues": [
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "key_toggle",
- "default": "6"
- },
- "expectResult": {
- "value": "2"
- }
- }
- ]
- },
- {
- "scenario": "Toggle multi-condition scenarios",
- "fixture": {
- "segments": {},
- "toggles": {
- "mc_toggle": {
- "key": "mc_toggle",
- "enabled": true,
- "forClient": false,
- "version": 11,
- "disabledServe": {
- "select": 0
- },
- "defaultServe": {
- "select": 0
- },
- "rules": [
- {
- "serve": {
- "select": 1
- },
- "conditions": [
- {
- "type": "string",
- "subject": "city",
- "predicate": "is one of",
- "objects": [
- "1",
- "2",
- "3"
- ]
- },
- {
- "type": "string",
- "subject": "email",
- "predicate": "is one of",
- "objects": [
- "test@a.com"
- ]
- }
- ]
- }
- ],
- "variations": [
- "default",
- "rule 1"
- ]
- }
- }
- },
- "cases": [
- {
- "name": "test first single condition not match.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "city",
- "value": "1"
- }
- ]
- },
- "function": {
- "name": "string_value",
- "toggle": "mc_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "default"
- }
- },
- {
- "name": "single second condition not match",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "email",
- "value": "test@a.com"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "mc_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "default"
- }
- },
- {
- "name": "test both condition match.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "email",
- "value": "test@a.com"
- },
- {
- "key": "city",
- "value": "1"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "mc_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "rule 1"
- }
- }
- ]
- },
- {
- "scenario": "String Type predicate scenarios",
- "fixture": {
- "segments": {},
- "toggles": {
- "string_toggle": {
- "key": "string_toggle",
- "enabled": true,
- "forClient": false,
- "version": 11,
- "disabledServe": {
- "select": 0
- },
- "defaultServe": {
- "select": 0
- },
- "rules": [
- {
- "serve": {
- "select": 1
- },
- "conditions": [
- {
- "type": "string",
- "subject": "city",
- "predicate": "is one of",
- "objects": [
- "1",
- "2"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 2
- },
- "conditions": [
- {
- "type": "string",
- "subject": "name",
- "predicate": "ends with",
- "objects": [
- "First",
- "Bob"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 3
- },
- "conditions": [
- {
- "type": "string",
- "subject": "name",
- "predicate": "starts with",
- "objects": [
- "John"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 4
- },
- "conditions": [
- {
- "type": "string",
- "subject": "name",
- "predicate": "contains",
- "objects": [
- "Middle"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 5
- },
- "conditions": [
- {
- "type": "string",
- "subject": "name",
- "predicate": "matches regex",
- "objects": [
- "\\d\\d\\d"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 6
- },
- "conditions": [
- {
- "type": "string",
- "subject": "name",
- "predicate": "is one of",
- "objects": [
- "is_not_any_of"
- ]
- },
- {
- "type": "string",
- "subject": "city",
- "predicate": "is not any of",
- "objects": [
- "1",
- "2",
- "3"
- ]
- }
- ]
- }
- ],
- "variations": [
- "error",
- "is one of",
- "ends with",
- "starts with",
- "contains",
- "matches regex",
- "is not any of"
- ]
- }
- }
- },
- "cases": [
- {
- "name": "test is one of",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "city",
- "value": "2"
- }
- ]
- },
- "function": {
- "name": "string_value",
- "toggle": "string_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "is one of"
- }
- },
- {
- "name": "test ends with",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "name",
- "value": "Martin Bob"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": "6"
- },
- "expectResult": {
- "value": "ends with"
- }
- },
- {
- "name": "test starts with",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "name",
- "value": "John Smith"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": "6"
- },
- "expectResult": {
- "value": "starts with"
- }
- },
- {
- "name": "test contains",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "name",
- "value": "A Middle B"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": "6"
- },
- "expectResult": {
- "value": "contains"
- }
- },
- {
- "name": "test regex",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "name",
- "value": "some 666 word"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": "6"
- },
- "expectResult": {
- "value": "matches regex"
- }
- },
- {
- "name": "test is not any of.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "name",
- "value": "is_not_any_of"
- },
- {
- "key": "city",
- "value": "10"
- }
- ]
- },
- "function": {
- "name": "string_value",
- "toggle": "string_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "is not any of"
- }
- }
- ]
- },
- {
- "scenario": "String value get detail scenarios",
- "fixture": {
- "segments": {},
- "toggles": {
- "string_toggle": {
- "key": "string_toggle",
- "enabled": true,
- "forClient": true,
- "version": 1,
- "disabledServe": {
- "select": 1
- },
- "defaultServe": {
- "select": 3
- },
- "rules": [
- {
- "serve": {
- "select": 0
- },
- "conditions": [
- {
- "type": "string",
- "subject": "city",
- "predicate": "is one of",
- "objects": [
- "1",
- "2",
- "3"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 1
- },
- "conditions": [
- {
- "type": "string",
- "subject": "email",
- "predicate": "is one of",
- "objects": [
- "test@a.com"
- ]
- }
- ]
- },
- {
- "serve": {
- "select": 2
- },
- "conditions": [
- {
- "type": "string",
- "subject": "city",
- "predicate": "is one of",
- "objects": [
- "10"
- ]
- },
- {
- "type": "string",
- "subject": "email",
- "predicate": "is one of",
- "objects": [
- "another@a.com"
- ]
- }
- ]
- }
- ],
- "variations": [
- "1",
- "2",
- "3",
- "4"
- ]
- }
- }
- },
- "cases": [
- {
- "name": "test string_detail match third rule.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "email",
- "value": "another@a.com"
- },
- {
- "key": "city",
- "value": "10"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "3",
- "rule_index": 2
- }
- },
- {
- "name": "test string_detail match first rule.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "email",
- "value": "test@a.com"
- },
- {
- "key": "city",
- "value": "2"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "1",
- "rule_index": 0
- }
- },
- {
- "name": "test string_detail missing attribute in second rule.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "city",
- "value": "12"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "4",
- "no_rule_index": true
- }
- },
- {
- "name": "test string_detail match no rule, use default serve.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "email",
- "value": "notest@a.com"
- }
- ]
- },
- "function": {
- "name": "string_detail",
- "toggle": "string_toggle",
- "default": ""
- },
- "expectResult": {
- "value": "4",
- "no_rule_index": true,
- "reason": "default"
- }
- },
- {
- "name": "test string variation get with bool_detail.",
- "user": {
- "key": "user id",
- "customValues": [
- {
- "key": "email",
- "value": "test@a.com"
- },
- {
- "key": "city",
- "value": "2"
- }
- ]
- },
- "function": {
- "name": "bool_detail",
- "toggle": "string_toggle",
- "default": true
- },
- "expectResult": {
- "value": true,
- "reason": "type mismatch"
- }
- }
- ]
- }
- ]
-}
\ No newline at end of file