Skip to content

Commit 69535ec

Browse files
committed
Merge remote-tracking branch 'upstream/main' into lazy-load-FlagFileParser
2 parents fa4a12b + 8fe5eef commit 69535ec

File tree

20 files changed

+264
-142
lines changed

20 files changed

+264
-142
lines changed

.github/actions/publish/publish.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ else
1414
}
1515
else
1616
echo "RELEASE"
17-
${WORKSPACE_PATH}/gradlew publishToSonatype closeAndReleaseRepository -p ${WORKSPACE_PATH} -Psigning.keyId="${SIGNING_KEY_ID}" -Psigning.password="${SIGNING_KEY_PASSPHRASE}" -Psigning.secretKeyRingFile="${SIGNING_SECRET_KEY_RING_FILE}" -PsonatypeUsername="${SONATYPE_USER_NAME}" -PsonatypePassword="${SONATYPE_PASSWORD}" || {
17+
${WORKSPACE_PATH}/gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -p ${WORKSPACE_PATH} -Psigning.keyId="${SIGNING_KEY_ID}" -Psigning.password="${SIGNING_KEY_PASSPHRASE}" -Psigning.secretKeyRingFile="${SIGNING_SECRET_KEY_RING_FILE}" -PsonatypeUsername="${SONATYPE_USER_NAME}" -PsonatypePassword="${SONATYPE_PASSWORD}" || {
1818
echo "Gradle publish/release failed" >&2
1919
exit 1
2020
}

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"lib/java-server-sdk-otel": "0.1.0",
33
"lib/shared/common": "2.1.1",
44
"lib/shared/internal": "1.3.0",
5-
"lib/sdk/server": "7.5.0"
5+
"lib/sdk/server": "7.6.0"
66
}

lib/sdk/server/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to the LaunchDarkly Java SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
44

