Skip to content

Commit 2108ec9

Browse files
Merge branch 'release/v1.63.x' into daniel.mohedano/backport-pr-11577
2 parents 3ab3467 + 04b0920 commit 2108ec9

4 files changed

Lines changed: 312 additions & 139 deletions

File tree

products/feature-flagging/feature-flagging-lib/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ dependencies {
2525

2626
compileOnly(project(":dd-trace-core")) // shading does not work with this one
2727

28+
testImplementation(libs.bundles.junit5)
29+
testImplementation(libs.bundles.mockito)
2830
testImplementation(project(":utils:test-utils"))
2931
testImplementation(project(":dd-java-agent:testing"))
3032
}

products/feature-flagging/feature-flagging-lib/src/main/java/com/datadog/featureflag/RemoteConfigServiceImpl.java

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.datadog.featureflag;
22

33
import com.squareup.moshi.JsonAdapter;
4+
import com.squareup.moshi.JsonDataException;
45
import com.squareup.moshi.JsonReader;
56
import com.squareup.moshi.JsonWriter;
67
import com.squareup.moshi.Moshi;
8+
import com.squareup.moshi.Types;
79
import datadog.communication.ddagent.SharedCommunicationObjects;
810
import datadog.remoteconfig.Capabilities;
911
import datadog.remoteconfig.ConfigurationChangesTypedListener;
@@ -13,11 +15,18 @@
1315
import datadog.remoteconfig.Product;
1416
import datadog.trace.api.Config;
1517
import datadog.trace.api.featureflag.FeatureFlaggingGateway;
18+
import datadog.trace.api.featureflag.ufc.v1.Flag;
1619
import datadog.trace.api.featureflag.ufc.v1.ServerConfiguration;
1720
import java.io.ByteArrayInputStream;
1821
import java.io.IOException;
19-
import java.time.OffsetDateTime;
22+
import java.lang.annotation.Annotation;
23+
import java.lang.reflect.Type;
24+
import java.time.Instant;
25+
import java.time.format.DateTimeFormatter;
2026
import java.util.Date;
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
import java.util.Set;
2130
import javax.annotation.Nonnull;
2231
import javax.annotation.Nullable;
2332
import okio.Okio;
@@ -59,18 +68,74 @@ static class UniversalFlagConfigDeserializer
5968

6069
static final UniversalFlagConfigDeserializer INSTANCE = new UniversalFlagConfigDeserializer();
6170

71+
private static final Moshi MOSHI =
72+
new Moshi.Builder().add(Date.class, new DateAdapter()).add(FlagMapAdapter.FACTORY).build();
6273
private static final JsonAdapter<ServerConfiguration> V1_ADAPTER =
63-
new Moshi.Builder()
64-
.add(Date.class, new DateAdapter())
65-
.build()
66-
.adapter(ServerConfiguration.class);
74+
MOSHI.adapter(ServerConfiguration.class);
6775

6876
@Override
6977
public ServerConfiguration deserialize(final byte[] content) throws IOException {
7078
return V1_ADAPTER.fromJson(Okio.buffer(Okio.source(new ByteArrayInputStream(content))));
7179
}
7280
}
7381

82+
static class FlagMapAdapter extends JsonAdapter<Map<String, Flag>> {
83+
84+
private static final Type FLAGS_TYPE =
85+
Types.newParameterizedType(Map.class, String.class, Flag.class);
86+
87+
static final Factory FACTORY =
88+
new Factory() {
89+
@Nullable
90+
@Override
91+
public JsonAdapter<?> create(
92+
@Nonnull final Type type,
93+
@Nonnull final Set<? extends Annotation> annotations,
94+
@Nonnull final Moshi moshi) {
95+
if (!annotations.isEmpty() || !Types.equals(type, FLAGS_TYPE)) {
96+
return null;
97+
}
98+
return new FlagMapAdapter(moshi.adapter(Flag.class));
99+
}
100+
};
101+
102+
private final JsonAdapter<Flag> flagAdapter;
103+
104+
FlagMapAdapter(final JsonAdapter<Flag> flagAdapter) {
105+
this.flagAdapter = flagAdapter;
106+
}
107+
108+
@Nullable
109+
@Override
110+
public Map<String, Flag> fromJson(@Nonnull final JsonReader reader) throws IOException {
111+
if (reader.peek() == JsonReader.Token.NULL) {
112+
return reader.nextNull();
113+
}
114+
final Map<String, Flag> flags = new HashMap<>();
115+
reader.beginObject();
116+
while (reader.hasNext()) {
117+
final String flagKey = reader.nextName();
118+
final Object rawFlag = reader.readJsonValue();
119+
try {
120+
final Flag flag = flagAdapter.fromJsonValue(rawFlag);
121+
if (flag != null) {
122+
flags.put(flagKey, flag);
123+
}
124+
} catch (JsonDataException | IllegalArgumentException ignored) {
125+
// A malformed flag must not prevent other flags in the same config from evaluating.
126+
}
127+
}
128+
reader.endObject();
129+
return flags;
130+
}
131+
132+
@Override
133+
public void toJson(@Nonnull final JsonWriter writer, @Nullable final Map<String, Flag> value)
134+
throws IOException {
135+
throw new UnsupportedOperationException("Reading only adapter");
136+
}
137+
}
138+
74139
static class DateAdapter extends JsonAdapter<Date> {
75140

76141
@Nullable
@@ -81,10 +146,8 @@ public Date fromJson(@Nonnull final JsonReader reader) throws IOException {
81146
return null;
82147
}
83148
try {
84-
// Use OffsetDateTime which handles variable precision fractional seconds (0-9 digits)
85-
// and UTC offsets (+01:00, -05:00, Z)
86-
final OffsetDateTime odt = OffsetDateTime.parse(date);
87-
return Date.from(odt.toInstant());
149+
final Instant instant = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(date, Instant::from);
150+
return Date.from(instant);
88151
} catch (Exception e) {
89152
// ignore wrongly set dates
90153
return null;

products/feature-flagging/feature-flagging-lib/src/test/groovy/com/datadog/featureflag/RemoteConfigServiceTest.groovy

Lines changed: 0 additions & 130 deletions
This file was deleted.

0 commit comments

Comments
 (0)