diff --git a/CHANGELOG.md b/CHANGELOG.md index f723edc5..0f279581 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the LaunchDarkly Android SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org). +## [3.6.0] - 2023-01-11 +### Added: +- `LDConfig.Builder.applicationInfo()`, for configuration of application metadata that may be used in LaunchDarkly analytics or other product features. This does not affect feature flag evaluations. + ## [4.1.1] - 2023-01-06 ### Fixed: - The fix for unnecessarily long-lived polling connections in the [3.2.2](https://github.com/launchdarkly/android-client-sdk/releases/tag/3.2.2) release was incomplete: rather than turning off the keep-alive behavior, it only reduced it from 10 minutes to 5 minutes. It should now close the connection immediately after each request as intended. diff --git a/contract-tests/src/main/java/com/launchdarkly/sdktest/SdkClientEntity.java b/contract-tests/src/main/java/com/launchdarkly/sdktest/SdkClientEntity.java index 045883c7..fbcb71f3 100644 --- a/contract-tests/src/main/java/com/launchdarkly/sdktest/SdkClientEntity.java +++ b/contract-tests/src/main/java/com/launchdarkly/sdktest/SdkClientEntity.java @@ -12,6 +12,8 @@ import com.launchdarkly.sdk.android.LaunchDarklyException; import com.launchdarkly.sdk.android.LDClient; import com.launchdarkly.sdk.android.LDConfig; + +import com.launchdarkly.sdk.android.integrations.ApplicationInfoBuilder; import com.launchdarkly.sdk.android.integrations.EventProcessorBuilder; import com.launchdarkly.sdk.android.integrations.PollingDataSourceBuilder; import com.launchdarkly.sdk.android.integrations.StreamingDataSourceBuilder; @@ -310,6 +312,17 @@ private LDConfig buildSdkConfig(SdkConfigParams params, LDLogAdapter logAdapter, Components.httpConfiguration().useReport(params.clientSide.useReport) ); + 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); + } + if (params.serviceEndpoints != null) { if (params.serviceEndpoints.streaming != null) { endpoints.streaming(params.serviceEndpoints.streaming); diff --git a/contract-tests/src/main/java/com/launchdarkly/sdktest/TestService.java b/contract-tests/src/main/java/com/launchdarkly/sdktest/TestService.java index e4bd626e..69f9b55f 100644 --- a/contract-tests/src/main/java/com/launchdarkly/sdktest/TestService.java +++ b/contract-tests/src/main/java/com/launchdarkly/sdktest/TestService.java @@ -32,6 +32,7 @@ public class TestService extends NanoHTTPD { "service-endpoints", "singleton", "strongly-typed", + "tags" }; private static final String MIME_JSON = "application/json"; static final Gson gson = new GsonBuilder() diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ClientContextImpl.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ClientContextImpl.java index 4f2fb7aa..80678cc2 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ClientContextImpl.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ClientContextImpl.java @@ -2,7 +2,6 @@ import com.launchdarkly.logging.LDLogger; import com.launchdarkly.sdk.LDContext; -import com.launchdarkly.sdk.android.interfaces.ServiceEndpoints; import com.launchdarkly.sdk.android.subsystems.ClientContext; import com.launchdarkly.sdk.android.subsystems.HttpConfiguration; import com.launchdarkly.sdk.android.subsystems.DataSourceUpdateSink; @@ -58,12 +57,13 @@ static ClientContextImpl fromConfig( TaskExecutor taskExecutor ) { boolean initiallyInBackground = platformState != null && !platformState.isForeground(); - ClientContext minimalContext = new ClientContext(mobileKey, logger, config, + ClientContext minimalContext = new ClientContext(mobileKey, config.applicationInfo, logger, config, null, environmentName, config.isEvaluationReasons(), initialContext, null, initiallyInBackground, null, config.serviceEndpoints, config.isOffline()); HttpConfiguration httpConfig = config.http.build(minimalContext); ClientContext baseClientContext = new ClientContext( mobileKey, + config.applicationInfo, logger, config, null, @@ -101,6 +101,7 @@ public static ClientContextImpl forDataSource( return new ClientContextImpl( new ClientContext( baseClientContext.getMobileKey(), + baseClientContext.getApplicationInfo(), baseClientContext.getBaseLogger(), baseClientContext.getConfig(), dataSourceUpdateSink, diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/Components.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/Components.java index c41382d8..eea95f08 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/Components.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/Components.java @@ -1,5 +1,6 @@ package com.launchdarkly.sdk.android; +import com.launchdarkly.sdk.android.integrations.ApplicationInfoBuilder; import com.launchdarkly.sdk.android.integrations.EventProcessorBuilder; import com.launchdarkly.sdk.android.integrations.HttpConfigurationBuilder; import com.launchdarkly.sdk.android.integrations.PollingDataSourceBuilder; @@ -25,6 +26,29 @@ public abstract class Components { private Components() {} + /** + * Returns a configuration builder for the SDK's application metadata. + *