5+
## [7.6.0](https://github.com/launchdarkly/java-core/compare/7.5.0...7.6.0) (2024-10-24)
6+
7+
8+
### Features
9+
10+
* Adds support for client side prerequisite events. ([#39](https://github.com/launchdarkly/java-core/issues/39)) ([e9ea4df](https://github.com/launchdarkly/java-core/commit/e9ea4df35df710da6ed38219d3c14350cfea2f94))
11+
512
## [7.5.0](https://github.com/launchdarkly/java-core/compare/v7.4.1...7.5.0) (2024-05-23)
613

714

lib/sdk/server/build.gradle

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ plugins {
2222
id "com.github.johnrengelman.shadow" version "7.1.2"
2323
id "maven-publish"
2424
id "io.github.gradle-nexus.publish-plugin" version "1.3.0" apply false
25-
id "io.codearte.nexus-staging" version "0.21.2"
26-
id "org.ajoberstar.git-publish" version "2.1.3"
2725
id "idea"
2826
}
2927

@@ -546,11 +544,6 @@ idea {
546544
}
547545
}
548546

549-
nexusStaging {
550-
packageGroup = "com.launchdarkly"
551-
numberOfRetries = 40 // we've seen extremely long delays in closing repositories
552-
}
553-
554547
def pomConfig = {
555548
name 'LaunchDarkly SDK for Java'
556549
packaging 'jar'
@@ -623,18 +616,3 @@ task exportDependencies(type: Copy, dependsOn: compileJava) {
623616
into "packaging-test/temp/dependencies-all"
624617
from (configurations.internal.resolvedConfiguration.resolvedArtifacts.collect { it.file })
625618
}
626-
627-
gitPublish {
628-
repoUri = '[email protected]:launchdarkly/java-server-sdk.git'
629-
branch = 'gh-pages'
630-
contents {
631-
from javadoc
632-
}
633-
preserve {
634-
// There's a dummy .circleci/config.yml file on the gh-pages branch so CircleCI won't
635-
// complain when it sees a commit there. The git-publish plugin would delete that file if
636-
// we didn't protect it here.
637-
include '.circleci/config.yml'
638-
}
639-
commitMessage = 'publishing javadocs'
640-
}

lib/sdk/server/contract-tests/service/src/main/java/sdktest/TestService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public class TestService {
3737
"event-sampling",
3838
"inline-context",
3939
"anonymous-redaction",
40-
"evaluation-hooks"
40+
"evaluation-hooks",
41+
"client-prereq-events"
4142
};
4243

4344
static final Gson gson = new GsonBuilder().serializeNulls().create();

lib/sdk/server/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#x-release-please-start-version
2-
version=7.5.0
2+
version=7.6.0
33
#x-release-please-end
44

55
# See https://github.com/gradle/gradle/issues/11308 regarding the following property

lib/sdk/server/src/main/java/com/launchdarkly/sdk/server/EvalResult.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import com.launchdarkly.sdk.LDValue;
77
import com.launchdarkly.sdk.LDValueType;
88

9+
import java.util.ArrayList;
10+
import java.util.Collections;
11+
import java.util.List;
12+
913
import static com.launchdarkly.sdk.EvaluationDetail.NO_VARIATION;
1014

1115
/**
@@ -30,6 +34,9 @@ final class EvalResult {
3034
private final EvaluationDetail<String> asString;
3135
private final boolean forceReasonTracking;
3236

37+
// A list of prerequisites evaluation records evaluated as part of obtaining this result.
38+
private List<PrerequisiteEvalRecord> prerequisiteEvalRecords = new ArrayList<>(0); // 0 initial capacity uses a static instance for performance
39+
3340
/**
3441
* Constructs an instance that wraps the specified EvaluationDetail and also precomputes
3542
* any appropriate type-specific variants (asBoolean, etc.).
@@ -100,6 +107,7 @@ private EvalResult(EvalResult from, EvaluationReason newReason) {
100107
this.asDouble = transformReason(from.asDouble, newReason);
101108
this.asString = transformReason(from.asString, newReason);
102109
this.forceReasonTracking = from.forceReasonTracking;
110+
this.prerequisiteEvalRecords = from.prerequisiteEvalRecords;
103111
}
104112

105113
private EvalResult(EvalResult from, boolean newForceTracking) {
@@ -109,6 +117,17 @@ private EvalResult(EvalResult from, boolean newForceTracking) {
109117
this.asDouble = from.asDouble;
110118
this.asString = from.asString;
111119
this.forceReasonTracking = newForceTracking;
120+
this.prerequisiteEvalRecords = from.prerequisiteEvalRecords;
121+
}
122+
123+
private EvalResult(EvalResult from, List<PrerequisiteEvalRecord> prerequisiteEvalRecords) {
124+
this.anyType = from.anyType;
125+
this.asBoolean = from.asBoolean;
126+
this.asInteger = from.asInteger;
127+
this.asDouble = from.asDouble;
128+
this.asString = from.asString;
129+
this.forceReasonTracking = from.forceReasonTracking;
130+
this.prerequisiteEvalRecords = prerequisiteEvalRecords;
112131
}
113132

114133
/**
@@ -208,6 +227,8 @@ public EvaluationDetail<String> getAsString() {
208227
* @return true if reason tracking is required for this result
209228
*/
210229
public boolean isForceReasonTracking() { return forceReasonTracking; }
230+
231+
public List<PrerequisiteEvalRecord> getPrerequisiteEvalRecords() { return prerequisiteEvalRecords; }
211232

212233
/**
213234
* Returns a transformed copy of this EvalResult with a different evaluation reason.
@@ -226,6 +247,10 @@ public EvalResult withReason(EvaluationReason newReason) {
226247
public EvalResult withForceReasonTracking(boolean newValue) {
227248
return this.forceReasonTracking == newValue ? this : new EvalResult(this, newValue);
228249
}
250+
251+
public EvalResult withPrerequisiteEvalRecords(List<PrerequisiteEvalRecord> newValue) {
252+
return this.prerequisiteEvalRecords == newValue ? this : new EvalResult(this, newValue);
253+
}
229254

230255
@Override
231256
public boolean equals(Object other) {

lib/sdk/server/src/main/java/com/launchdarkly/sdk/server/Evaluator.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ private static class EvaluatorState {
122122
private EvaluationReason.BigSegmentsStatus bigSegmentsStatus = null;
123123
private FeatureFlag originalFlag = null;
124124
private List<String> prerequisiteStack = null;
125+
private List<PrerequisiteEvalRecord> prerequisiteEvalRecords = new ArrayList<>(0); // 0 initial capacity uses a static instance for performance
125126
private List<String> segmentStack = null;
126127
}
127128

@@ -150,17 +151,31 @@ EvalResult evaluate(FeatureFlag flag, LDContext context, @Nonnull EvaluationReco
150151
EvalResult result = evaluateInternal(flag, context, recorder, state);
151152

152153
if (state.bigSegmentsStatus != null) {
153-
return result.withReason(
154+
result = result.withReason(
154155
result.getReason().withBigSegmentsStatus(state.bigSegmentsStatus)
155156
);
156157
}
158+
159+
if (state.prerequisiteEvalRecords != null && !state.prerequisiteEvalRecords.isEmpty()) {
160+
result = result.withPrerequisiteEvalRecords(state.prerequisiteEvalRecords);
161+
}
162+
157163
return result;
158164
} catch (EvaluationException e) {
159165
logger.error("Could not evaluate flag \"{}\": {}", flag.getKey(), e.getMessage());
160166
return EvalResult.error(e.errorKind);
161167
}
162168
}
163169

170+
/**
171+
* Internal evaluation function that may be called multiple times during a flag evaluation.
172+
*
173+
* @param flag that to evaluate
174+
* @param context to use for evaluation
175+
* @param recorder that will be used to record evaluation events
176+
* @param state for mutable values needed during evaluation
177+
* @return the evaluation result
178+
*/
164179
private EvalResult evaluateInternal(FeatureFlag flag, LDContext context, @Nonnull EvaluationRecorder recorder, EvaluatorState state) {
165180
if (!flag.isOn()) {
166181
return EvaluatorHelpers.offResult(flag);
@@ -237,6 +252,7 @@ private EvalResult checkPrerequisites(FeatureFlag flag, LDContext context, @Nonn
237252
if (!prereqFeatureFlag.isOn() || prereqEvalResult.getVariationIndex() != prereq.getVariation()) {
238253
prereqOk = false;
239254
}
255+
state.prerequisiteEvalRecords.add(new PrerequisiteEvalRecord(prereqFeatureFlag, flag, prereqEvalResult));
240256
recorder.recordPrerequisiteEvaluation(prereqFeatureFlag, flag, context, prereqEvalResult);
241257
}
242258
if (!prereqOk) {

lib/sdk/server/src/main/java/com/launchdarkly/sdk/server/FeatureFlagsState.java

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
import com.launchdarkly.sdk.server.interfaces.LDClientInterface;
1313

1414
import java.io.IOException;
15+
import java.util.ArrayList;
1516
import java.util.HashMap;
17+
import java.util.List;
1618
import java.util.Map;
1719
import java.util.Objects;
20+
import java.util.stream.Collectors;
1821

1922
import static com.launchdarkly.sdk.server.JsonHelpers.gsonInstanceWithNullsAllowed;
2023

@@ -48,18 +51,20 @@ static class FlagMetadata {
4851
final boolean trackEvents;
4952
final boolean trackReason;
5053
final Long debugEventsUntilDate;
54+
final List<String> prerequisites;
5155

5256
FlagMetadata(LDValue value, Integer variation, EvaluationReason reason, Integer version,
53-
boolean trackEvents, boolean trackReason, Long debugEventsUntilDate) {
57+
boolean trackEvents, boolean trackReason, Long debugEventsUntilDate, List<String> prerequisites) {
5458
this.value = LDValue.normalize(value);
5559
this.variation = variation;
5660
this.reason = reason;
5761
this.version = version;
5862
this.trackEvents = trackEvents;
5963
this.trackReason = trackReason;
6064
this.debugEventsUntilDate = debugEventsUntilDate;
65+
this.prerequisites = prerequisites;
6166
}
62-
67+
6368
@Override
6469
public boolean equals(Object other) {
6570
if (other instanceof FlagMetadata) {
@@ -70,14 +75,15 @@ public boolean equals(Object other) {
7075
Objects.equals(version, o.version) &&
7176
trackEvents == o.trackEvents &&
7277
trackReason == o.trackReason &&
73-
Objects.equals(debugEventsUntilDate, o.debugEventsUntilDate);
78+
Objects.equals(debugEventsUntilDate, o.debugEventsUntilDate) &&
79+
Objects.equals(prerequisites, o.prerequisites);
7480
}
7581
return false;
7682
}
77-
83+
7884
@Override
7985
public int hashCode() {
80-
return Objects.hash(variation, version, trackEvents, trackReason, debugEventsUntilDate);
86+
return Objects.hash(value, variation, reason, version, trackEvents, trackReason, debugEventsUntilDate, prerequisites);
8187
}
8288
}
8389

@@ -206,7 +212,8 @@ public Builder valid(boolean valid) {
206212
* @param reason the evaluation reason
207213
* @param flagVersion the current flag version
208214
* @param trackEvents true if full event tracking is turned on for this flag
209-
* @param debugEventsUntilDate if set, event debugging is turned until this time (millisecond timestamp)
215+
* @param debugEventsUntilDate if set, event debugging is turned until this time (millisecond timestamp)
216+
* @param prerequisites list of flag keys of the top level prerequisite flags evaluated as part of this evaluation
210217
* @return the builder
211218
*/
212219
public Builder add(
@@ -216,9 +223,10 @@ public Builder add(
216223
EvaluationReason reason,
217224
int flagVersion,
218225
boolean trackEvents,
219-
Long debugEventsUntilDate
226+
Long debugEventsUntilDate,
227+
List<String> prerequisites
220228
) {
221-
return add(flagKey, value, variationIndex, reason, flagVersion, trackEvents, false, debugEventsUntilDate);
229+
return add(flagKey, value, variationIndex, reason, flagVersion, trackEvents, false, debugEventsUntilDate, prerequisites);
222230
}
223231

224232
/**
@@ -236,7 +244,8 @@ public Builder add(
236244
* @param flagVersion the current flag version
237245
* @param trackEvents true if full event tracking is turned on for this flag
238246
* @param trackReason true if evaluation reasons must be included due to experimentation
239-
* @param debugEventsUntilDate if set, event debugging is turned until this time (millisecond timestamp)
247+
* @param debugEventsUntilDate if set, event debugging is turned until this time (millisecond timestamp)
248+
* @param prerequisites list of flag keys of the top level prerequisite flags evaluated as part of this evaluation
240249
* @return the builder
241250
*/
242251
public Builder add(
@@ -247,7 +256,8 @@ public Builder add(
247256
int flagVersion,
248257
boolean trackEvents,
249258
boolean trackReason,
250-
Long debugEventsUntilDate
259+
Long debugEventsUntilDate,
260+
List<String> prerequisites
251261
) {
252262
final boolean flagIsTracked = trackEvents ||
253263
(debugEventsUntilDate != null && debugEventsUntilDate > System.currentTimeMillis());
@@ -259,8 +269,8 @@ public Builder add(
259269
wantDetails ? Integer.valueOf(flagVersion) : null,
260270
trackEvents,
261271
trackReason,
262-
debugEventsUntilDate
263-
);
272+
debugEventsUntilDate,
273+
prerequisites);
264274
flagMetadata.put(flagKey, data);
265275
return this;
266276
}
@@ -274,7 +284,11 @@ Builder addFlag(DataModel.FeatureFlag flag, EvalResult eval) {
274284
flag.getVersion(),
275285
flag.isTrackEvents() || eval.isForceReasonTracking(),
276286
eval.isForceReasonTracking(),
277-
flag.getDebugEventsUntilDate()
287+
flag.getDebugEventsUntilDate(),
288+
eval.getPrerequisiteEvalRecords().stream()
289+
.filter(record -> record.prereqOfFlag.getKey() == flag.getKey()) // only include top level prereqs
290+
.map(record -> record.flag.getKey()) // map from prereq record to prereq key
291+
.collect(Collectors.toList())
278292
);
279293
}
280294

@@ -331,6 +345,14 @@ public void write(JsonWriter out, FeatureFlagsState state) throws IOException {
331345
out.name("debugEventsUntilDate");
332346
out.value(meta.debugEventsUntilDate.longValue());
333347
}
348+
if (meta.prerequisites != null && !meta.prerequisites.isEmpty()) {
349+
out.name("prerequisites");
350+
out.beginArray();
351+
for (String s: meta.prerequisites) {
352+
out.value(s);
353+
}
354+
out.endArray();
355+
}
334356
out.endObject();
335357
}
336358
out.endObject();
@@ -377,8 +399,8 @@ public FeatureFlagsState read(JsonReader in) throws IOException {
377399
m0.version,
378400
m0.trackEvents,
379401
m0.trackReason,
380-
m0.debugEventsUntilDate
381-
);
402+
m0.debugEventsUntilDate,
403+
m0.prerequisites != null ? m0.prerequisites : new ArrayList<>(0));
382404
allFlagMetadata.put(e.getKey(), m1);
383405
}
384406
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.launchdarkly.sdk.server;
2+
3+
public class PrerequisiteEvalRecord {
4+
public final DataModel.FeatureFlag flag;
5+
public final DataModel.FeatureFlag prereqOfFlag;
6+
public final EvalResult result;
7+
8+
public PrerequisiteEvalRecord(DataModel.FeatureFlag flag, DataModel.FeatureFlag prereqOfFlag, EvalResult result) {
9+
this.flag = flag;
10+
this.prereqOfFlag = prereqOfFlag;
11+
this.result = result;
12+
}
13+
}

0 commit comments

Comments
 (0)