diff --git a/.circleci/config.yml b/.circleci/config.yml index 37d487759..3f2bc8df9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -80,10 +80,11 @@ jobs: ./gradlew jacocoTestReport mkdir -p coverage/ cp -r build/reports/jacoco/test/* ./coverage - - run: mkdir -p ~/junit/ - run: name: Save test results - command: find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; when: always - run: make build-contract-tests @@ -126,7 +127,8 @@ jobs: name: save test results command: | mkdir .\junit - cp build/test-results/test/*.xml junit + cp build/test-results/test/*.xml junit -ErrorAction SilentlyContinue + when: always - store_test_results: path: .\junit - store_artifacts: diff --git a/contract-tests/service/src/main/java/sdktest/Representations.java b/contract-tests/service/src/main/java/sdktest/Representations.java index 60cf2b2a4..c30e538d9 100644 --- a/contract-tests/service/src/main/java/sdktest/Representations.java +++ b/contract-tests/service/src/main/java/sdktest/Representations.java @@ -25,6 +25,7 @@ public static class SdkConfigParams { SdkConfigStreamParams streaming; SdkConfigEventParams events; SdkConfigBigSegmentsParams bigSegments; + SdkConfigTagParams tags; } public static class SdkConfigStreamParams { @@ -49,6 +50,11 @@ public static class SdkConfigBigSegmentsParams { Long statusPollIntervalMs; Long staleAfterMs; } + + public static class SdkConfigTagParams { + String applicationId; + String applicationVersion; + } public static class CommandParams { String command; diff --git a/contract-tests/service/src/main/java/sdktest/SdkClientEntity.java b/contract-tests/service/src/main/java/sdktest/SdkClientEntity.java index a3f43fd88..9bc473ad1 100644 --- a/contract-tests/service/src/main/java/sdktest/SdkClientEntity.java +++ b/contract-tests/service/src/main/java/sdktest/SdkClientEntity.java @@ -8,6 +8,7 @@ import com.launchdarkly.sdk.server.FlagsStateOption; import com.launchdarkly.sdk.server.LDClient; import com.launchdarkly.sdk.server.LDConfig; +import com.launchdarkly.sdk.server.integrations.ApplicationInfoBuilder; import com.launchdarkly.sdk.server.integrations.BigSegmentsConfigurationBuilder; import com.launchdarkly.sdk.server.integrations.EventProcessorBuilder; import com.launchdarkly.sdk.server.integrations.StreamingDataSourceBuilder; @@ -235,6 +236,17 @@ private LDConfig buildSdkConfig(SdkConfigParams params) { } builder.bigSegments(bsb); } + + if (params.tags != null) { + ApplicationInfoBuilder ab = Components.applicationInfo(); + if (params.tags.applicationId != null) { + ab.applicationId(params.tags.applicationId); + } + if (params.tags.applicationVersion != null) { + ab.applicationVersion(params.tags.applicationVersion); + } + builder.applicationInfo(ab); + } return builder.build(); } diff --git a/contract-tests/service/src/main/java/sdktest/TestService.java b/contract-tests/service/src/main/java/sdktest/TestService.java index af6647879..68eed1456 100644 --- a/contract-tests/service/src/main/java/sdktest/TestService.java +++ b/contract-tests/service/src/main/java/sdktest/TestService.java @@ -26,7 +26,8 @@ public class TestService { "all-flags-client-side-only", "all-flags-details-only-for-tracked-flags", "all-flags-with-reasons", - "big-segments" + "big-segments", + "tags" }; static final Gson gson = new GsonBuilder().serializeNulls().create(); diff --git a/src/main/java/com/launchdarkly/sdk/server/ClientContextImpl.java b/src/main/java/com/launchdarkly/sdk/server/ClientContextImpl.java index 1cdf659fe..8004b36f9 100644 --- a/src/main/java/com/launchdarkly/sdk/server/ClientContextImpl.java +++ b/src/main/java/com/launchdarkly/sdk/server/ClientContextImpl.java @@ -1,5 +1,6 @@ package com.launchdarkly.sdk.server; +import com.launchdarkly.sdk.server.interfaces.ApplicationInfo; import com.launchdarkly.sdk.server.interfaces.BasicConfiguration; import com.launchdarkly.sdk.server.interfaces.ClientContext; import com.launchdarkly.sdk.server.interfaces.HttpConfiguration; @@ -51,7 +52,8 @@ private ClientContextImpl( ScheduledExecutorService sharedExecutor, DiagnosticAccumulator diagnosticAccumulator ) { - this.basicConfiguration = new BasicConfiguration(sdkKey, configuration.offline, configuration.threadPriority); + ApplicationInfo applicationInfo = configuration.applicationInfoBuilder.createApplicationInfo(); + this.basicConfiguration = new BasicConfiguration(sdkKey, configuration.offline, configuration.threadPriority, applicationInfo); this.httpConfiguration = configuration.httpConfigFactory.createHttpConfiguration(basicConfiguration); this.loggingConfiguration = configuration.loggingConfigFactory.createLoggingConfiguration(basicConfiguration); diff --git a/src/main/java/com/launchdarkly/sdk/server/Components.java b/src/main/java/com/launchdarkly/sdk/server/Components.java index bede125f5..171406f39 100644 --- a/src/main/java/com/launchdarkly/sdk/server/Components.java +++ b/src/main/java/com/launchdarkly/sdk/server/Components.java @@ -11,6 +11,7 @@ import com.launchdarkly.sdk.server.ComponentsImpl.PersistentDataStoreBuilderImpl; import com.launchdarkly.sdk.server.ComponentsImpl.PollingDataSourceBuilderImpl; import com.launchdarkly.sdk.server.ComponentsImpl.StreamingDataSourceBuilderImpl; +import com.launchdarkly.sdk.server.integrations.ApplicationInfoBuilder; import com.launchdarkly.sdk.server.integrations.BigSegmentsConfigurationBuilder; import com.launchdarkly.sdk.server.integrations.EventProcessorBuilder; import com.launchdarkly.sdk.server.integrations.HttpConfigurationBuilder; @@ -320,4 +321,27 @@ public static HttpAuthentication httpBasicAuthentication(String username, String public static LoggingConfigurationBuilder logging() { return new LoggingConfigurationBuilderImpl(); } + + /** + * Returns a configuration builder for the SDK's application metadata. + *

+ * Passing this to {@link LDConfig.Builder#applicationInfo(com.launchdarkly.sdk.server.integrations.ApplicationInfoBuilder)}, + * after setting any desired properties on the builder, applies this configuration to the SDK. + *


+   *     LDConfig config = new LDConfig.Builder()
+   *         .applicationInfo(
+   *             Components.applicationInfo()
+   *                 .applicationId("authentication-service")
+   *                 .applicationVersion("1.0.0")
+   *         )
+   *         .build();
+   * 
+ * + * @return a builder object + * @since 5.8.0 + * @see LDConfig.Builder#applicationInfo(com.launchdarkly.sdk.server.integrations.ApplicationInfoBuilder) + */ + public static ApplicationInfoBuilder applicationInfo() { + return new ApplicationInfoBuilder(); + } } diff --git a/src/main/java/com/launchdarkly/sdk/server/ComponentsImpl.java b/src/main/java/com/launchdarkly/sdk/server/ComponentsImpl.java index 33ddaa4fb..86aab22c2 100644 --- a/src/main/java/com/launchdarkly/sdk/server/ComponentsImpl.java +++ b/src/main/java/com/launchdarkly/sdk/server/ComponentsImpl.java @@ -253,6 +253,12 @@ public HttpConfiguration createHttpConfiguration(BasicConfiguration basicConfigu ImmutableMap.Builder headers = ImmutableMap.builder(); headers.put("Authorization", basicConfiguration.getSdkKey()); headers.put("User-Agent", "JavaClient/" + Version.SDK_VERSION); + if (basicConfiguration.getApplicationInfo() != null) { + String tagHeader = Util.applicationTagHeader(basicConfiguration.getApplicationInfo()); + if (!tagHeader.isEmpty()) { + headers.put("X-LaunchDarkly-Tags", tagHeader); + } + } if (wrapperName != null) { String wrapperId = wrapperVersion == null ? wrapperName : (wrapperName + "/" + wrapperVersion); headers.put("X-LaunchDarkly-Wrapper", wrapperId); diff --git a/src/main/java/com/launchdarkly/sdk/server/LDConfig.java b/src/main/java/com/launchdarkly/sdk/server/LDConfig.java index 2ae272aa1..4c18a51bd 100644 --- a/src/main/java/com/launchdarkly/sdk/server/LDConfig.java +++ b/src/main/java/com/launchdarkly/sdk/server/LDConfig.java @@ -1,6 +1,7 @@ package com.launchdarkly.sdk.server; import com.launchdarkly.sdk.EvaluationReason; +import com.launchdarkly.sdk.server.integrations.ApplicationInfoBuilder; import com.launchdarkly.sdk.server.integrations.BigSegmentsConfigurationBuilder; import com.launchdarkly.sdk.server.interfaces.BigSegmentStoreFactory; import com.launchdarkly.sdk.server.interfaces.DataSourceFactory; @@ -25,6 +26,7 @@ public final class LDConfig { protected static final LDConfig DEFAULT = new Builder().build(); + final ApplicationInfoBuilder applicationInfoBuilder; final BigSegmentsConfigurationBuilder bigSegmentsConfigBuilder; final DataSourceFactory dataSourceFactory; final DataStoreFactory dataStoreFactory; @@ -46,6 +48,8 @@ protected LDConfig(Builder builder) { this.eventProcessorFactory = builder.eventProcessorFactory == null ? Components.sendEvents() : builder.eventProcessorFactory; } + this.applicationInfoBuilder = builder.applicationInfoBuilder == null ? Components.applicationInfo() : + builder.applicationInfoBuilder; this.bigSegmentsConfigBuilder = builder.bigSegmentsConfigBuilder == null ? Components.bigSegments(null) : builder.bigSegmentsConfigBuilder; this.dataStoreFactory = builder.dataStoreFactory == null ? Components.inMemoryDataStore() : @@ -72,6 +76,7 @@ protected LDConfig(Builder builder) { * */ public static class Builder { + private ApplicationInfoBuilder applicationInfoBuilder = null; private BigSegmentsConfigurationBuilder bigSegmentsConfigBuilder = null; private DataSourceFactory dataSourceFactory = null; private DataStoreFactory dataStoreFactory = null; @@ -89,6 +94,22 @@ public static class Builder { public Builder() { } + /** + * Sets the SDK's application metadata, which may be used in LaunchDarkly analytics or other product features, + * but does not affect feature flag evaluations. + *

+ * This object is normally a configuration builder obtained from {@link Components#applicationInfo()}, + * which has methods for setting individual logging-related properties. + * + * @param applicationInfoBuilder a configuration builder object returned by {@link Components#applicationInfo()} + * @return the builder + * @since 5.8.0 + */ + public Builder applicationInfo(ApplicationInfoBuilder applicationInfoBuilder) { + this.applicationInfoBuilder = applicationInfoBuilder; + return this; + } + /** * Sets the configuration of the SDK's Big Segments feature. *

diff --git a/src/main/java/com/launchdarkly/sdk/server/Util.java b/src/main/java/com/launchdarkly/sdk/server/Util.java index fc6a4cc02..990e12343 100644 --- a/src/main/java/com/launchdarkly/sdk/server/Util.java +++ b/src/main/java/com/launchdarkly/sdk/server/Util.java @@ -1,5 +1,7 @@ package com.launchdarkly.sdk.server; +import com.launchdarkly.sdk.server.Loggers; +import com.launchdarkly.sdk.server.interfaces.ApplicationInfo; import com.launchdarkly.sdk.server.interfaces.HttpAuthentication; import com.launchdarkly.sdk.server.interfaces.HttpConfiguration; @@ -13,7 +15,10 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.time.Duration; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import java.util.concurrent.TimeUnit; import static com.google.common.collect.Iterables.transform; @@ -207,4 +212,35 @@ static URI concatenateUriPath(URI baseUri, String path) { String addPath = path.startsWith("/") ? path.substring(1) : path; return URI.create(uriStr + (uriStr.endsWith("/") ? "" : "/") + addPath); } + + // Tag values must not be empty, and only contain letters, numbers, `.`, `_`, or `-`. + private static Pattern TAG_VALUE_REGEX = Pattern.compile("^[\\w.-]+$"); + + /** + * Builds the "X-LaunchDarkly-Tags" HTTP header out of the configured application info. + * + * @param applicationInfo the application metadata + * @return a space-separated string of tags, e.g. "application-id/authentication-service application-version/1.0.0" + */ + static String applicationTagHeader(ApplicationInfo applicationInfo) { + String[][] tags = { + {"applicationId", "application-id", applicationInfo.getApplicationId()}, + {"applicationVersion", "application-version", applicationInfo.getApplicationVersion()}, + }; + List parts = new ArrayList<>(); + for (String[] row : tags) { + String javaKey = row[0]; + String tagKey = row[1]; + String tagVal = row[2]; + if (tagVal == null) { + continue; + } + if (!TAG_VALUE_REGEX.matcher(tagVal).matches()) { + Loggers.MAIN.warn("Value of ApplicationInfo.{} contained invalid characters and was discarded", javaKey); + continue; + } + parts.add(tagKey + "/" + tagVal); + } + return String.join(" ", parts); + } } diff --git a/src/main/java/com/launchdarkly/sdk/server/integrations/ApplicationInfoBuilder.java b/src/main/java/com/launchdarkly/sdk/server/integrations/ApplicationInfoBuilder.java new file mode 100644 index 000000000..1b510ef72 --- /dev/null +++ b/src/main/java/com/launchdarkly/sdk/server/integrations/ApplicationInfoBuilder.java @@ -0,0 +1,77 @@ +package com.launchdarkly.sdk.server.integrations; + +import com.launchdarkly.sdk.server.Components; +import com.launchdarkly.sdk.server.interfaces.ApplicationInfo; + +/** + * Contains methods for configuring the SDK's application metadata. + *

+ * Application metadata may be used in LaunchDarkly analytics or other product features, but does not affect feature flag evaluations. + *

+ * If you want to set non-default values for any of these fields, create a builder with + * {@link Components#applicationInfo()}, change its properties with the methods of this class, + * and pass it to {@link com.launchdarkly.sdk.server.LDConfig.Builder#applicationInfo(ApplicationInfoBuilder)}: + *


+ *     LDConfig config = new LDConfig.Builder()
+ *         .applicationInfo(
+ *             Components.applicationInfo()
+ *                 .applicationId("authentication-service")
+ *                 .applicationVersion("1.0.0")
+ *         )
+ *         .build();
+ * 
+ *

+ * + * @since 5.8.0 + */ +public final class ApplicationInfoBuilder { + private String applicationId; + private String applicationVersion; + + /** + * Create an empty ApplicationInfoBuilder. + * + * @see Components#applicationInfo() + */ + public ApplicationInfoBuilder() {} + + /** + * Sets a unique identifier representing the application where the LaunchDarkly SDK is running. + *

+ * This can be specified as any string value as long as it only uses the following characters: ASCII + * letters, ASCII digits, period, hyphen, underscore. A string containing any other characters will be + * ignored. + * + * @param applicationId the application identifier + * @return the builder + */ + public ApplicationInfoBuilder applicationId(String applicationId) { + this.applicationId = applicationId; + return this; + } + + /** + * Sets a unique identifier representing the version of the application where the LaunchDarkly SDK + * is running. + *

+ * This can be specified as any string value as long as it only uses the following characters: ASCII + * letters, ASCII digits, period, hyphen, underscore. A string containing any other characters will be + * ignored. + * + * @param applicationVersion the application version + * @return the builder + */ + public ApplicationInfoBuilder applicationVersion(String applicationVersion) { + this.applicationVersion = applicationVersion; + return this; + } + + /** + * Called internally by the SDK to create the configuration object. + * + * @return the configuration object + */ + public ApplicationInfo createApplicationInfo() { + return new ApplicationInfo(applicationId, applicationVersion); + } +} diff --git a/src/main/java/com/launchdarkly/sdk/server/interfaces/ApplicationInfo.java b/src/main/java/com/launchdarkly/sdk/server/interfaces/ApplicationInfo.java new file mode 100644 index 000000000..e64ed6c0b --- /dev/null +++ b/src/main/java/com/launchdarkly/sdk/server/interfaces/ApplicationInfo.java @@ -0,0 +1,46 @@ +package com.launchdarkly.sdk.server.interfaces; + +import com.launchdarkly.sdk.server.integrations.ApplicationInfoBuilder; + +/** + * Encapsulates the SDK's application metadata. + *

+ * See {@link ApplicationInfoBuilder} for more details on these properties. + * + * @since 5.8.0 + */ +public final class ApplicationInfo { + private String applicationId; + private String applicationVersion; + + /** + * Used internally by the SDK to store application metadata. + * + * @param applicationId the application ID + * @param applicationVersion the application version + * @see ApplicationInfoBuilder + */ + public ApplicationInfo(String applicationId, String applicationVersion) { + this.applicationId = applicationId; + this.applicationVersion = applicationVersion; + } + + /** + * A unique identifier representing the application where the LaunchDarkly SDK is running. + * + * @return the application identifier, or null + */ + public String getApplicationId() { + return applicationId; + } + + /** + * A unique identifier representing the version of the application where the + * LaunchDarkly SDK is running. + * + * @return the application version, or null + */ + public String getApplicationVersion() { + return applicationVersion; + } +} diff --git a/src/main/java/com/launchdarkly/sdk/server/interfaces/BasicConfiguration.java b/src/main/java/com/launchdarkly/sdk/server/interfaces/BasicConfiguration.java index 926cf3f13..3a19a9737 100644 --- a/src/main/java/com/launchdarkly/sdk/server/interfaces/BasicConfiguration.java +++ b/src/main/java/com/launchdarkly/sdk/server/interfaces/BasicConfiguration.java @@ -9,18 +9,33 @@ public final class BasicConfiguration { private final String sdkKey; private final boolean offline; private final int threadPriority; - + private final ApplicationInfo applicationInfo; + /** * Constructs an instance. - * + * * @param sdkKey the SDK key * @param offline true if the SDK was configured to be completely offline * @param threadPriority the thread priority that should be used for any worker threads created by SDK components + * @param applicationInfo metadata about the application using this SDK */ - public BasicConfiguration(String sdkKey, boolean offline, int threadPriority) { + public BasicConfiguration(String sdkKey, boolean offline, int threadPriority, ApplicationInfo applicationInfo) { this.sdkKey = sdkKey; this.offline = offline; this.threadPriority = threadPriority; + this.applicationInfo = applicationInfo; + } + + /** + * Constructs an instance. + * + * @param sdkKey the SDK key + * @param offline true if the SDK was configured to be completely offline + * @param threadPriority the thread priority that should be used for any worker threads created by SDK components + */ + @Deprecated + public BasicConfiguration(String sdkKey, boolean offline, int threadPriority) { + this(sdkKey, offline, threadPriority, null); } /** @@ -51,4 +66,14 @@ public boolean isOffline() { public int getThreadPriority() { return threadPriority; } + + /** + * The metadata about the application using this SDK. + * + * @return the application info + * @see com.launchdarkly.sdk.server.LDConfig.Builder#applicationInfo(com.launchdarkly.sdk.server.integrations.ApplicationInfoBuilder) + */ + public ApplicationInfo getApplicationInfo() { + return applicationInfo; + } } diff --git a/src/test/java/com/launchdarkly/sdk/server/ClientContextImplTest.java b/src/test/java/com/launchdarkly/sdk/server/ClientContextImplTest.java index 1268e5378..026576a5b 100644 --- a/src/test/java/com/launchdarkly/sdk/server/ClientContextImplTest.java +++ b/src/test/java/com/launchdarkly/sdk/server/ClientContextImplTest.java @@ -133,7 +133,7 @@ public void packagePrivatePropertiesHaveDefaultsIfContextIsNotOurImplementation( private static final class SomeOtherContextImpl implements ClientContext { public BasicConfiguration getBasic() { - return new BasicConfiguration(SDK_KEY, false, Thread.MIN_PRIORITY); + return new BasicConfiguration(SDK_KEY, false, Thread.MIN_PRIORITY, null); } public HttpConfiguration getHttp() { diff --git a/src/test/java/com/launchdarkly/sdk/server/DefaultFeatureRequestorTest.java b/src/test/java/com/launchdarkly/sdk/server/DefaultFeatureRequestorTest.java index 2c3f52587..4844a5e0f 100644 --- a/src/test/java/com/launchdarkly/sdk/server/DefaultFeatureRequestorTest.java +++ b/src/test/java/com/launchdarkly/sdk/server/DefaultFeatureRequestorTest.java @@ -42,7 +42,7 @@ private DefaultFeatureRequestor makeRequestor(HttpServer server, LDConfig config } private HttpConfiguration makeHttpConfig(LDConfig config) { - return config.httpConfigFactory.createHttpConfiguration(new BasicConfiguration(sdkKey, false, 0)); + return config.httpConfigFactory.createHttpConfiguration(new BasicConfiguration(sdkKey, false, 0, null)); } private void verifyExpectedData(FeatureRequestor.AllData data) { diff --git a/src/test/java/com/launchdarkly/sdk/server/LDConfigTest.java b/src/test/java/com/launchdarkly/sdk/server/LDConfigTest.java index 0afba1f6b..3794e990a 100644 --- a/src/test/java/com/launchdarkly/sdk/server/LDConfigTest.java +++ b/src/test/java/com/launchdarkly/sdk/server/LDConfigTest.java @@ -26,7 +26,7 @@ @SuppressWarnings("javadoc") public class LDConfigTest { - private static final BasicConfiguration BASIC_CONFIG = new BasicConfiguration("", false, 0); + private static final BasicConfiguration BASIC_CONFIG = new BasicConfiguration("", false, 0, null); @Test public void defaults() { diff --git a/src/test/java/com/launchdarkly/sdk/server/UtilTest.java b/src/test/java/com/launchdarkly/sdk/server/UtilTest.java index 6cf5b6fb3..2a9b91151 100644 --- a/src/test/java/com/launchdarkly/sdk/server/UtilTest.java +++ b/src/test/java/com/launchdarkly/sdk/server/UtilTest.java @@ -1,5 +1,6 @@ package com.launchdarkly.sdk.server; +import com.launchdarkly.sdk.server.interfaces.ApplicationInfo; import com.launchdarkly.sdk.server.interfaces.HttpAuthentication; import com.launchdarkly.sdk.server.interfaces.HttpConfiguration; @@ -90,4 +91,16 @@ public void describeDuration() { assertEquals("1 minute", Util.describeDuration(Duration.ofMillis(60000))); assertEquals("2 minutes", Util.describeDuration(Duration.ofMillis(120000))); } + + @Test + public void applicationTagHeader() { + assertEquals("", Util.applicationTagHeader(new ApplicationInfo(null, null))); + assertEquals("application-id/foo", Util.applicationTagHeader(new ApplicationInfo("foo", null))); + assertEquals("application-version/1.0.0", Util.applicationTagHeader(new ApplicationInfo(null, "1.0.0"))); + assertEquals("application-id/foo application-version/1.0.0", Util.applicationTagHeader(new ApplicationInfo("foo", "1.0.0"))); + // Values with invalid characters get discarded + assertEquals("", Util.applicationTagHeader(new ApplicationInfo("invalid name", "lol!"))); + // Empty values get discarded + assertEquals("", Util.applicationTagHeader(new ApplicationInfo("", ""))); + } } diff --git a/src/test/java/com/launchdarkly/sdk/server/integrations/ApplicationInfoBuilderTest.java b/src/test/java/com/launchdarkly/sdk/server/integrations/ApplicationInfoBuilderTest.java new file mode 100644 index 000000000..9b9977e06 --- /dev/null +++ b/src/test/java/com/launchdarkly/sdk/server/integrations/ApplicationInfoBuilderTest.java @@ -0,0 +1,27 @@ +package com.launchdarkly.sdk.server.integrations; + +import com.launchdarkly.sdk.server.Components; +import com.launchdarkly.sdk.server.interfaces.ApplicationInfo; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@SuppressWarnings("javadoc") +public class ApplicationInfoBuilderTest { + @Test + public void infoBuilder() { + ApplicationInfo i1 = Components.applicationInfo() + .createApplicationInfo(); + assertNull(i1.getApplicationId()); + assertNull(i1.getApplicationVersion()); + + ApplicationInfo i2 = Components.applicationInfo() + .applicationId("authentication-service") + .applicationVersion("1.0.0") + .createApplicationInfo(); + assertEquals("authentication-service", i2.getApplicationId()); + assertEquals("1.0.0", i2.getApplicationVersion()); + } +} diff --git a/src/test/java/com/launchdarkly/sdk/server/integrations/HttpConfigurationBuilderTest.java b/src/test/java/com/launchdarkly/sdk/server/integrations/HttpConfigurationBuilderTest.java index 6b2b6bea6..06783edf8 100644 --- a/src/test/java/com/launchdarkly/sdk/server/integrations/HttpConfigurationBuilderTest.java +++ b/src/test/java/com/launchdarkly/sdk/server/integrations/HttpConfigurationBuilderTest.java @@ -2,6 +2,7 @@ import com.google.common.collect.ImmutableMap; import com.launchdarkly.sdk.server.Components; +import com.launchdarkly.sdk.server.interfaces.ApplicationInfo; import com.launchdarkly.sdk.server.interfaces.BasicConfiguration; import com.launchdarkly.sdk.server.interfaces.HttpConfiguration; @@ -32,7 +33,7 @@ @SuppressWarnings("javadoc") public class HttpConfigurationBuilderTest { private static final String SDK_KEY = "sdk-key"; - private static final BasicConfiguration BASIC_CONFIG = new BasicConfiguration(SDK_KEY, false, 0); + private static final BasicConfiguration BASIC_CONFIG = new BasicConfiguration(SDK_KEY, false, 0, null); private static ImmutableMap.Builder buildBasicHeaders() { return ImmutableMap.builder() @@ -136,6 +137,15 @@ public void testWrapperWithVersion() { .createHttpConfiguration(BASIC_CONFIG); assertEquals("Scala/0.1.0", ImmutableMap.copyOf(hc.getDefaultHeaders()).get("X-LaunchDarkly-Wrapper")); } + + @Test + public void testApplicationTags() { + ApplicationInfo info = new ApplicationInfo("authentication-service", "1.0.0"); + BasicConfiguration basicConfigWithTags = new BasicConfiguration(SDK_KEY, false, 0, info); + HttpConfiguration hc = Components.httpConfiguration() + .createHttpConfiguration(basicConfigWithTags); + assertEquals("application-id/authentication-service application-version/1.0.0", ImmutableMap.copyOf(hc.getDefaultHeaders()).get("X-LaunchDarkly-Tags")); + } public static class StubSocketFactory extends SocketFactory { public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) diff --git a/src/test/java/com/launchdarkly/sdk/server/integrations/LoggingConfigurationBuilderTest.java b/src/test/java/com/launchdarkly/sdk/server/integrations/LoggingConfigurationBuilderTest.java index 33d86a53a..f9c8f6992 100644 --- a/src/test/java/com/launchdarkly/sdk/server/integrations/LoggingConfigurationBuilderTest.java +++ b/src/test/java/com/launchdarkly/sdk/server/integrations/LoggingConfigurationBuilderTest.java @@ -14,7 +14,7 @@ @SuppressWarnings("javadoc") public class LoggingConfigurationBuilderTest { private static final String SDK_KEY = "sdk-key"; - private static final BasicConfiguration BASIC_CONFIG = new BasicConfiguration(SDK_KEY, false, 0); + private static final BasicConfiguration BASIC_CONFIG = new BasicConfiguration(SDK_KEY, false, 0, null); @Test public void testDefaults() {