Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit b52356d

Browse files
LaunchDarklyReleaseBoteli-darklyLaunchDarklyReleaseBottanderson-ldld-repository-standards[bot]
authored
prepare 1.3.0 release (#10)
## [1.3.0] - 2024-03-13 ### Changed: - Redact anonymous attributes within feature events - Always inline contexts for feature events --------- Co-authored-by: Eli Bishop <[email protected]> Co-authored-by: LaunchDarklyReleaseBot <[email protected]> Co-authored-by: Todd Anderson <[email protected]> Co-authored-by: tanderson-ld <[email protected]> Co-authored-by: ld-repository-standards[bot] <113625520+ld-repository-standards[bot]@users.noreply.github.com> Co-authored-by: Kane Parkinson <[email protected]> Co-authored-by: Ryan Lamb <[email protected]> Co-authored-by: Matthew M. Keeler <[email protected]>
1 parent 95af3d3 commit b52356d

File tree

8 files changed

+295
-235
lines changed

8 files changed

+295
-235
lines changed

src/main/java/com/launchdarkly/sdk/internal/events/DefaultEventSender.java

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public final class DefaultEventSender implements EventSender {
3434
* Default value for {@code retryDelayMillis} parameter.
3535
*/
3636
public static final long DEFAULT_RETRY_DELAY_MILLIS = 1000;
37-
37+
3838
/**
3939
* Default value for {@code analyticsRequestPath} parameter, for the server-side SDK.
4040
* The Android SDK should modify this value.
@@ -46,7 +46,7 @@ public final class DefaultEventSender implements EventSender {
4646
* The Android SDK should modify this value.
4747
*/
4848
public static final String DEFAULT_DIAGNOSTIC_REQUEST_PATH = "/diagnostic";
49-
49+
5050
private static final String EVENT_SCHEMA_HEADER = "X-LaunchDarkly-Event-Schema";
5151
private static final String EVENT_SCHEMA_VERSION = "4";
5252
private static final String EVENT_PAYLOAD_ID_HEADER = "X-LaunchDarkly-Payload-ID";
@@ -65,7 +65,7 @@ public final class DefaultEventSender implements EventSender {
6565

6666
/**
6767
* Creates an instance.
68-
*
68+
*
6969
* @param httpProperties the HTTP configuration
7070
* @param analyticsRequestPath the request path for posting analytics events
7171
* @param diagnosticRequestPath the request path for posting diagnostic events
@@ -91,20 +91,20 @@ public DefaultEventSender(
9191
this.baseHeaders = httpProperties.toHeadersBuilder()
9292
.add("Content-Type", "application/json")
9393
.build();
94-
94+
9595
this.analyticsRequestPath = analyticsRequestPath == null ? DEFAULT_ANALYTICS_REQUEST_PATH : analyticsRequestPath;
9696
this.diagnosticRequestPath = diagnosticRequestPath == null ? DEFAULT_DIAGNOSTIC_REQUEST_PATH : diagnosticRequestPath;
97-
97+
9898
this.retryDelayMillis = retryDelayMillis <= 0 ? DEFAULT_RETRY_DELAY_MILLIS : retryDelayMillis;
9999
}
100-
100+
101101
@Override
102102
public void close() throws IOException {
103103
if (shouldCloseHttpClient) {
104104
HttpProperties.shutdownHttpClient(httpClient);
105105
}
106106
}
107-
107+
108108
@Override
109109
public Result sendAnalyticsEvents(byte[] data, int eventCount, URI eventsBaseUri) {
110110
return sendEventData(false, data, eventCount, eventsBaseUri);
@@ -114,33 +114,33 @@ public Result sendAnalyticsEvents(byte[] data, int eventCount, URI eventsBaseUri
114114
public Result sendDiagnosticEvent(byte[] data, URI eventsBaseUri) {
115115
return sendEventData(true, data, 1, eventsBaseUri);
116116
}
117-
117+
118118
private Result sendEventData(boolean isDiagnostic, byte[] data, int eventCount, URI eventsBaseUri) {
119119
if (data == null || data.length == 0) {
120120
// DefaultEventProcessor won't normally pass us an empty payload, but if it does, don't bother sending
121121
return new Result(true, false, null);
122122
}
123-
123+
124124
Headers.Builder headersBuilder = baseHeaders.newBuilder();
125125
String path;
126126
String description;
127-
127+
128128
if (isDiagnostic) {
129129
path = diagnosticRequestPath;
130-
description = "diagnostic event";
130+
description = "diagnostic event";
131131
} else {
132132
path = analyticsRequestPath;
133133
String eventPayloadId = UUID.randomUUID().toString();
134134
headersBuilder.add(EVENT_PAYLOAD_ID_HEADER, eventPayloadId);
135135
headersBuilder.add(EVENT_SCHEMA_HEADER, EVENT_SCHEMA_VERSION);
136136
description = String.format("%d event(s)", eventCount);
137137
}
138-
138+
139139
URI uri = HttpHelpers.concatenateUriPath(eventsBaseUri, path);
140140
Headers headers = headersBuilder.build();
141141
RequestBody body = RequestBody.create(data, JSON_CONTENT_TYPE);
142142
boolean mustShutDown = false;
143-
143+
144144
logger.debug("Posting {} to {} with payload: {}", description, uri,
145145
LogValues.defer(new LazilyPrintedUtf8Data(data)));
146146

@@ -162,15 +162,15 @@ private Result sendEventData(boolean isDiagnostic, byte[] data, int eventCount,
162162
long startTime = System.currentTimeMillis();
163163
String nextActionMessage = attempt == 0 ? "will retry" : "some events were dropped";
164164
String errorContext = "posting " + description;
165-
165+
166166
try (Response response = httpClient.newCall(request).execute()) {
167167
long endTime = System.currentTimeMillis();
168168
logger.debug("{} delivery took {} ms, response status {}", description, endTime - startTime, response.code());
169-
169+
170170
if (response.isSuccessful()) {
171171
return new Result(true, false, parseResponseDate(response));
172172
}
173-
173+
174174
String errorDesc = httpErrorDescription(response.code());
175175
boolean recoverable = checkIfErrorIsRecoverableAndLog(
176176
logger,
@@ -187,10 +187,10 @@ private Result sendEventData(boolean isDiagnostic, byte[] data, int eventCount,
187187
checkIfErrorIsRecoverableAndLog(logger, e.toString(), errorContext, 0, nextActionMessage);
188188
}
189189
}
190-
190+
191191
return new Result(false, mustShutDown, null);
192192
}
193-
193+
194194
private final Date parseResponseDate(Response response) {
195195
String dateStr = response.header("Date");
196196
if (dateStr != null) {
@@ -205,10 +205,10 @@ private final Date parseResponseDate(Response response) {
205205
}
206206
return null;
207207
}
208-
208+
209209
private final class LazilyPrintedUtf8Data implements LogValues.StringProvider {
210210
private final byte[] data;
211-
211+
212212
LazilyPrintedUtf8Data(byte[] data) {
213213
this.data = data;
214214
}

src/main/java/com/launchdarkly/sdk/internal/events/EventContextFormatter.java

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,25 @@ class EventContextFormatter {
3030
this.allAttributesPrivate = allAttributesPrivate;
3131
this.globalPrivateAttributes = globalPrivateAttributes == null ? new AttributeRef[0] : globalPrivateAttributes;
3232
}
33-
34-
public void write(LDContext c, JsonWriter w) throws IOException {
33+
34+
public void write(LDContext c, JsonWriter w, boolean redactAnonymous) throws IOException {
3535
if (c.isMultiple()) {
3636
w.beginObject();
3737
w.name("kind").value("multi");
3838
for (int i = 0; i < c.getIndividualContextCount(); i++) {
3939
LDContext c1 = c.getIndividualContext(i);
4040
w.name(c1.getKind().toString());
41-
writeSingleKind(c1, w, false);
41+
writeSingleKind(c1, w, false, redactAnonymous);
4242
}
4343
w.endObject();
4444
} else {
45-
writeSingleKind(c, w, true);
45+
writeSingleKind(c, w, true, redactAnonymous);
4646
}
4747
}
48-
49-
private void writeSingleKind(LDContext c, JsonWriter w, boolean includeKind) throws IOException {
48+
49+
private void writeSingleKind(LDContext c, JsonWriter w, boolean includeKind, boolean redactAnonymous) throws IOException {
5050
w.beginObject();
51-
51+
5252
// kind, key, and anonymous are never redacted
5353
if (includeKind) {
5454
w.name("kind").value(c.getKind().toString());
@@ -57,21 +57,20 @@ private void writeSingleKind(LDContext c, JsonWriter w, boolean includeKind) thr
5757
if (c.isAnonymous()) {
5858
w.name("anonymous").value(true);
5959
}
60-
60+
6161
List<String> redacted = null;
62-
6362
if (c.getName() != null) {
64-
if (isAttributeEntirelyPrivate(c, "name")) {
63+
if (isAttributeEntirelyPrivate(c, "name", redactAnonymous)) {
6564
redacted = addOrCreate(redacted, "name");
6665
} else {
6766
w.name("name").value(c.getName());
6867
}
6968
}
70-
69+
7170
for (String attrName: c.getCustomAttributeNames()) {
72-
redacted = writeOrRedactAttribute(w, c, attrName, c.getValue(attrName), redacted);
71+
redacted = writeOrRedactAttribute(w, c, attrName, c.getValue(attrName), redacted, redactAnonymous);
7372
}
74-
73+
7574
boolean haveRedacted = redacted != null && !redacted.isEmpty();
7675
if (haveRedacted) {
7776
w.name("_meta").beginObject();
@@ -82,31 +81,36 @@ private void writeSingleKind(LDContext c, JsonWriter w, boolean includeKind) thr
8281
w.endArray();
8382
w.endObject();
8483
}
85-
84+
8685
w.endObject();
8786
}
88-
89-
private boolean isAttributeEntirelyPrivate(LDContext c, String attrName) {
87+
88+
private boolean isAttributeEntirelyPrivate(LDContext c, String attrName, boolean redactAnonymous) {
9089
if (allAttributesPrivate) {
9190
return true;
91+
} else if (redactAnonymous && c.isAnonymous()) {
92+
return true;
9293
}
9394
AttributeRef privateRef = findPrivateRef(c, 1, attrName, null);
9495
return privateRef != null && privateRef.getDepth() == 1;
9596
}
96-
97+
9798
private List<String> writeOrRedactAttribute(
9899
JsonWriter w,
99100
LDContext c,
100101
String attrName,
101102
LDValue value,
102-
List<String> redacted
103+
List<String> redacted,
104+
boolean redactAnonymous
103105
) throws IOException {
104106
if (allAttributesPrivate) {
105107
return addOrCreate(redacted, attrName);
108+
} else if (redactAnonymous && c.isAnonymous()) {
109+
return addOrCreate(redacted, attrName);
106110
}
107111
return writeRedactedValue(w, c, 0, attrName, value, null, redacted);
108112
}
109-
113+
110114
// This method implements the context-aware attribute redaction logic, in which an attribute
111115
// can be 1. written as-is, 2. fully redacted, or 3. (for a JSON object) partially redacted.
112116
// It returns the updated redacted attribute list.

0 commit comments

Comments
 (0)