+ * Passing this to {@link LDConfig.Builder#applicationInfo(com.launchdarkly.sdk.android.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 + * @see LDConfig.Builder#applicationInfo(com.launchdarkly.sdk.android.integrations.ApplicationInfoBuilder) + * @since 4.1.0 + */ + public static ApplicationInfoBuilder applicationInfo() { + return new ApplicationInfoBuilder(); + } + /** * Returns a configuration builder for the SDK's networking configuration. *

diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ComponentsImpl.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ComponentsImpl.java index 367a2343..92df3580 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ComponentsImpl.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ComponentsImpl.java @@ -211,6 +211,13 @@ public HttpConfiguration build(ClientContext clientContext) { Map headers = new HashMap<>(); headers.put("Authorization", LDUtil.AUTH_SCHEME + clientContext.getMobileKey()); headers.put("User-Agent", LDUtil.USER_AGENT_HEADER_VALUE); + if (clientContext.getApplicationInfo() != null) { + String tagHeader = LDUtil.applicationTagHeader(clientContext.getApplicationInfo(), + clientContext.getBaseLogger()); + 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/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDConfig.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDConfig.java index 9b4a2dad..c0baa974 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDConfig.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDConfig.java @@ -6,8 +6,10 @@ import com.launchdarkly.sdk.ContextKind; import com.launchdarkly.sdk.LDContext; import com.launchdarkly.sdk.LDUser; +import com.launchdarkly.sdk.android.integrations.ApplicationInfoBuilder; import com.launchdarkly.sdk.android.integrations.ServiceEndpointsBuilder; import com.launchdarkly.sdk.android.interfaces.ServiceEndpoints; +import com.launchdarkly.sdk.android.subsystems.ApplicationInfo; import com.launchdarkly.sdk.android.subsystems.ComponentConfigurer; import com.launchdarkly.sdk.android.subsystems.DataSource; import com.launchdarkly.sdk.android.subsystems.EventProcessor; @@ -55,6 +57,7 @@ public class LDConfig { final ServiceEndpoints serviceEndpoints; + final ApplicationInfo applicationInfo; final ComponentConfigurer dataSource; final ComponentConfigurer events; final ComponentConfigurer http; @@ -71,6 +74,7 @@ public class LDConfig { LDConfig(Map mobileKeys, ServiceEndpoints serviceEndpoints, + ApplicationInfo applicationInfo, ComponentConfigurer dataSource, ComponentConfigurer events, ComponentConfigurer http, @@ -85,6 +89,7 @@ public class LDConfig { String loggerName) { this.mobileKeys = mobileKeys; this.serviceEndpoints = serviceEndpoints; + this.applicationInfo = applicationInfo; this.dataSource = dataSource; this.events = events; this.http = http; @@ -151,6 +156,7 @@ public static class Builder { private ServiceEndpointsBuilder serviceEndpointsBuilder; + private ApplicationInfoBuilder applicationInfoBuilder = null; private ComponentConfigurer dataSource = null; private ComponentConfigurer events = null; private ComponentConfigurer http = null; @@ -235,6 +241,22 @@ public Builder serviceEndpoints(ServiceEndpointsBuilder serviceEndpointsBuilder) return this; } + /** + * 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 metadata properties. + * + * @param applicationInfoBuilder a configuration builder object returned by {@link Components#applicationInfo()} + * @return the builder + * @since 4.1.0 + */ + public Builder applicationInfo(ApplicationInfoBuilder applicationInfoBuilder) { + this.applicationInfoBuilder = applicationInfoBuilder; + return this; + } + /** * Sets the configuration of the component that receives feature flag data from LaunchDarkly. *

@@ -574,9 +596,14 @@ public LDConfig build() { serviceEndpointsBuilder) .createServiceEndpoints(); + ApplicationInfo applicationInfo = this.applicationInfoBuilder == null ? + Components.applicationInfo().createApplicationInfo() : + applicationInfoBuilder.createApplicationInfo(); + return new LDConfig( mobileKeys, serviceEndpoints, + applicationInfo, this.dataSource == null ? Components.streamingDataSource() : this.dataSource, this.events == null ? Components.sendEvents() : this.events, this.http == null ? Components.httpConfiguration() : this.http, diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDUtil.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDUtil.java index 19636b1c..c23166c2 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDUtil.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDUtil.java @@ -7,6 +7,7 @@ import com.launchdarkly.logging.LDLogger; import com.launchdarkly.logging.LogValues; import com.launchdarkly.sdk.LDContext; +import com.launchdarkly.sdk.android.subsystems.ApplicationInfo; import com.launchdarkly.sdk.android.subsystems.Callback; import com.launchdarkly.sdk.android.subsystems.ClientContext; import com.launchdarkly.sdk.android.subsystems.HttpConfiguration; @@ -18,9 +19,12 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import okhttp3.Headers; @@ -31,11 +35,48 @@ class LDUtil { static Callback noOpCallback() { return new Callback() { @Override - public void onSuccess(T result) {} + public void onSuccess(T result) { + } @Override - public void onError(Throwable error) {} + public void onError(Throwable error) { + } + }; + } + + // Tag values must not be empty, and only contain letters, numbers, `.`, `_`, or `-`. + private static Pattern TAG_VALUE_REGEX = Pattern.compile("^[-a-zA-Z0-9._]+$"); + + /** + * 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, LDLogger logger) { + 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()) { + logger.warn("Value of ApplicationInfo.{} contained invalid characters and was discarded", javaKey); + continue; + } + if (tagVal.length() > 64) { + logger.warn("Value of ApplicationInfo.{} was longer than 64 characters and was discarded", javaKey); + continue; + } + parts.add(tagKey + "/" + tagVal); + } + return String.join(" ", parts); } static Headers makeRequestHeaders( diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/ApplicationInfoBuilder.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/ApplicationInfoBuilder.java new file mode 100644 index 00000000..9295c931 --- /dev/null +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/ApplicationInfoBuilder.java @@ -0,0 +1,77 @@ +package com.launchdarkly.sdk.android.integrations; + +import com.launchdarkly.sdk.android.Components; +import com.launchdarkly.sdk.android.subsystems.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.android.LDConfig.Builder#applicationInfo(ApplicationInfoBuilder)}: + *


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

+ * + * @since 4.1.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/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ApplicationInfo.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ApplicationInfo.java new file mode 100644 index 00000000..d66f20db --- /dev/null +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ApplicationInfo.java @@ -0,0 +1,46 @@ +package com.launchdarkly.sdk.android.subsystems; + +import com.launchdarkly.sdk.android.integrations.ApplicationInfoBuilder; + +/** + * Encapsulates the SDK's application metadata. + *

+ * See {@link ApplicationInfoBuilder} for more details on these properties. + * + * @since 4.1.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/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ClientContext.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ClientContext.java index f5b01589..58ab0298 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ClientContext.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ClientContext.java @@ -22,6 +22,7 @@ * @since 3.3.0 */ public class ClientContext { + private final ApplicationInfo applicationInfo; private final LDLogger baseLogger; private final LDConfig config; private final DataSourceUpdateSink dataSourceUpdateSink; @@ -53,6 +54,7 @@ public class ClientContext { */ public ClientContext( String mobileKey, + ApplicationInfo applicationInfo, LDLogger baseLogger, LDConfig config, DataSourceUpdateSink dataSourceUpdateSink, @@ -66,6 +68,7 @@ public ClientContext( boolean setOffline ) { this.mobileKey = mobileKey; + this.applicationInfo = applicationInfo; this.baseLogger = baseLogger; this.config = config; this.dataSourceUpdateSink = dataSourceUpdateSink; @@ -79,6 +82,42 @@ public ClientContext( this.setOffline = setOffline; } + /** + * Deprecated constructor overload. + * + * @param mobileKey see {@link #getMobileKey()} + * @param baseLogger see {@link #getBaseLogger()} + * @param config see {@link #getConfig()} + * @param dataSourceUpdateSink see {@link #getDataSourceUpdateSink()} + * @param environmentName see {@link #getEnvironmentName()} + * @param evaluationReasons see {@link #isEvaluationReasons()} + * @param evaluationContext see {@link #getEvaluationContext()} + * @param http see {@link #getHttp()} + * @param inBackground see {@link #isInBackground()} + * @param serviceEndpoints see {@link #getServiceEndpoints()} + * @param setOffline see {@link #isSetOffline()} + * @deprecated use newer constructor + */ + @Deprecated + public ClientContext( + String mobileKey, + LDLogger baseLogger, + LDConfig config, + DataSourceUpdateSink dataSourceUpdateSink, + String environmentName, + boolean evaluationReasons, + LDContext evaluationContext, + HttpConfiguration http, + boolean inBackground, + Boolean previouslyInBackground, + ServiceEndpoints serviceEndpoints, + boolean setOffline + ) { + this(mobileKey, null, baseLogger, config, dataSourceUpdateSink, environmentName, + evaluationReasons, evaluationContext, http, inBackground, previouslyInBackground, + serviceEndpoints, setOffline); + } + /** * Deprecated constructor overload. * @@ -109,7 +148,7 @@ public ClientContext( ServiceEndpoints serviceEndpoints, boolean setOffline ) { - this(mobileKey, baseLogger, config, dataSourceUpdateSink, environmentName, + this(mobileKey, null, baseLogger, config, dataSourceUpdateSink, environmentName, evaluationReasons, evaluationContext, http, inBackground, null, serviceEndpoints, setOffline); @@ -118,6 +157,7 @@ public ClientContext( protected ClientContext(ClientContext copyFrom) { this( copyFrom.mobileKey, + copyFrom.applicationInfo, copyFrom.baseLogger, copyFrom.config, copyFrom.dataSourceUpdateSink, @@ -132,6 +172,14 @@ protected ClientContext(ClientContext copyFrom) { ); } + /** + * The application metadata object. + * @return the application metadata + */ + public ApplicationInfo getApplicationInfo() { + return applicationInfo; + } + /** * The base logger for the SDK. * @return a logger instance diff --git a/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/HttpConfigurationBuilderTest.java b/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/HttpConfigurationBuilderTest.java new file mode 100644 index 00000000..559b0711 --- /dev/null +++ b/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/HttpConfigurationBuilderTest.java @@ -0,0 +1,76 @@ +package com.launchdarkly.sdk.android; + +import com.launchdarkly.sdk.android.subsystems.ApplicationInfo; +import com.launchdarkly.sdk.android.subsystems.ClientContext; +import com.launchdarkly.sdk.android.subsystems.HttpConfiguration; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static com.launchdarkly.sdk.android.integrations.HttpConfigurationBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS; +import static org.junit.Assert.assertEquals; + +public class HttpConfigurationBuilderTest { + private static final String MOBILE_KEY = "mobile-key"; + private static final ClientContext BASIC_CONTEXT = new ClientContext(MOBILE_KEY, null, null, null, null, "", + false, null, null, false, null, null, false); + + private static Map buildBasicHeaders() { + Map ret = new HashMap<>(); + ret.put("Authorization", LDUtil.AUTH_SCHEME + MOBILE_KEY); + ret.put("User-Agent", LDUtil.USER_AGENT_HEADER_VALUE); + return ret; + } + + private static Map toMap(Iterable> entries) { + Map ret = new HashMap<>(); + for (Map.Entry e: entries) { + ret.put(e.getKey(), e.getValue()); + } + return ret; + } + + @Test + public void testDefaults() { + HttpConfiguration hc = Components.httpConfiguration().build(BASIC_CONTEXT); + assertEquals(DEFAULT_CONNECT_TIMEOUT_MILLIS, hc.getConnectTimeoutMillis()); + assertEquals(buildBasicHeaders(), toMap(hc.getDefaultHeaders())); + } + + @Test + public void testConnectTimeout() { + HttpConfiguration hc = Components.httpConfiguration() + .connectTimeoutMillis(999) + .build(BASIC_CONTEXT); + assertEquals(999, hc.getConnectTimeoutMillis()); + } + + @Test + public void testWrapperNameOnly() { + HttpConfiguration hc = Components.httpConfiguration() + .wrapper("Scala", null) + .build(BASIC_CONTEXT); + assertEquals("Scala", toMap(hc.getDefaultHeaders()).get("X-LaunchDarkly-Wrapper")); + } + + @Test + public void testWrapperWithVersion() { + HttpConfiguration hc = Components.httpConfiguration() + .wrapper("Scala", "0.1.0") + .build(BASIC_CONTEXT); + assertEquals("Scala/0.1.0", toMap(hc.getDefaultHeaders()).get("X-LaunchDarkly-Wrapper")); + } + + @Test + public void testApplicationTags() { + ApplicationInfo info = new ApplicationInfo("authentication-service", "1.0.0"); + ClientContext contextWithTags = new ClientContext(MOBILE_KEY, info, null, null, null, + "", false, null, null, false, null, null, false); + HttpConfiguration hc = Components.httpConfiguration() + .build(contextWithTags); + assertEquals("application-id/authentication-service application-version/1.0.0", + toMap(hc.getDefaultHeaders()).get("X-LaunchDarkly-Tags")); + } +} diff --git a/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/TestDataTest.java b/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/TestDataTest.java index 0a76e8fe..557e1b73 100644 --- a/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/TestDataTest.java +++ b/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/TestDataTest.java @@ -152,7 +152,7 @@ public void flagConfigByVariationByValue() { } private void createAndStart() { - ClientContext clientContext = new ClientContext("", LDLogger.none(), + ClientContext clientContext = new ClientContext("", null, LDLogger.none(), new LDConfig.Builder().build(), updates, "", false, initialUser, null, false, null, null, false); DataSource ds = td.build(clientContext);