Skip to content

Commit 2e5c146

Browse files
feat: Change fractional custom op from percentage-based to relative weighting. #828 (#833)
Signed-off-by: Simon Schrottner <[email protected]> Co-authored-by: Kavindu Dodanduwa <[email protected]>
1 parent 301f852 commit 2e5c146

File tree

12 files changed

+217
-263
lines changed

12 files changed

+217
-263
lines changed

Diff for: providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Fractional.java

+29-22
Original file line numberDiff line numberDiff line change
@@ -50,37 +50,35 @@ public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationEx
5050
}
5151

5252
final List<FractionProperty> propertyList = new ArrayList<>();
53+
int totalWeight = 0;
5354

54-
double distribution = 0;
5555
try {
5656
for (Object dist : distibutions) {
5757
FractionProperty fractionProperty = new FractionProperty(dist);
5858
propertyList.add(fractionProperty);
59-
distribution += fractionProperty.getPercentage();
59+
totalWeight += fractionProperty.getWeight();
6060
}
6161
} catch (JsonLogicException e) {
6262
log.debug("Error parsing fractional targeting rule", e);
6363
return null;
6464
}
6565

66-
if (distribution != 100) {
67-
log.debug("Fractional properties do not sum to 100");
68-
return null;
69-
}
70-
7166
// find distribution
72-
return distributeValue(bucketBy, propertyList);
67+
return distributeValue(bucketBy, propertyList, totalWeight);
7368
}
7469

75-
private static String distributeValue(final String hashKey, final List<FractionProperty> propertyList)
76-
throws JsonLogicEvaluationException {
70+
private static String distributeValue(
71+
final String hashKey,
72+
final List<FractionProperty> propertyList,
73+
int totalWeight
74+
) throws JsonLogicEvaluationException {
7775
byte[] bytes = hashKey.getBytes(StandardCharsets.UTF_8);
7876
int mmrHash = MurmurHash3.hash32x86(bytes, 0, bytes.length, 0);
79-
int bucket = (int) ((Math.abs(mmrHash) * 1.0f / Integer.MAX_VALUE) * 100);
77+
float bucket = (Math.abs(mmrHash) * 1.0f / Integer.MAX_VALUE) * 100;
8078

81-
int bucketSum = 0;
79+
float bucketSum = 0;
8280
for (FractionProperty p : propertyList) {
83-
bucketSum += p.getPercentage();
81+
bucketSum += p.getPercentage(totalWeight);
8482

8583
if (bucket < bucketSum) {
8684
return p.getVariant();
@@ -95,7 +93,7 @@ private static String distributeValue(final String hashKey, final List<FractionP
9593
@SuppressWarnings({"checkstyle:NoFinalizer"})
9694
private static class FractionProperty {
9795
private final String variant;
98-
private final int percentage;
96+
private final int weight;
9997

10098
protected final void finalize() {
10199
// DO NOT REMOVE, spotbugs: CT_CONSTRUCTOR_THROW
@@ -108,23 +106,32 @@ protected final void finalize() {
108106

109107
final List<?> array = (List) from;
110108

111-
if (array.size() != 2) {
112-
throw new JsonLogicException("Fraction property does not have two elements");
109+
if (array.isEmpty()) {
110+
throw new JsonLogicException("Fraction property needs at least one element");
113111
}
114112

115113
// first must be a string
116114
if (!(array.get(0) instanceof String)) {
117115
throw new JsonLogicException("First element of the fraction property is not a string variant");
118116
}
119117

120-
// second element must be a number
121-
if (!(array.get(1) instanceof Number)) {
122-
throw new JsonLogicException("Second element of the fraction property is not a number");
123-
}
124-
125118
variant = (String) array.get(0);
126-
percentage = ((Number) array.get(1)).intValue();
119+
if (array.size() >= 2) {
120+
// second element must be a number
121+
if (!(array.get(1) instanceof Number)) {
122+
throw new JsonLogicException("Second element of the fraction property is not a number");
123+
}
124+
weight = ((Number) array.get(1)).intValue();
125+
} else {
126+
weight = 1;
127+
}
127128
}
128129

130+
float getPercentage(int totalWeight) {
131+
if (weight == 0) {
132+
return 0;
133+
}
134+
return (float) (weight * 100) / totalWeight;
135+
}
129136
}
130137
}

0 commit comments

Comments
 (0)