From 23c481d2e5f018a23487c47f6378707536bf7cb7 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Wed, 6 Nov 2024 10:35:45 +0000 Subject: [PATCH 01/28] refactor: remove config listeners --- .../internal/arch/DataCaptureOrchestrator.kt | 20 -------- .../arch/datasource/DataSourceState.kt | 7 --- .../internal/config/ConfigService.kt | 8 ---- .../internal/config/EmbraceConfigService.kt | 28 ----------- .../internal/injection/ConfigModuleImpl.kt | 1 - .../injection/DataSourceModuleImpl.kt | 3 -- .../injection/DataSourceModuleSupplier.kt | 5 -- .../arch/DataCaptureOrchestratorTest.kt | 30 ------------ .../internal/arch/DataSourceStateTest.kt | 47 +++---------------- .../config/EmbraceConfigServiceTest.kt | 28 +---------- .../injection/DataSourceModuleImplTest.kt | 2 - .../internal/session/PayloadFactoryBaTest.kt | 1 - .../session/PayloadFactorySessionTest.kt | 1 - .../SessionOrchestrationModuleImplTest.kt | 7 --- .../orchestrator/SessionOrchestratorTest.kt | 1 - .../anr/ndk/NativeThreadSamplerInstaller.kt | 20 -------- .../internal/logging/InternalErrorType.kt | 2 - .../testcases/EmbraceInternalInterfaceTest.kt | 2 - .../testcases/NetworkRequestApiTest.kt | 3 -- .../injection/ModuleInitBootstrapper.kt | 1 - .../fakes/FakeModuleInitBootstrapper.kt | 2 +- .../embracesdk/fakes/FakeConfigService.kt | 17 +------ .../embracesdk/fakes/FakeDataSourceModule.kt | 1 - 23 files changed, 10 insertions(+), 227 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/arch/DataCaptureOrchestrator.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/arch/DataCaptureOrchestrator.kt index 880bb5001c..e3e71caceb 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/arch/DataCaptureOrchestrator.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/arch/DataCaptureOrchestrator.kt @@ -1,7 +1,6 @@ package io.embrace.android.embracesdk.internal.arch import io.embrace.android.embracesdk.internal.arch.datasource.DataSourceState -import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.InternalErrorType import io.embrace.android.embracesdk.internal.worker.BackgroundWorker @@ -12,17 +11,10 @@ import java.util.concurrent.CopyOnWriteArrayList * place to coordinate everything in one place. */ class DataCaptureOrchestrator( - configService: ConfigService, private val worker: BackgroundWorker, private val logger: EmbLogger, ) : EmbraceFeatureRegistry { - init { - configService.addListener { - onConfigChange() - } - } - private val dataSourceStates = CopyOnWriteArrayList>() var currentSessionType: SessionType? = null @@ -38,18 +30,6 @@ class DataCaptureOrchestrator( } } - private fun onConfigChange() { - dataSourceStates.forEach { state -> - try { - state.dispatchStateChange { - state.onConfigChange() - } - } catch (exc: Throwable) { - logger.trackInternalError(InternalErrorType.CFG_CHANGE_DATA_CAPTURE_FAIL, exc) - } - } - } - /** * Callback that is invoked when the session type changes. */ diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/arch/datasource/DataSourceState.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/arch/datasource/DataSourceState.kt index d278f42172..707de6f66b 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/arch/datasource/DataSourceState.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/arch/datasource/DataSourceState.kt @@ -67,13 +67,6 @@ class DataSourceState>( } } - /** - * Callback that is invoked when the config layer experiences a change. - */ - fun onConfigChange() { - updateDataSource() - } - private fun updateDataSource() { val enabled = currentSessionType != null && currentSessionType != disabledSessionType && configGate() diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt index 14b688cc09..05a7bd7f0c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt @@ -112,14 +112,6 @@ interface ConfigService { */ fun isOnlyUsingOtelExporters(): Boolean - /** - * Adds a listener for changes to the [RemoteConfig]. The listeners will be notified when the - * [ConfigService] refreshes its configuration. - * - * @param configListener the listener to add - */ - fun addListener(configListener: () -> Unit) - /** * Checks if the SDK is enabled. * diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt index e0e32ac9fe..1cf435eccb 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt @@ -32,16 +32,12 @@ import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehav import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehaviorImpl import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.logging.EmbLogger -import io.embrace.android.embracesdk.internal.logging.InternalErrorType import io.embrace.android.embracesdk.internal.opentelemetry.OpenTelemetryConfiguration import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.prefs.PreferencesService import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessStateListener import io.embrace.android.embracesdk.internal.utils.Provider -import io.embrace.android.embracesdk.internal.utils.stream import io.embrace.android.embracesdk.internal.worker.BackgroundWorker -import java.util.concurrent.CopyOnWriteArraySet import kotlin.math.min /** @@ -51,7 +47,6 @@ internal class EmbraceConfigService( openTelemetryCfg: OpenTelemetryConfiguration, private val preferencesService: PreferencesService, private val clock: Clock, - private val logger: EmbLogger, private val backgroundWorker: BackgroundWorker, suppliedFramework: AppFramework, private val foregroundAction: ConfigService.() -> Unit, @@ -60,10 +55,6 @@ internal class EmbraceConfigService( BehaviorThresholdCheck { preferencesService.deviceIdentifier }, ) : ConfigService, ProcessStateListener { - /** - * The listeners subscribed to configuration changes. - */ - private val listeners: MutableSet<() -> Unit> = CopyOnWriteArraySet() private val lock = Any() override var remoteConfigSource: RemoteConfigSource? = null set(value) { @@ -238,8 +229,6 @@ internal class EmbraceConfigService( if (b) { configProp = newConfig persistConfig() - // Only notify listeners if the config has actually changed value - notifyListeners() } } @@ -259,10 +248,6 @@ internal class EmbraceConfigService( return preferencesService.backgroundActivityEnabled } - override fun addListener(configListener: () -> Unit) { - listeners.add(configListener) - } - override fun onForeground(coldStart: Boolean, timestamp: Long) { // Refresh the config on resume if it has expired getConfig() @@ -273,19 +258,6 @@ internal class EmbraceConfigService( AppFramework.fromString(it) } ?: suppliedFramework - /** - * Notifies the listeners that a new config was fetched from the server. - */ - private fun notifyListeners() { - stream(listeners) { listener -> - try { - listener() - } catch (ex: Exception) { - logger.trackInternalError(InternalErrorType.CONFIG_LISTENER_FAIL, ex) - } - } - } - /** * Checks if the time diff since the last fetch exceeds the * [EmbraceConfigService.CONFIG_TTL] millis. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index b756dead12..46713d00cf 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -25,7 +25,6 @@ internal class ConfigModuleImpl( openTelemetryCfg = openTelemetryModule.openTelemetryConfiguration, preferencesService = androidServicesModule.preferencesService, clock = initModule.clock, - logger = initModule.logger, backgroundWorker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), suppliedFramework = framework, foregroundAction = foregroundAction, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DataSourceModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DataSourceModuleImpl.kt index d8c89a3b5f..45c0932c1e 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DataSourceModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DataSourceModuleImpl.kt @@ -2,18 +2,15 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.arch.DataCaptureOrchestrator import io.embrace.android.embracesdk.internal.arch.EmbraceFeatureRegistry -import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.worker.Worker internal class DataSourceModuleImpl( initModule: InitModule, - configService: ConfigService, workerThreadModule: WorkerThreadModule, ) : DataSourceModule { override val dataCaptureOrchestrator: DataCaptureOrchestrator by singleton { DataCaptureOrchestrator( - configService, workerThreadModule.backgroundWorker(Worker.Background.NonIoRegWorker), initModule.logger ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DataSourceModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DataSourceModuleSupplier.kt index 859a44cdda..d6b78b4154 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DataSourceModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DataSourceModuleSupplier.kt @@ -1,22 +1,17 @@ package io.embrace.android.embracesdk.internal.injection -import io.embrace.android.embracesdk.internal.config.ConfigService - /** * Function that returns an instance of [DataSourceModule]. Matches the signature of the constructor for [DataSourceModuleImpl] */ typealias DataSourceModuleSupplier = ( initModule: InitModule, - configService: ConfigService, workerThreadModule: WorkerThreadModule, ) -> DataSourceModule fun createDataSourceModule( initModule: InitModule, - configService: ConfigService, workerThreadModule: WorkerThreadModule, ): DataSourceModule = DataSourceModuleImpl( initModule, - configService, workerThreadModule ) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/DataCaptureOrchestratorTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/DataCaptureOrchestratorTest.kt index 0a37a1e48e..426d6b21cf 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/DataCaptureOrchestratorTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/DataCaptureOrchestratorTest.kt @@ -39,24 +39,11 @@ internal class DataCaptureOrchestratorTest { configService = FakeConfigService() executorService = BlockingScheduledExecutorService(blockingMode = false) orchestrator = DataCaptureOrchestrator( - configService, BackgroundWorker(executorService), EmbLoggerImpl(), ) } - @Test - fun `config changes are propagated`() { - orchestrator.add(syncDataSource) - assertEquals(0, dataSource.enableDataCaptureCount) - orchestrator.currentSessionType = SessionType.FOREGROUND - assertEquals(1, dataSource.enableDataCaptureCount) - - enabled = false - configService.updateListeners() - assertEquals(1, dataSource.disableDataCaptureCount) - } - @Test fun `session type change is propagated`() { orchestrator.add(syncDataSource) @@ -65,23 +52,6 @@ internal class DataCaptureOrchestratorTest { assertEquals(1, dataSource.enableDataCaptureCount) } - @Test - fun `async config change`() { - orchestrator.add(asyncDataSource) - executorService.blockingMode = true - - orchestrator.currentSessionType = SessionType.FOREGROUND - assertEquals(0, dataSource.enableDataCaptureCount) - executorService.runCurrentlyBlocked() - assertEquals(1, dataSource.enableDataCaptureCount) - - enabled = false - configService.updateListeners() - assertEquals(0, dataSource.disableDataCaptureCount) - executorService.runCurrentlyBlocked() - assertEquals(1, dataSource.disableDataCaptureCount) - } - @Test fun `async session change`() { orchestrator.add(asyncDataSource) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/DataSourceStateTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/DataSourceStateTest.kt index c9e41f82f2..0c54d86ee4 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/DataSourceStateTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/DataSourceStateTest.kt @@ -21,15 +21,9 @@ internal class DataSourceStateTest { configGate = { true } ) - // data source not retrievable if not session type is null + // data source not retrievable if the session type is null assertNull(state.dataSource) - // data capture is enabled by default. - state.onConfigChange() - state.currentSessionType = null - assertEquals(0, source.enableDataCaptureCount) - assertEquals(0, source.disableDataCaptureCount) - // data capture enabled for a session state.currentSessionType = SessionType.FOREGROUND assertSame(source, state.dataSource) @@ -64,7 +58,7 @@ internal class DataSourceStateTest { } @Test - fun `test config gate enabled by default`() { + fun `test config gate enabled`() { val source = FakeDataSource(RuntimeEnvironment.getApplication()) DataSourceState( factory = { source }, @@ -79,47 +73,18 @@ internal class DataSourceStateTest { } @Test - fun `test config gate affects data capture`() { + fun `test config gate disabled`() { val source = FakeDataSource(RuntimeEnvironment.getApplication()) - var enabled = false - val state = DataSourceState( + DataSourceState( factory = { source }, - configGate = { enabled }, + configGate = { false }, ).apply { currentSessionType = SessionType.FOREGROUND } - // data source not retrievable if disabled - assertNull(state.dataSource) - - // data capture is disabled by default. + // data capture is enabled by default. assertEquals(0, source.enableDataCaptureCount) assertEquals(0, source.disableDataCaptureCount) - - // enabling the config gate should enable data capture - enabled = true - state.onConfigChange() - assertEquals(1, source.enableDataCaptureCount) - assertEquals(0, source.disableDataCaptureCount) - - // another config change should not reregister listeners - state.onConfigChange() - assertEquals(1, source.enableDataCaptureCount) - assertEquals(0, source.disableDataCaptureCount) - - // deregistering works - enabled = false - state.onConfigChange() - assertEquals(1, source.enableDataCaptureCount) - assertEquals(1, source.disableDataCaptureCount) - - // functions can be called multiple times without issue - enabled = true - state.onConfigChange() - enabled = false - state.onConfigChange() - assertEquals(2, source.enableDataCaptureCount) - assertEquals(2, source.disableDataCaptureCount) } @Test diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt index 2683920eb0..9ec3582738 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt @@ -26,6 +26,7 @@ import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.unmockkAll +import io.mockk.verify import org.junit.After import org.junit.AfterClass import org.junit.Assert.assertEquals @@ -202,28 +203,6 @@ internal class EmbraceConfigServiceTest { assertFalse(service.anrBehavior.isAnrCaptureEnabled()) } - /** - * Test that calling getConfig() notifies the listener. - * As we are using a DirectExecutor this method will run synchronously and - * return the updated config. - * In a real situation, the async refresh would be triggered and the config returned would be the previous one. - */ - @Test - fun `test getConfig() notifies a listener`() { - // advance the clock so it's safe to retry config refresh - fakeClock.tick(1000000000000) - - // return a different object from default so listener triggers - val newConfig = RemoteConfig(anrConfig = AnrRemoteConfig(sampleIntervalMs = 200)) - every { mockApiService.getConfig() } returns newConfig - fakePreferenceService.sdkDisabled = false - service.addListener(mockConfigListener) - - // call an arbitrary function to trigger a config refresh - service.anrBehavior.isAnrCaptureEnabled() - assertTrue(configListenerTriggered) - } - /** * Test that calling getConfig() refreshes the config and notify the listener * As we are using a DirectExecutor this method will run synchronously and @@ -237,11 +216,9 @@ internal class EmbraceConfigServiceTest { val newConfig = RemoteConfig(anrConfig = AnrRemoteConfig()) every { mockApiService.getConfig() } returns newConfig fakePreferenceService.sdkDisabled = false - service.addListener(mockConfigListener) service.onForeground(true, 1100L) - - assertTrue(configListenerTriggered) + verify(exactly = 2) { mockApiService.getConfig() } } @Test @@ -335,7 +312,6 @@ internal class EmbraceConfigServiceTest { openTelemetryCfg = config, preferencesService = fakePreferenceService, clock = fakeClock, - logger = logger, backgroundWorker = worker, suppliedFramework = AppFramework.NATIVE, foregroundAction = action, diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DataSourceModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DataSourceModuleImplTest.kt index 93e168ba0e..0684ff6269 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DataSourceModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DataSourceModuleImplTest.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.internal.injection -import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule import io.embrace.android.embracesdk.internal.worker.Worker @@ -15,7 +14,6 @@ internal class DataSourceModuleImplTest { val fakeInitModule = FakeInitModule() val module = DataSourceModuleImpl( fakeInitModule, - FakeConfigService(), FakeWorkerThreadModule( fakeInitModule = fakeInitModule, testWorkerName = Worker.Background.NonIoRegWorker diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt index ec6771cae4..bbe7f28480 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt @@ -70,7 +70,6 @@ internal class PayloadFactoryBaTest { configService = FakeConfigService( backgroundActivityCaptureEnabled = true ) - configService.updateListeners() blockingExecutorService = BlockingScheduledExecutorService(blockingMode = false) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactorySessionTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactorySessionTest.kt index 004c0d5ee1..a69b8ba6aa 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactorySessionTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactorySessionTest.kt @@ -92,7 +92,6 @@ internal class PayloadFactorySessionTest { spanRepository = initModule.openTelemetryModule.spanRepository currentSessionSpan = initModule.openTelemetryModule.currentSessionSpan spanService = initModule.openTelemetryModule.spanService - configService.updateListeners() blockingExecutorService = BlockingScheduledExecutorService(blockingMode = false) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt index 67b66e3ea5..abbac13686 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt @@ -14,7 +14,6 @@ import io.embrace.android.embracesdk.internal.injection.SessionOrchestrationModu import io.embrace.android.embracesdk.internal.injection.createDataSourceModule import io.embrace.android.embracesdk.internal.worker.Worker import org.junit.Assert.assertNotNull -import org.junit.Assert.assertTrue import org.junit.Test internal class SessionOrchestrationModuleImplTest { @@ -30,7 +29,6 @@ internal class SessionOrchestrationModuleImplTest { fun testDefaultImplementations() { val dataSourceModule = createDataSourceModule( initModule, - configService, workerThreadModule ) val module = SessionOrchestrationModuleImpl( @@ -48,10 +46,6 @@ internal class SessionOrchestrationModuleImplTest { assertNotNull(module.payloadMessageCollator) assertNotNull(module.payloadFactory) assertNotNull(module.sessionOrchestrator) - assertTrue( - configService.listeners.single().javaClass.toString() - .contains("DataCaptureOrchestrator") - ) } @Test @@ -59,7 +53,6 @@ internal class SessionOrchestrationModuleImplTest { val configModule = createEnabledBehavior() val dataSourceModule = createDataSourceModule( initModule, - configService, workerThreadModule ) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt index 72c2f3c421..c0b4300953 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt @@ -369,7 +369,6 @@ internal class SessionOrchestratorTest { ) fakeDataSource = FakeDataSource(RuntimeEnvironment.getApplication()) dataCaptureOrchestrator = DataCaptureOrchestrator( - configService, fakeBackgroundWorker(), logger ).apply { diff --git a/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/anr/ndk/NativeThreadSamplerInstaller.kt b/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/anr/ndk/NativeThreadSamplerInstaller.kt index 0b5dbeb306..9b12592cb1 100644 --- a/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/anr/ndk/NativeThreadSamplerInstaller.kt +++ b/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/anr/ndk/NativeThreadSamplerInstaller.kt @@ -55,32 +55,12 @@ class NativeThreadSamplerInstaller( if (configService.anrBehavior.isUnityAnrCaptureEnabled()) { monitorCurrentThread(sampler, anrService) } - - // always install the handler. if config subsequently changes we take the decision - // to just ignore anr intervals, rather than attempting to uninstall the handler - configService.addListener { - onConfigChange(configService, sampler, anrService) - } } private fun isMonitoringCurrentThread(): Boolean { return isMonitoring.get() && Thread.currentThread().id == currentThread?.id } - private fun onConfigChange( - configService: ConfigService, - sampler: NativeThreadSamplerService, - anrService: AnrService, - ) { - targetHandler?.post( - Runnable { - if (configService.anrBehavior.isUnityAnrCaptureEnabled() && !isMonitoring.get()) { - monitorCurrentThread(sampler, anrService) - } - } - ) - } - private fun monitorCurrentThread(sampler: NativeThreadSamplerService, anrService: AnrService) { synchronized(this) { if (!isMonitoring.get()) { diff --git a/embrace-android-infra/src/main/kotlin/io/embrace/android/embracesdk/internal/logging/InternalErrorType.kt b/embrace-android-infra/src/main/kotlin/io/embrace/android/embracesdk/internal/logging/InternalErrorType.kt index 6a0cc58b19..e1326504ff 100644 --- a/embrace-android-infra/src/main/kotlin/io/embrace/android/embracesdk/internal/logging/InternalErrorType.kt +++ b/embrace-android-infra/src/main/kotlin/io/embrace/android/embracesdk/internal/logging/InternalErrorType.kt @@ -10,13 +10,11 @@ enum class InternalErrorType { ENABLE_DATA_CAPTURE, NETWORK_STATUS_CAPTURE_FAIL, SCREEN_RES_CAPTURE_FAIL, - CONFIG_LISTENER_FAIL, MEMORY_CLEAN_LISTENER_FAIL, FG_SESSION_CACHE_FAIL, ACTIVITY_LISTENER_FAIL, PROCESS_STATE_CALLBACK_FAIL, ANR_HEARTBEAT_CHECK_FAIL, - CFG_CHANGE_DATA_CAPTURE_FAIL, SESSION_CHANGE_DATA_CAPTURE_FAIL, DATA_SOURCE_DATA_CAPTURE_FAIL, DISK_STAT_CAPTURE_FAIL, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt index c54c185289..4cd612964b 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt @@ -113,8 +113,6 @@ internal class EmbraceInternalInterfaceTest { testRule.runTest( testCaseAction = { recordSession { - clock.tick() - configService.updateListeners() clock.tick() EmbraceInternalApi.getInstance().internalInterface.recordCompletedNetworkRequest( url = URL, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt index ab0ee9df8c..b867a2df33 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt @@ -199,7 +199,6 @@ internal class NetworkRequestApiTest { }, testCaseAction = { recordSession { - configService.updateListeners() clock.tick(5) embrace.recordNetworkRequest( EmbraceNetworkRequest.fromCompletedRequest( @@ -253,7 +252,6 @@ internal class NetworkRequestApiTest { testRule.runTest( testCaseAction = { recordSession { - configService.updateListeners() clock.tick(5) val request = EmbraceNetworkRequest.fromCompletedRequest( @@ -292,7 +290,6 @@ internal class NetworkRequestApiTest { testCaseAction = { recordSession { clock.tick(2L) - configService.updateListeners() clock.tick(5L) embrace.recordNetworkRequest(expectedRequest) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index 1c14221144..e90b048f14 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -226,7 +226,6 @@ internal class ModuleInitBootstrapper( dataSourceModule = init(DataSourceModule::class) { dataSourceModuleSupplier( initModule, - configModule.configService, workerThreadModule ) } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 126689b871..511da68143 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -44,7 +44,7 @@ internal fun fakeModuleInitBootstrapper( storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, - dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _, _ -> FakeDataSourceModule() }, + dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, anrModuleSupplier: AnrModuleSupplier = { _, _, _ -> FakeAnrModule() }, diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt index 022dc87320..728788a17e 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt @@ -19,7 +19,7 @@ import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehav import io.embrace.android.embracesdk.internal.payload.AppFramework /** - * Fake [ConfigService] used for testing. Updates to registered listeners can be triggered by calling [updateListeners]. Note that the + * Fake [ConfigService] used for testing. Note that the * current config values of this object will be propagated, and you can trigger this fake update even if you have not changed the underlying * data. Beware of this difference in implementation compared to the real EmbraceConfigService */ @@ -45,25 +45,10 @@ class FakeConfigService( override var networkSpanForwardingBehavior: NetworkSpanForwardingBehavior = createNetworkSpanForwardingBehavior(), override var sensitiveKeysBehavior: SensitiveKeysBehavior = createSensitiveKeysBehavior(), ) : ConfigService { - override var remoteConfigSource: RemoteConfigSource? = null - - val listeners: MutableSet<() -> Unit> = mutableSetOf() - override fun addListener(configListener: () -> Unit) { - listeners.add(configListener) - } - override fun isSdkDisabled(): Boolean = sdkDisabled || sdkModeBehavior.isSdkDisabled() - override fun isBackgroundActivityCaptureEnabled(): Boolean = backgroundActivityCaptureEnabled - override fun hasValidRemoteConfig(): Boolean = hasValidRemoteConfig override fun isAppExitInfoCaptureEnabled(): Boolean = appExitInfoBehavior.isAeiCaptureEnabled() override fun isOnlyUsingOtelExporters(): Boolean = onlyUsingOtelExporters - - fun updateListeners() { - listeners.forEach { - it() - } - } } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeDataSourceModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeDataSourceModule.kt index 140c571db8..4984a8b3bf 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeDataSourceModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeDataSourceModule.kt @@ -7,7 +7,6 @@ import io.embrace.android.embracesdk.internal.injection.DataSourceModule class FakeDataSourceModule : DataSourceModule { override val dataCaptureOrchestrator: DataCaptureOrchestrator = DataCaptureOrchestrator( - FakeConfigService(), fakeBackgroundWorker(), FakeEmbLogger() ) From 42193e606d29b4d59d2be683486e893ea1472a3a Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Wed, 6 Nov 2024 11:39:59 +0000 Subject: [PATCH 02/28] refactor: avoid shared prefs for config --- .../internal/config/ConfigService.kt | 37 -------------- .../internal/config/EmbraceConfigService.kt | 22 -------- .../prefs/EmbracePreferencesService.kt | 17 ------- .../internal/prefs/PreferencesService.kt | 15 ------ .../session/message/PayloadFactoryImpl.kt | 2 +- .../config/EmbraceConfigServiceTest.kt | 50 +------------------ .../prefs/EmbracePreferencesServiceTest.kt | 23 --------- .../internal/session/PayloadFactoryBaTest.kt | 8 ++- .../session/PayloadFactorySessionTest.kt | 3 +- .../SessionOrchestrationModuleImplTest.kt | 6 ++- .../session/message/PayloadFactoryImplTest.kt | 10 +++- .../orchestrator/SessionOrchestratorTest.kt | 16 ++++-- .../internal/injection/FeatureModuleImpl.kt | 2 +- .../embracesdk/testcases/PublicApiTest.kt | 6 ++- .../embracesdk/testcases/TracingApiTest.kt | 4 +- .../testcases/features/PruningFeatureTest.kt | 4 +- .../features/SessionPropertiesTest.kt | 6 ++- .../session/BackgroundActivityDisabledTest.kt | 4 +- .../actions/EmbraceSetupInterface.kt | 6 ++- .../embrace/android/embracesdk/EmbraceImpl.kt | 2 +- .../injection/ModuleInitBootstrapper.kt | 2 +- .../embracesdk/fakes/FakeConfigService.kt | 7 --- .../embracesdk/fakes/FakePreferenceService.kt | 3 -- 23 files changed, 59 insertions(+), 196 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt index 05a7bd7f0c..6691c0ce66 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt @@ -111,41 +111,4 @@ interface ConfigService { * to Embrace). */ fun isOnlyUsingOtelExporters(): Boolean - - /** - * Checks if the SDK is enabled. - * - * The SDK can be configured to disable a percentage of devices based on the normalization of - * their device ID between 1-100. This threshold is set in [RemoteConfig]. - * - * @return true if the sdk is enabled, false otherwise - */ - fun isSdkDisabled(): Boolean - - /** - * Checks if the capture of background activity is enabled. - * - * - * The background activity capture can be configured to enable a percentage of - * devices based on the normalization of their device ID between 1-100. - * - * @return true if background activity capture is enabled. - */ - fun isBackgroundActivityCaptureEnabled(): Boolean - - /** - * Returns true if the remote config has been fetched and is not expired. Generally speaking - * use of this function should be discouraged - but it can be useful to prevent running risky - * behavior that should only be switched on via remote config. - * - * Most callers will not need this function - try not to abuse it. - */ - fun hasValidRemoteConfig(): Boolean - - /** - * Checks if the capture of Application Exit Info is enabled. - * - * @return true if AEI capture is enabled. - */ - fun isAppExitInfoCaptureEnabled(): Boolean } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt index 1cf435eccb..0dc49bdf38 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt @@ -228,26 +228,9 @@ internal class EmbraceConfigService( val b = newConfig != previousConfig if (b) { configProp = newConfig - persistConfig() } } - private fun persistConfig() { - // TODO: future get rid of these prefs from PrefService entirely? - preferencesService.sdkDisabled = sdkModeBehavior.isSdkDisabled() - preferencesService.backgroundActivityEnabled = - backgroundActivityBehavior.isBackgroundActivityCaptureEnabled() - } - - // TODO: future extract these out to SdkBehavior interface - override fun isSdkDisabled(): Boolean { - return preferencesService.sdkDisabled - } - - override fun isBackgroundActivityCaptureEnabled(): Boolean { - return preferencesService.backgroundActivityEnabled - } - override fun onForeground(coldStart: Boolean, timestamp: Long) { // Refresh the config on resume if it has expired getConfig() @@ -277,11 +260,6 @@ internal class EmbraceConfigService( return clock.now() > lastRefreshConfigAttempt + configRetrySafeWindow * 1000 } - override fun hasValidRemoteConfig(): Boolean = !configRequiresRefresh() - override fun isAppExitInfoCaptureEnabled(): Boolean { - return appExitInfoBehavior.isAeiCaptureEnabled() - } - private companion object { /** diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt index 4b22aaf24b..388f34de76 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt @@ -159,10 +159,6 @@ internal class EmbracePreferencesService( } set(value) = prefs.setStringPreference(DEVICE_IDENTIFIER_KEY, value) - override var sdkDisabled: Boolean - get() = prefs.getBooleanPreference(SDK_DISABLED_KEY, false) - set(value) = prefs.setBooleanPreference(SDK_DISABLED_KEY, value) - override var userPayer: Boolean get() = prefs.getBooleanPreference(USER_IS_PAYER_KEY, false) set(value) = prefs.setBooleanPreference(USER_IS_PAYER_KEY, value) @@ -191,10 +187,6 @@ internal class EmbracePreferencesService( get() = prefs.getLongPreference(SDK_CONFIG_FETCHED_TIMESTAMP) set(value) = prefs.setLongPreference(SDK_CONFIG_FETCHED_TIMESTAMP, value) - override var userMessageNeedsRetry: Boolean - get() = prefs.getBooleanPreference(LAST_USER_MESSAGE_FAILED_KEY, false) - set(value) = prefs.setBooleanPreference(LAST_USER_MESSAGE_FAILED_KEY, value) - override fun incrementAndGetSessionNumber(): Int { return incrementAndGetOrdinal(LAST_SESSION_NUMBER_KEY) } @@ -275,10 +267,6 @@ internal class EmbracePreferencesService( get() = prefs.getStringPreference(SCREEN_RESOLUTION_KEY) set(value) = prefs.setStringPreference(SCREEN_RESOLUTION_KEY, value) - override var backgroundActivityEnabled: Boolean - get() = prefs.getBooleanPreference(BACKGROUND_ACTIVITY_ENABLED_KEY, false) - set(value) = prefs.setBooleanPreference(BACKGROUND_ACTIVITY_ENABLED_KEY, value) - override var applicationExitInfoHistory: Set? get() = prefs.getStringSet(AEI_HASH_CODES, null) set(value) = prefs.setArrayPreference(AEI_HASH_CODES, value) @@ -317,8 +305,6 @@ internal class EmbracePreferencesService( } companion object { - internal const val SDK_STARTUP_IN_PROGRESS = "startup_entered" - internal const val SDK_STARTUP_COMPLETED = "startup_completed" private const val DEVICE_IDENTIFIER_KEY = "io.embrace.deviceid" private const val PREVIOUS_APP_VERSION_KEY = "io.embrace.lastappversion" private const val PREVIOUS_OS_VERSION_KEY = "io.embrace.lastosversion" @@ -328,7 +314,6 @@ internal class EmbracePreferencesService( private const val USER_USERNAME_KEY = "io.embrace.username" private const val USER_IS_PAYER_KEY = "io.embrace.userispayer" private const val USER_PERSONAS_KEY = "io.embrace.userpersonas" - private const val LAST_USER_MESSAGE_FAILED_KEY = "io.embrace.userupdatefailed" private const val LAST_SESSION_NUMBER_KEY = "io.embrace.sessionnumber" private const val LAST_BACKGROUND_ACTIVITY_NUMBER_KEY = "io.embrace.bgactivitynumber" private const val LAST_CRASH_NUMBER_KEY = "io.embrace.crashnumber" @@ -346,9 +331,7 @@ internal class EmbracePreferencesService( private const val EMBRACE_FLUTTER_SDK_VERSION_KEY = "io.embrace.flutter.sdk.version" private const val IS_JAILBROKEN_KEY = "io.embrace.is_jailbroken" private const val SCREEN_RESOLUTION_KEY = "io.embrace.screen.resolution" - private const val BACKGROUND_ACTIVITY_ENABLED_KEY = "io.embrace.bgactivitycapture" private const val NETWORK_CAPTURE_RULE_PREFIX_KEY = "io.embrace.networkcapturerule" - private const val SDK_DISABLED_KEY = "io.embrace.disabled" private const val SDK_CONFIG_FETCHED_TIMESTAMP = "io.embrace.sdkfetchedtimestamp" private const val AEI_HASH_CODES = "io.embrace.aeiHashCode" private const val CPU_NAME_KEY = "io.embrace.cpuName" diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/PreferencesService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/PreferencesService.kt index f8a147bf55..d90fcd4bf4 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/PreferencesService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/PreferencesService.kt @@ -22,11 +22,6 @@ interface PreferencesService { */ var deviceIdentifier: String - /** - * If the sdk is disabled - */ - var sdkDisabled: Boolean - /** * If the user is payer */ @@ -62,11 +57,6 @@ interface PreferencesService { */ var lastConfigFetchDate: Long? - /** - * If the user message needs to retry send - */ - var userMessageNeedsRetry: Boolean - /** * Increments and returns the session number ordinal. This is an integer that increments * at the start of every session. This allows us to check the % of sessions that didn't get @@ -165,11 +155,6 @@ interface PreferencesService { */ var egl: String? - /** - * If background activity capture is enabled - */ - var backgroundActivityEnabled: Boolean - /** * Set of hashcodes derived from ApplicationExitInfo objects */ diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImpl.kt index 5ae5f5d991..f627c491ce 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImpl.kt @@ -195,5 +195,5 @@ internal class PayloadFactoryImpl( ) } - private fun isBackgroundActivityEnabled(): Boolean = configService.isBackgroundActivityCaptureEnabled() + private fun isBackgroundActivityEnabled(): Boolean = configService.backgroundActivityBehavior.isBackgroundActivityCaptureEnabled() } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt index 9ec3582738..a49b99e2fc 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.internal.config -import io.embrace.android.embracesdk.concurrency.BlockingScheduledExecutorService import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeLogRecordExporter import io.embrace.android.embracesdk.fakes.FakePreferenceService @@ -204,7 +203,7 @@ internal class EmbraceConfigServiceTest { } /** - * Test that calling getConfig() refreshes the config and notify the listener + * Test that calling getConfig() refreshes the config * As we are using a DirectExecutor this method will run synchronously and * return the updated config. * In a real situation, the async refresh would be triggered and the config returned would be the previous one. @@ -215,58 +214,11 @@ internal class EmbraceConfigServiceTest { fakeClock.tick(1000000000000) val newConfig = RemoteConfig(anrConfig = AnrRemoteConfig()) every { mockApiService.getConfig() } returns newConfig - fakePreferenceService.sdkDisabled = false service.onForeground(true, 1100L) verify(exactly = 2) { mockApiService.getConfig() } } - @Test - fun `test onForeground() with sdk started and config sdkDisabled=true stops the SDK`() { - var stopped = false - service = createService(worker, action = { - stopped = true - }) - fakePreferenceService.sdkDisabled = true - service.onForeground(true, 1100L) - assertTrue(stopped) - } - - @Test - fun `test isSdkDisabled returns true`() { - fakePreferenceService.sdkDisabled = true - assertTrue(service.isSdkDisabled()) - } - - @Test - fun `test isSdkDisabled returns false`() { - fakePreferenceService.sdkDisabled = false - assertFalse(service.isSdkDisabled()) - } - - @Test - fun `Update listeners when cached config is loaded`() { - // Use ExecutorService that requires tasks to be explicitly run. This allows us to simulate the case - // when the loading from the cache doesn't run before the config is read. - - val executorService = BlockingScheduledExecutorService(blockingMode = true) - - every { mockApiService.getCachedConfig() } returns CachedConfig(null, null) - - // Create a new instance of the ConfigService where the value of the config is what it is when the config - // variable is initialized, before the cached version is loaded. - val configService = createService(BackgroundWorker(executorService)) - assertFalse(configService.hasValidRemoteConfig()) - - // call arbitrary function to trigger config refresh - configService.anrBehavior.isAnrCaptureEnabled() - - // Only run the task from the executor that loads the cached config to the ConfigService so the call to fetch - // a new config from the server isn't run - executorService.runCurrentlyBlocked() - assertTrue(configService.hasValidRemoteConfig()) - } - @Test fun `test app framework`() { assertEquals(AppFramework.NATIVE, service.appFramework) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt index 20529e7d6f..1d53f3a400 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt @@ -84,13 +84,6 @@ internal class EmbracePreferencesServiceTest { assertNotNull(service.deviceIdentifier) } - @Test - fun `test sdk disabled is saved`() { - assertFalse(service.sdkDisabled) - service.sdkDisabled = true - assertTrue(service.sdkDisabled) - } - @Test fun `test user payer is saved`() { assertFalse(service.userPayer) @@ -156,13 +149,6 @@ internal class EmbracePreferencesServiceTest { assertEquals(1234L, service.lastConfigFetchDate) } - @Test - fun `test user message needs retry is saved`() { - assertFalse(service.userMessageNeedsRetry) - service.userMessageNeedsRetry = true - assertTrue(service.userMessageNeedsRetry) - } - @Test fun `test session number is saved`() { assertEquals(1, service.incrementAndGetSessionNumber()) @@ -317,15 +303,6 @@ internal class EmbracePreferencesServiceTest { assertEquals(version, service.embraceFlutterSdkVersion) } - @Test - fun `test background activity enabled is saved`() { - assertFalse(service.backgroundActivityEnabled) - - val expected = true - service.backgroundActivityEnabled = true - assertEquals(expected, service.backgroundActivityEnabled) - } - @Test fun `test is users first day`() { assertFalse(service.isUsersFirstDay()) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt index bbe7f28480..9646eba7b8 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt @@ -14,10 +14,12 @@ import io.embrace.android.embracesdk.fakes.FakePreferenceService import io.embrace.android.embracesdk.fakes.FakeProcessStateService import io.embrace.android.embracesdk.fakes.FakeSessionIdTracker import io.embrace.android.embracesdk.fakes.FakeUserService +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.fakeSessionZygote import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.capture.metadata.MetadataService import io.embrace.android.embracesdk.internal.capture.user.UserService +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.envelope.session.SessionEnvelopeSourceImpl import io.embrace.android.embracesdk.internal.envelope.session.SessionPayloadSourceImpl import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl @@ -60,7 +62,7 @@ internal class PayloadFactoryBaTest { activityService = FakeProcessStateService(isInBackground = true) deliveryService = FakeDeliveryService() ndkService = FakeNdkService() - preferencesService = FakePreferenceService(backgroundActivityEnabled = true) + preferencesService = FakePreferenceService() userService = FakeUserService() val initModule = FakeInitModule(clock = clock) spanRepository = initModule.openTelemetryModule.spanRepository @@ -68,7 +70,9 @@ internal class PayloadFactoryBaTest { currentSessionSpan = initModule.openTelemetryModule.currentSessionSpan spanService = initModule.openTelemetryModule.spanService configService = FakeConfigService( - backgroundActivityCaptureEnabled = true + backgroundActivityBehavior = createBackgroundActivityBehavior { + BackgroundActivityRemoteConfig(threshold = 100f) + } ) blockingExecutorService = BlockingScheduledExecutorService(blockingMode = false) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactorySessionTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactorySessionTest.kt index a69b8ba6aa..a319b0dd5c 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactorySessionTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactorySessionTest.kt @@ -60,7 +60,6 @@ internal class PayloadFactorySessionTest { companion object { private val processStateService = FakeProcessStateService() - private val clock = FakeClock() @BeforeClass @JvmStatic @@ -86,7 +85,7 @@ internal class PayloadFactorySessionTest { sessionIdTracker = FakeSessionIdTracker() activityService = FakeProcessStateService(isInBackground = true) ndkService = FakeNdkService() - preferencesService = FakePreferenceService(backgroundActivityEnabled = true) + preferencesService = FakePreferenceService() userService = FakeUserService() val initModule = FakeInitModule(clock = clock) spanRepository = initModule.openTelemetryModule.spanRepository diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt index abbac13686..b60f3cf77d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt @@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.internal.session import io.embrace.android.embracesdk.fakes.FakeConfigModule import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeStartupService +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.injection.FakeAndroidServicesModule import io.embrace.android.embracesdk.fakes.injection.FakeDeliveryModule import io.embrace.android.embracesdk.fakes.injection.FakeEssentialServiceModule @@ -10,6 +11,7 @@ import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeLogModule import io.embrace.android.embracesdk.fakes.injection.FakePayloadSourceModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.injection.SessionOrchestrationModuleImpl import io.embrace.android.embracesdk.internal.injection.createDataSourceModule import io.embrace.android.embracesdk.internal.worker.Worker @@ -76,7 +78,9 @@ internal class SessionOrchestrationModuleImplTest { private fun createEnabledBehavior(): FakeConfigModule { return FakeConfigModule( configService = FakeConfigService( - backgroundActivityCaptureEnabled = true + backgroundActivityBehavior = createBackgroundActivityBehavior { + BackgroundActivityRemoteConfig(threshold = 100f) + }, ) ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImplTest.kt index df0566353a..3f0602e8f1 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImplTest.kt @@ -6,7 +6,9 @@ import io.embrace.android.embracesdk.fakes.FakeEnvelopeResourceSource import io.embrace.android.embracesdk.fakes.FakeGatingService import io.embrace.android.embracesdk.fakes.FakePreferenceService import io.embrace.android.embracesdk.fakes.FakeSessionPayloadSource +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.injection.FakeInitModule +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.envelope.session.SessionEnvelopeSourceImpl import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessState import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessState.BACKGROUND @@ -54,7 +56,9 @@ internal class PayloadFactoryImplTest { @Test fun `verify expected payloads with ba enabled`() { - configService.backgroundActivityCaptureEnabled = true + configService.backgroundActivityBehavior = createBackgroundActivityBehavior { + BackgroundActivityRemoteConfig(threshold = 100f) + } verifyPayloadWithState(state = FOREGROUND, zygoteCreated = true, startNewSession = true) verifyPayloadWithState(state = BACKGROUND, zygoteCreated = true, startNewSession = true) verifyPayloadWithManual() @@ -62,7 +66,9 @@ internal class PayloadFactoryImplTest { @Test fun `verify expected payloads with ba disabled`() { - configService.backgroundActivityCaptureEnabled = false + configService.backgroundActivityBehavior = createBackgroundActivityBehavior { + BackgroundActivityRemoteConfig(threshold = 0f) + } verifyPayloadWithState(state = FOREGROUND, zygoteCreated = true, startNewSession = false) verifyPayloadWithState(state = BACKGROUND, zygoteCreated = false, startNewSession = false) verifyPayloadWithManual() diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt index c0b4300953..9b93b34c55 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt @@ -17,11 +17,13 @@ import io.embrace.android.embracesdk.fakes.FakeStartupService import io.embrace.android.embracesdk.fakes.FakeUserService import io.embrace.android.embracesdk.fakes.FakeV2PayloadCollator import io.embrace.android.embracesdk.fakes.behavior.FakeSessionBehavior +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.fakeBackgroundWorker import io.embrace.android.embracesdk.internal.arch.DataCaptureOrchestrator import io.embrace.android.embracesdk.internal.arch.datasource.DataSourceState import io.embrace.android.embracesdk.internal.capture.session.SessionPropertiesService import io.embrace.android.embracesdk.internal.clock.nanosToMillis +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.delivery.caching.PayloadCachingService import io.embrace.android.embracesdk.internal.delivery.caching.PayloadCachingServiceImpl import io.embrace.android.embracesdk.internal.logging.EmbLogger @@ -65,7 +67,11 @@ internal class SessionOrchestratorTest { fun setUp() { clock = FakeClock() logger = EmbLoggerImpl() - configService = FakeConfigService(backgroundActivityCaptureEnabled = true) + configService = FakeConfigService( + backgroundActivityBehavior = createBackgroundActivityBehavior { + BackgroundActivityRemoteConfig(threshold = 100f) + } + ) } @Test @@ -254,7 +260,9 @@ internal class SessionOrchestratorTest { @Test fun `end with crash in background`() { configService = FakeConfigService( - backgroundActivityCaptureEnabled = true, + backgroundActivityBehavior = createBackgroundActivityBehavior { + BackgroundActivityRemoteConfig(threshold = 100f) + } ) createOrchestrator(true) orchestrator.handleCrash("crashId") @@ -264,7 +272,9 @@ internal class SessionOrchestratorTest { @Test fun `end with crash in foreground`() { configService = FakeConfigService( - backgroundActivityCaptureEnabled = true, + backgroundActivityBehavior = createBackgroundActivityBehavior { + BackgroundActivityRemoteConfig(threshold = 100f) + } ) createOrchestrator(false) orchestrator.handleCrash("crashId") diff --git a/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/FeatureModuleImpl.kt b/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/FeatureModuleImpl.kt index adc2bde58d..f208561ece 100644 --- a/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/FeatureModuleImpl.kt +++ b/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/FeatureModuleImpl.kt @@ -198,7 +198,7 @@ internal class FeatureModuleImpl( override val applicationExitInfoDataSource: DataSourceState by dataSourceState { DataSourceState( factory = { aeiService }, - configGate = { configService.isAppExitInfoCaptureEnabled() } + configGate = { configService.appExitInfoBehavior.isAeiCaptureEnabled() } ) } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt index 440f82ff68..142d57d5b1 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt @@ -3,6 +3,8 @@ package io.embrace.android.embracesdk.testcases import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.LastRunEndState import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkSpanForwardingBehavior +import io.embrace.android.embracesdk.fakes.createSdkModeBehavior +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.testframework.IntegrationTestRule import io.embrace.android.embracesdk.testframework.actions.EmbraceSetupInterface @@ -43,7 +45,7 @@ internal class PublicApiTest { }, testCaseAction = { assertEquals(AppFramework.NATIVE, configService.appFramework) - assertFalse(configService.isSdkDisabled()) + assertFalse(configService.sdkModeBehavior.isSdkDisabled()) assertTrue(embrace.isStarted) } ) @@ -64,7 +66,7 @@ internal class PublicApiTest { testRule.runTest( expectSdkToStart = false, setupAction = { - overriddenConfigService.sdkDisabled = true + overriddenConfigService.sdkModeBehavior = createSdkModeBehavior { RemoteConfig(0) } }, testCaseAction = { assertFalse(embrace.isStarted) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt index 0a6fb25a0e..5ba8ae6103 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt @@ -5,10 +5,12 @@ import io.embrace.android.embracesdk.arch.assertIsTypePerformance import io.embrace.android.embracesdk.assertions.assertEmbraceSpanData import io.embrace.android.embracesdk.concurrency.SingleThreadTestScheduledExecutor import io.embrace.android.embracesdk.fakes.FakeSpanExporter +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fixtures.TOO_LONG_ATTRIBUTE_KEY import io.embrace.android.embracesdk.fixtures.TOO_LONG_ATTRIBUTE_VALUE import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.clock.millisToNanos +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.payload.ApplicationState import io.embrace.android.embracesdk.internal.payload.Attribute import io.embrace.android.embracesdk.internal.payload.Span @@ -321,7 +323,7 @@ internal class TracingApiTest { fun `can only create span if there is a valid session`() { testRule.runTest( setupAction = { - overriddenConfigService.backgroundActivityCaptureEnabled = false + overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } }, preSdkStartAction = { assertNull(embrace.startSpan("test")) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/PruningFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/PruningFeatureTest.kt index 191648bd9e..48ece0206d 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/PruningFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/PruningFeatureTest.kt @@ -4,9 +4,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeEmbLogger +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fixtures.fakeSessionStoredTelemetryMetadata import io.embrace.android.embracesdk.internal.comms.delivery.NetworkStatus +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.delivery.StoredTelemetryMetadata import io.embrace.android.embracesdk.internal.spans.findAttributeValue import io.embrace.android.embracesdk.internal.worker.Worker.Priority.DataPersistenceWorker @@ -41,7 +43,7 @@ internal class PruningFeatureTest { fun `stored payloads are pruned appropriately`() { testRule.runTest( setupAction = { - overriddenConfigService.backgroundActivityCaptureEnabled = false + overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } }, testCaseAction = { simulateNetworkChange(NetworkStatus.NOT_REACHABLE) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SessionPropertiesTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SessionPropertiesTest.kt index 3639692c08..46ad746650 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SessionPropertiesTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SessionPropertiesTest.kt @@ -3,6 +3,8 @@ package io.embrace.android.embracesdk.testcases.features import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.Embrace import io.embrace.android.embracesdk.assertions.findSessionSpan +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.payload.ApplicationState import io.embrace.android.embracesdk.internal.payload.Span import io.embrace.android.embracesdk.internal.spans.getSessionProperty @@ -67,7 +69,7 @@ internal class SessionPropertiesTest { fun `session properties work with background activity disabled`() { testRule.runTest( setupAction = { - overriddenConfigService.backgroundActivityCaptureEnabled = false + overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } setupPermanentProperties() }, testCaseAction = { @@ -114,7 +116,7 @@ internal class SessionPropertiesTest { fun `session properties are persisted in cached payloads when bg activities are disabled`() { testRule.runTest( setupAction = { - overriddenConfigService.backgroundActivityCaptureEnabled = false + overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } setupPermanentProperties() }, testCaseAction = { diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityDisabledTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityDisabledTest.kt index 208ed62c62..ee2037f498 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityDisabledTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityDisabledTest.kt @@ -5,10 +5,12 @@ import io.embrace.android.embracesdk.assertions.findEventsOfType import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.assertions.getSessionId import io.embrace.android.embracesdk.fakes.FakeClock +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.clock.nanosToMillis +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.embCleanExit import io.embrace.android.embracesdk.internal.opentelemetry.embColdStart import io.embrace.android.embracesdk.internal.opentelemetry.embProcessIdentifier @@ -57,7 +59,7 @@ internal class BackgroundActivityDisabledTest { overriddenInitModule = initModule, overriddenWorkerThreadModule = workerThreadModule, ).apply { - overriddenConfigService.backgroundActivityCaptureEnabled = false + overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } } } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index dc5b149aae..c05eb4b444 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -14,10 +14,12 @@ import io.embrace.android.embracesdk.fakes.FakeNetworkConnectivityService import io.embrace.android.embracesdk.fakes.FakeRequestExecutionService import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkSpanForwardingBehavior +import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.injection.FakeAnrModule import io.embrace.android.embracesdk.fakes.injection.FakeCoreModule import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeNativeCoreModule +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.delivery.storage.PayloadStorageService import io.embrace.android.embracesdk.internal.injection.AndroidServicesModule import io.embrace.android.embracesdk.internal.injection.AnrModule @@ -44,9 +46,9 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( val overriddenOpenTelemetryModule: OpenTelemetryModule = overriddenInitModule.openTelemetryModule, val overriddenCoreModule: FakeCoreModule = FakeCoreModule(), val overriddenConfigService: FakeConfigService = FakeConfigService( - backgroundActivityCaptureEnabled = true, networkSpanForwardingBehavior = FakeNetworkSpanForwardingBehavior(true), - autoDataCaptureBehavior = FakeAutoDataCaptureBehavior(thermalStatusCaptureEnabled = false) + autoDataCaptureBehavior = FakeAutoDataCaptureBehavior(thermalStatusCaptureEnabled = false), + backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 100f) } ), val overriddenWorkerThreadModule: WorkerThreadModule = createWorkerThreadModule(), val overriddenAndroidServicesModule: AndroidServicesModule = createAndroidServicesModule( diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt index 64b1252f71..382ff57672 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt @@ -174,7 +174,7 @@ internal class EmbraceImpl @JvmOverloads constructor( application = coreModule.application val configModule = bootstrapper.configModule - if (configModule.configService.isSdkDisabled()) { + if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { stop() return } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index e90b048f14..8bf96ae957 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -158,7 +158,7 @@ internal class ModuleInitBootstrapper( appFramework, configServiceProvider, ) { - if (isSdkDisabled()) { + if (sdkModeBehavior.isSdkDisabled()) { EmbraceInternalApi.getInstance().internalInterface.stopSdk() } } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt index 728788a17e..afab00c2e7 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt @@ -26,10 +26,7 @@ import io.embrace.android.embracesdk.internal.payload.AppFramework class FakeConfigService( override var appFramework: AppFramework = AppFramework.NATIVE, override var appId: String = "abcde", - var sdkDisabled: Boolean = false, - var backgroundActivityCaptureEnabled: Boolean = false, var onlyUsingOtelExporters: Boolean = false, - private var hasValidRemoteConfig: Boolean = false, override var backgroundActivityBehavior: BackgroundActivityBehavior = createBackgroundActivityBehavior(), override var autoDataCaptureBehavior: AutoDataCaptureBehavior = createAutoDataCaptureBehavior(), override var breadcrumbBehavior: BreadcrumbBehavior = FakeBreadcrumbBehavior(), @@ -46,9 +43,5 @@ class FakeConfigService( override var sensitiveKeysBehavior: SensitiveKeysBehavior = createSensitiveKeysBehavior(), ) : ConfigService { override var remoteConfigSource: RemoteConfigSource? = null - override fun isSdkDisabled(): Boolean = sdkDisabled || sdkModeBehavior.isSdkDisabled() - override fun isBackgroundActivityCaptureEnabled(): Boolean = backgroundActivityCaptureEnabled - override fun hasValidRemoteConfig(): Boolean = hasValidRemoteConfig - override fun isAppExitInfoCaptureEnabled(): Boolean = appExitInfoBehavior.isAeiCaptureEnabled() override fun isOnlyUsingOtelExporters(): Boolean = onlyUsingOtelExporters } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakePreferenceService.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakePreferenceService.kt index 87c0174bb0..eba2b91487 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakePreferenceService.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakePreferenceService.kt @@ -8,7 +8,6 @@ class FakePreferenceService( override var osVersion: String? = null, override var installDate: Long? = 0, override var deviceIdentifier: String = "", - override var sdkDisabled: Boolean = false, override var userPayer: Boolean = false, override var userIdentifier: String? = null, override var userEmailAddress: String? = null, @@ -16,13 +15,11 @@ class FakePreferenceService( override var username: String? = null, override var permanentSessionProperties: Map? = null, override var lastConfigFetchDate: Long? = null, - override var userMessageNeedsRetry: Boolean = false, override var reactNativeVersionNumber: String? = null, override var unityVersionNumber: String? = null, override var unityBuildIdNumber: String? = null, override var unitySdkVersionNumber: String? = null, override var screenResolution: String? = null, - override var backgroundActivityEnabled: Boolean = false, override var dartSdkVersion: String? = null, override var javaScriptBundleURL: String? = null, override var javaScriptBundleId: String? = null, From 5b3668378e4e2d129f0d6bda39e95872f5343154 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Wed, 6 Nov 2024 17:22:56 +0000 Subject: [PATCH 03/28] refactor: extract config loading --- .../internal/comms/api/ApiService.kt | 4 +- .../config/CachedRemoteConfigSource.kt | 8 + .../internal/config/ConfigService.kt | 2 - .../internal/config/EmbraceConfigService.kt | 165 ++---------------- .../internal/config/RemoteConfigSource.kt | 3 - .../internal/config/RemoteConfigSourceImpl.kt | 142 +++++++++++++++ .../internal/injection/ConfigModule.kt | 2 + .../internal/injection/ConfigModuleImpl.kt | 27 +-- .../injection/ConfigModuleSupplier.kt | 8 +- .../config/EmbraceConfigServiceTest.kt | 145 +++++++-------- .../injection/ConfigModuleImplTest.kt | 19 -- .../testframework/IntegrationTestRule.kt | 15 +- .../actions/EmbraceSetupInterface.kt | 7 + .../embrace/android/embracesdk/EmbraceImpl.kt | 31 +--- .../injection/ModuleInitBootstrapper.kt | 11 +- .../fakes/FakeModuleInitBootstrapper.kt | 2 +- .../injection/ModuleInitBootstrapperTest.kt | 2 +- .../embracesdk/fakes/FakeConfigModule.kt | 2 + .../embracesdk/fakes/FakeConfigService.kt | 2 - .../fakes/FakeRemoteConfigSource.kt | 10 ++ 20 files changed, 291 insertions(+), 316 deletions(-) create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/CachedRemoteConfigSource.kt create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSourceImpl.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt index e0269036ba..9c619c2733 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt @@ -1,13 +1,13 @@ package io.embrace.android.embracesdk.internal.comms.api import io.embrace.android.embracesdk.internal.capture.connectivity.NetworkConnectivityListener -import io.embrace.android.embracesdk.internal.config.RemoteConfigSource +import io.embrace.android.embracesdk.internal.config.CachedRemoteConfigSource import io.embrace.android.embracesdk.internal.injection.SerializationAction import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.LogPayload import java.util.concurrent.Future -interface ApiService : RemoteConfigSource, NetworkConnectivityListener { +interface ApiService : CachedRemoteConfigSource, NetworkConnectivityListener { /** * Sends a list of OTel Logs to the API. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/CachedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/CachedRemoteConfigSource.kt new file mode 100644 index 0000000000..7e775eb406 --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/CachedRemoteConfigSource.kt @@ -0,0 +1,8 @@ +package io.embrace.android.embracesdk.internal.config + +import io.embrace.android.embracesdk.internal.comms.api.CachedConfig + +interface CachedRemoteConfigSource : RemoteConfigSource { + + fun getCachedConfig(): CachedConfig +} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt index 6691c0ce66..486e237c57 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt @@ -23,8 +23,6 @@ import io.embrace.android.embracesdk.internal.payload.AppFramework */ interface ConfigService { - var remoteConfigSource: RemoteConfigSource? - /** * How background activity functionality should behave. */ diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt index 0dc49bdf38..e299845dd5 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.internal.config -import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.internal.config.behavior.AnrBehavior import io.embrace.android.embracesdk.internal.config.behavior.AnrBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.AppExitInfoBehavior @@ -35,64 +34,36 @@ import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.OpenTelemetryConfiguration import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.prefs.PreferencesService -import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessStateListener import io.embrace.android.embracesdk.internal.utils.Provider -import io.embrace.android.embracesdk.internal.worker.BackgroundWorker -import kotlin.math.min /** * Loads configuration for the app from the Embrace API. */ internal class EmbraceConfigService( openTelemetryCfg: OpenTelemetryConfiguration, - private val preferencesService: PreferencesService, - private val clock: Clock, - private val backgroundWorker: BackgroundWorker, + preferencesService: PreferencesService, suppliedFramework: AppFramework, - private val foregroundAction: ConfigService.() -> Unit, appIdFromConfig: String?, - val thresholdCheck: BehaviorThresholdCheck = - BehaviorThresholdCheck { preferencesService.deviceIdentifier }, -) : ConfigService, ProcessStateListener { - - private val lock = Any() - override var remoteConfigSource: RemoteConfigSource? = null - set(value) { - field = value - loadConfigFromCache() - attemptConfigRefresh() - } - - @Volatile - private var configProp = RemoteConfig() - - @Volatile - var lastUpdated: Long = 0 - - @Volatile - private var lastRefreshConfigAttempt: Long = 0 - - @Volatile - private var configRetrySafeWindow = DEFAULT_RETRY_WAIT_TIME.toDouble() - - private val remoteSupplier: Provider = { getConfig() } + configProvider: Provider, + thresholdCheck: BehaviorThresholdCheck = BehaviorThresholdCheck { preferencesService.deviceIdentifier }, +) : ConfigService { override val backgroundActivityBehavior: BackgroundActivityBehavior = BackgroundActivityBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = { getConfig().backgroundActivityConfig } + remoteSupplier = { configProvider()?.backgroundActivityConfig } ) override val autoDataCaptureBehavior: AutoDataCaptureBehavior = AutoDataCaptureBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier + remoteSupplier = configProvider ) override val breadcrumbBehavior: BreadcrumbBehavior = BreadcrumbBehaviorImpl( thresholdCheck, - remoteSupplier = remoteSupplier + remoteSupplier = configProvider ) override val sensitiveKeysBehavior: SensitiveKeysBehavior = SensitiveKeysBehaviorImpl() @@ -100,34 +71,34 @@ internal class EmbraceConfigService( override val logMessageBehavior: LogMessageBehavior = LogMessageBehaviorImpl( thresholdCheck, - remoteSupplier = { getConfig().logConfig } + remoteSupplier = { configProvider()?.logConfig } ) override val anrBehavior: AnrBehavior = AnrBehaviorImpl( thresholdCheck, - remoteSupplier = { getConfig().anrConfig } + remoteSupplier = { configProvider()?.anrConfig } ) override val sessionBehavior: SessionBehavior = SessionBehaviorImpl( thresholdCheck, - remoteSupplier = { getConfig() } + remoteSupplier = configProvider ) override val networkBehavior: NetworkBehavior = NetworkBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier + remoteSupplier = configProvider ) override val dataCaptureEventBehavior: DataCaptureEventBehavior = DataCaptureEventBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier + remoteSupplier = configProvider ) override val sdkModeBehavior: SdkModeBehavior = SdkModeBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier + remoteSupplier = configProvider ) override val sdkEndpointBehavior: SdkEndpointBehavior = SdkEndpointBehaviorImpl( @@ -136,19 +107,19 @@ internal class EmbraceConfigService( override val appExitInfoBehavior: AppExitInfoBehavior = AppExitInfoBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier + remoteSupplier = configProvider ) override val networkSpanForwardingBehavior: NetworkSpanForwardingBehavior = NetworkSpanForwardingBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = { getConfig().networkSpanForwardingRemoteConfig } + remoteSupplier = { configProvider()?.networkSpanForwardingRemoteConfig } ) override val webViewVitalsBehavior: WebViewVitalsBehavior = WebViewVitalsBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier + remoteSupplier = configProvider ) override val appId: String? = resolveAppId(appIdFromConfig, openTelemetryCfg) @@ -170,111 +141,7 @@ internal class EmbraceConfigService( return id } - /** - * Load Config from cache if present. - */ - fun loadConfigFromCache() { - val cachedConfig = remoteConfigSource?.getCachedConfig() - val obj = cachedConfig?.remoteConfig - - if (obj != null) { - val oldConfig = configProp - updateConfig(oldConfig, obj) - } - } - - private fun getConfig(): RemoteConfig { - attemptConfigRefresh() - return configProp - } - - private fun attemptConfigRefresh() { - if (configRequiresRefresh() && configRetryIsSafe()) { - synchronized(lock) { - if (configRequiresRefresh() && configRetryIsSafe()) { - lastRefreshConfigAttempt = clock.now() - // Attempt to asynchronously update the config if it is out of date - refreshConfig() - } - } - } - } - - private fun refreshConfig() { - val previousConfig = configProp - backgroundWorker.submit { - // Ensure that another thread didn't refresh it already in the meantime - if (configRequiresRefresh()) { - try { - lastRefreshConfigAttempt = clock.now() - val newConfig = remoteConfigSource?.getConfig() - if (newConfig != null) { - updateConfig(previousConfig, newConfig) - lastUpdated = clock.now() - } - configRetrySafeWindow = DEFAULT_RETRY_WAIT_TIME.toDouble() - } catch (ex: Exception) { - configRetrySafeWindow = - min( - MAX_ALLOWED_RETRY_WAIT_TIME.toDouble(), - configRetrySafeWindow * 2 - ) - } - } - } - } - - private fun updateConfig(previousConfig: RemoteConfig, newConfig: RemoteConfig) { - val b = newConfig != previousConfig - if (b) { - configProp = newConfig - } - } - - override fun onForeground(coldStart: Boolean, timestamp: Long) { - // Refresh the config on resume if it has expired - getConfig() - foregroundAction() - } - override val appFramework: AppFramework = InstrumentedConfig.project.getAppFramework()?.let { AppFramework.fromString(it) } ?: suppliedFramework - - /** - * Checks if the time diff since the last fetch exceeds the - * [EmbraceConfigService.CONFIG_TTL] millis. - * - * @return if the config requires to be fetched from the remote server again or not. - */ - private fun configRequiresRefresh(): Boolean { - return clock.now() - lastUpdated > CONFIG_TTL - } - - /** - * Checks if the time diff since the last attempt is enough to try again. - * - * @return if the config can be fetched from the remote server again or not. - */ - private fun configRetryIsSafe(): Boolean { - return clock.now() > lastRefreshConfigAttempt + configRetrySafeWindow * 1000 - } - - private companion object { - - /** - * Config lives for 1 hour before attempting to retrieve again. - */ - private const val CONFIG_TTL = 60 * 60 * 1000L - - /** - * Config refresh default retry period. - */ - private const val DEFAULT_RETRY_WAIT_TIME: Long = 20 // 20 seconds - - /** - * Config max allowed refresh retry period. - */ - private const val MAX_ALLOWED_RETRY_WAIT_TIME: Long = 300 // 5 minutes - } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSource.kt index 2fd1aadad5..f75ab62132 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSource.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.internal.config -import io.embrace.android.embracesdk.internal.comms.api.CachedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig interface RemoteConfigSource { @@ -14,6 +13,4 @@ interface RemoteConfigSource { * @return a future containing the configuration. */ fun getConfig(): RemoteConfig? - - fun getCachedConfig(): CachedConfig } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSourceImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSourceImpl.kt new file mode 100644 index 0000000000..973284a799 --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSourceImpl.kt @@ -0,0 +1,142 @@ +package io.embrace.android.embracesdk.internal.config + +import io.embrace.android.embracesdk.internal.clock.Clock +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessStateListener +import io.embrace.android.embracesdk.internal.worker.BackgroundWorker +import kotlin.math.min + +/** + * Loads configuration for the app from the Embrace API. + */ +class RemoteConfigSourceImpl( + private val clock: Clock, + private val backgroundWorker: BackgroundWorker, + private val foregroundAction: () -> Unit, +) : RemoteConfigSource, ProcessStateListener { + + private val lock = Any() + + var remoteConfigSource: CachedRemoteConfigSource? = null + set(value) { + field = value + loadConfigFromCache() + attemptConfigRefresh() + } + + @Volatile + private var configProp = RemoteConfig() + + @Volatile + var lastUpdated: Long = 0 + + @Volatile + private var lastRefreshConfigAttempt: Long = 0 + + @Volatile + private var configRetrySafeWindow = DEFAULT_RETRY_WAIT_TIME.toDouble() + + override fun getConfig(): RemoteConfig { + attemptConfigRefresh() + return configProp + } + + /** + * Load Config from cache if present. + */ + internal fun loadConfigFromCache() { + val cachedConfig = remoteConfigSource?.getCachedConfig() + val obj = cachedConfig?.remoteConfig + + if (obj != null) { + val oldConfig = configProp + updateConfig(oldConfig, obj) + } + } + + private fun attemptConfigRefresh() { + if (configRequiresRefresh() && configRetryIsSafe()) { + synchronized(lock) { + if (configRequiresRefresh() && configRetryIsSafe()) { + lastRefreshConfigAttempt = clock.now() + // Attempt to asynchronously update the config if it is out of date + refreshConfig() + } + } + } + } + + private fun refreshConfig() { + val previousConfig = configProp + backgroundWorker.submit { + // Ensure that another thread didn't refresh it already in the meantime + if (configRequiresRefresh()) { + try { + lastRefreshConfigAttempt = clock.now() + val newConfig = remoteConfigSource?.getConfig() + if (newConfig != null) { + updateConfig(previousConfig, newConfig) + lastUpdated = clock.now() + } + configRetrySafeWindow = DEFAULT_RETRY_WAIT_TIME.toDouble() + } catch (ex: Exception) { + configRetrySafeWindow = + min( + MAX_ALLOWED_RETRY_WAIT_TIME.toDouble(), + configRetrySafeWindow * 2 + ) + } + } + } + } + + private fun updateConfig(previousConfig: RemoteConfig, newConfig: RemoteConfig) { + val b = newConfig != previousConfig + if (b) { + configProp = newConfig + } + } + + override fun onForeground(coldStart: Boolean, timestamp: Long) { + // Refresh the config on resume if it has expired + getConfig() + foregroundAction() + } + + /** + * Checks if the time diff since the last fetch exceeds the + * [RemoteConfigSourceImpl.CONFIG_TTL] millis. + * + * @return if the config requires to be fetched from the remote server again or not. + */ + private fun configRequiresRefresh(): Boolean { + return clock.now() - lastUpdated > CONFIG_TTL + } + + /** + * Checks if the time diff since the last attempt is enough to try again. + * + * @return if the config can be fetched from the remote server again or not. + */ + private fun configRetryIsSafe(): Boolean { + return clock.now() > lastRefreshConfigAttempt + configRetrySafeWindow * 1000 + } + + private companion object { + + /** + * Config lives for 1 hour before attempting to retrieve again. + */ + private const val CONFIG_TTL = 60 * 60 * 1000L + + /** + * Config refresh default retry period. + */ + private const val DEFAULT_RETRY_WAIT_TIME: Long = 20 // 20 seconds + + /** + * Config max allowed refresh retry period. + */ + private const val MAX_ALLOWED_RETRY_WAIT_TIME: Long = 300 // 5 minutes + } +} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt index be6c8636fc..b051b60039 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt @@ -1,7 +1,9 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.config.ConfigService +import io.embrace.android.embracesdk.internal.config.RemoteConfigSource interface ConfigModule { val configService: ConfigService + val remoteConfigSource: RemoteConfigSource } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index 46713d00cf..212e6379e6 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.EmbraceConfigService +import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.worker.Worker @@ -13,23 +14,25 @@ internal class ConfigModuleImpl( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - private val configServiceProvider: (framework: AppFramework) -> ConfigService? = { null }, - private val foregroundAction: ConfigService.() -> Unit, + foregroundAction: () -> Unit, private val appIdFromConfig: String? = InstrumentedConfig.project.getAppId(), ) : ConfigModule { override val configService: ConfigService by singleton { Systrace.traceSynchronous("config-service-init") { - configServiceProvider(framework) - ?: EmbraceConfigService( - openTelemetryCfg = openTelemetryModule.openTelemetryConfiguration, - preferencesService = androidServicesModule.preferencesService, - clock = initModule.clock, - backgroundWorker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), - suppliedFramework = framework, - foregroundAction = foregroundAction, - appIdFromConfig = appIdFromConfig, - ) + EmbraceConfigService( + openTelemetryCfg = openTelemetryModule.openTelemetryConfiguration, + preferencesService = androidServicesModule.preferencesService, + suppliedFramework = framework, + appIdFromConfig = appIdFromConfig, + configProvider = remoteConfigSource::getConfig + ) } } + + override val remoteConfigSource = RemoteConfigSourceImpl( + clock = initModule.clock, + backgroundWorker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), + foregroundAction = foregroundAction, + ) } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt index 01664e6265..627e0246b3 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.internal.injection -import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.payload.AppFramework /** @@ -12,8 +11,7 @@ typealias ConfigModuleSupplier = ( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - configServiceProvider: (framework: AppFramework) -> ConfigService?, - foregroundAction: ConfigService.() -> Unit, + foregroundAction: () -> Unit, ) -> ConfigModule fun createConfigModule( @@ -22,14 +20,12 @@ fun createConfigModule( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - configServiceProvider: (framework: AppFramework) -> ConfigService? = { null }, - foregroundAction: ConfigService.() -> Unit, + foregroundAction: () -> Unit, ): ConfigModule = ConfigModuleImpl( initModule, openTelemetryModule, workerThreadModule, androidServicesModule, framework, - configServiceProvider, foregroundAction ) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt index a49b99e2fc..c73e46f949 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt @@ -1,14 +1,13 @@ package io.embrace.android.embracesdk.internal.config +import io.embrace.android.embracesdk.concurrency.BlockingScheduledExecutorService import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeLogRecordExporter import io.embrace.android.embracesdk.fakes.FakePreferenceService import io.embrace.android.embracesdk.fakes.FakeProcessStateService -import io.embrace.android.embracesdk.fakes.fakeBackgroundWorker import io.embrace.android.embracesdk.internal.SystemInfo -import io.embrace.android.embracesdk.internal.comms.api.ApiService import io.embrace.android.embracesdk.internal.comms.api.CachedConfig -import io.embrace.android.embracesdk.internal.comms.delivery.CacheService +import io.embrace.android.embracesdk.internal.config.behavior.BehaviorThresholdCheck import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.logging.EmbLogger @@ -21,11 +20,8 @@ import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessStateServ import io.embrace.android.embracesdk.internal.spans.SpanSinkImpl import io.embrace.android.embracesdk.internal.worker.BackgroundWorker import io.mockk.clearAllMocks -import io.mockk.every -import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.unmockkAll -import io.mockk.verify import org.junit.After import org.junit.AfterClass import org.junit.Assert.assertEquals @@ -41,17 +37,16 @@ internal class EmbraceConfigServiceTest { private lateinit var fakePreferenceService: PreferencesService private lateinit var service: EmbraceConfigService private lateinit var worker: BackgroundWorker + private lateinit var executor: BlockingScheduledExecutorService + private lateinit var thresholdCheck: BehaviorThresholdCheck + private lateinit var remoteConfigSource: RemoteConfigSourceImpl companion object { private lateinit var remoteConfig: RemoteConfig - private lateinit var mockApiService: ApiService private lateinit var processStateService: ProcessStateService - private lateinit var mockCacheService: CacheService private lateinit var logger: EmbLogger private lateinit var fakeClock: FakeClock - private lateinit var mockConfigListener: () -> Unit private lateinit var fakeCachedConfig: RemoteConfig - private var configListenerTriggered = false /** * Setup before all tests get executed. Create mocks here. @@ -61,13 +56,9 @@ internal class EmbraceConfigServiceTest { fun setupBeforeAll() { mockkStatic(RemoteConfig::class) remoteConfig = RemoteConfig() - mockApiService = mockk() processStateService = FakeProcessStateService() - mockCacheService = mockk(relaxed = true) fakeClock = FakeClock() logger = EmbLoggerImpl() - configListenerTriggered = false - mockConfigListener = { configListenerTriggered = true } fakeCachedConfig = RemoteConfig( // alter config to trigger listener anrConfig = AnrRemoteConfig() ) @@ -89,13 +80,9 @@ internal class EmbraceConfigServiceTest { @Before fun setup() { fakeClock.setCurrentTime(1000000000000) - every { mockApiService.getConfig() } returns remoteConfig fakePreferenceService = FakePreferenceService(deviceIdentifier = "07D85B44E4E245F4A30E559BFC0D07FF") - every { - mockCacheService.loadObject("config.json", RemoteConfig::class.java) - } returns fakeCachedConfig - every { mockApiService.getCachedConfig() } returns CachedConfig(fakeCachedConfig, null) - worker = fakeBackgroundWorker() + executor = BlockingScheduledExecutorService(blockingMode = false) + worker = BackgroundWorker(executor) service = createService(worker = worker, action = {}) assertFalse(service.isOnlyUsingOtelExporters()) } @@ -112,96 +99,72 @@ internal class EmbraceConfigServiceTest { @Test fun `test legacy normalized DeviceId`() { fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFC0D0700" - assertEquals(0.0, service.thresholdCheck.getNormalizedDeviceId().toDouble(), 0.01) + assertEquals(0.0, thresholdCheck.getNormalizedDeviceId().toDouble(), 0.01) fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFC0D07FF" - assertEquals(100.0, service.thresholdCheck.getNormalizedDeviceId().toDouble(), 0.01) + assertEquals(100.0, thresholdCheck.getNormalizedDeviceId().toDouble(), 0.01) fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFC0D0739" - assertEquals(22.35, service.thresholdCheck.getNormalizedDeviceId().toDouble(), 0.01) + assertEquals(22.35, thresholdCheck.getNormalizedDeviceId().toDouble(), 0.01) fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFC0D07D9" - assertEquals(85.09, service.thresholdCheck.getNormalizedDeviceId().toDouble(), 0.01) + assertEquals(85.09, thresholdCheck.getNormalizedDeviceId().toDouble(), 0.01) } @Test fun `test new normalized DeviceId`() { fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFC000000" - assertEquals(0.0, service.thresholdCheck.getNormalizedLargeDeviceId().toDouble(), 0.01) + assertEquals(0.0, thresholdCheck.getNormalizedLargeDeviceId().toDouble(), 0.01) fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFCFFFFFF" - assertEquals(100.0, service.thresholdCheck.getNormalizedLargeDeviceId().toDouble(), 0.01) + assertEquals(100.0, thresholdCheck.getNormalizedLargeDeviceId().toDouble(), 0.01) fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFC0D0739" - assertEquals(5.08, service.thresholdCheck.getNormalizedLargeDeviceId().toDouble(), 0.01) + assertEquals(5.08, thresholdCheck.getNormalizedLargeDeviceId().toDouble(), 0.01) fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFCED0739" - assertEquals(92.58, service.thresholdCheck.getNormalizedLargeDeviceId().toDouble(), 0.01) + assertEquals(92.58, thresholdCheck.getNormalizedLargeDeviceId().toDouble(), 0.01) } @Test fun `test isBehaviourEnabled`() { fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFC000000" - assertFalse(service.thresholdCheck.isBehaviorEnabled(0.0f)) - assertTrue(service.thresholdCheck.isBehaviorEnabled(0.1f)) - assertTrue(service.thresholdCheck.isBehaviorEnabled(100.0f)) - assertTrue(service.thresholdCheck.isBehaviorEnabled(99.9f)) - assertTrue(service.thresholdCheck.isBehaviorEnabled(34.9f)) + assertFalse(thresholdCheck.isBehaviorEnabled(0.0f)) + assertTrue(thresholdCheck.isBehaviorEnabled(0.1f)) + assertTrue(thresholdCheck.isBehaviorEnabled(100.0f)) + assertTrue(thresholdCheck.isBehaviorEnabled(99.9f)) + assertTrue(thresholdCheck.isBehaviorEnabled(34.9f)) fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFCFFFFFF" - assertFalse(service.thresholdCheck.isBehaviorEnabled(99.9f)) - assertTrue(service.thresholdCheck.isBehaviorEnabled(100.0f)) + assertFalse(thresholdCheck.isBehaviorEnabled(99.9f)) + assertTrue(thresholdCheck.isBehaviorEnabled(100.0f)) fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFC0D0739" - assertFalse(service.thresholdCheck.isBehaviorEnabled(0.0f)) - assertFalse(service.thresholdCheck.isBehaviorEnabled(2.0f)) - assertFalse(service.thresholdCheck.isBehaviorEnabled(5.0f)) - assertFalse(service.thresholdCheck.isBehaviorEnabled(5.07f)) - assertTrue(service.thresholdCheck.isBehaviorEnabled(5.09f)) - assertTrue(service.thresholdCheck.isBehaviorEnabled(47.92f)) - assertTrue(service.thresholdCheck.isBehaviorEnabled(100.0f)) + assertFalse(thresholdCheck.isBehaviorEnabled(0.0f)) + assertFalse(thresholdCheck.isBehaviorEnabled(2.0f)) + assertFalse(thresholdCheck.isBehaviorEnabled(5.0f)) + assertFalse(thresholdCheck.isBehaviorEnabled(5.07f)) + assertTrue(thresholdCheck.isBehaviorEnabled(5.09f)) + assertTrue(thresholdCheck.isBehaviorEnabled(47.92f)) + assertTrue(thresholdCheck.isBehaviorEnabled(100.0f)) } @Test fun `test isBehaviourEnabled with bad input`() { fakePreferenceService.deviceIdentifier = "07D85B44E4E245F4A30E559BFCFFFFFF" - assertFalse(service.thresholdCheck.isBehaviorEnabled(1000f)) - assertFalse(service.thresholdCheck.isBehaviorEnabled(-1000f)) - } - - @Test - fun `test config exists in cache and is loaded correctly`() { - assertTrue(service.anrBehavior.isAnrCaptureEnabled()) - - val obj = RemoteConfig(anrConfig = AnrRemoteConfig(pctEnabled = 0)) - every { mockApiService.getCachedConfig() } returns CachedConfig(obj, null) - service.loadConfigFromCache() - - // config was updated - assertFalse(service.anrBehavior.isAnrCaptureEnabled()) + assertFalse(thresholdCheck.isBehaviorEnabled(1000f)) + assertFalse(thresholdCheck.isBehaviorEnabled(-1000f)) } @Test fun `test config does not exist in cache, so it's not loaded`() { assertTrue(service.anrBehavior.isAnrCaptureEnabled()) - every { mockApiService.getCachedConfig() } returns CachedConfig(null, null) - service.loadConfigFromCache() + remoteConfigSource.loadConfigFromCache() // config was not updated assertTrue(service.anrBehavior.isAnrCaptureEnabled()) } - @Test - fun `test service constructor reads cached config`() { - val obj = RemoteConfig(anrConfig = AnrRemoteConfig(pctEnabled = 0)) - every { mockApiService.getConfig() } returns null - every { mockApiService.getCachedConfig() } returns CachedConfig(obj, null) - service = createService(worker) - - // config was updated - assertFalse(service.anrBehavior.isAnrCaptureEnabled()) - } - /** * Test that calling getConfig() refreshes the config * As we are using a DirectExecutor this method will run synchronously and @@ -213,10 +176,19 @@ internal class EmbraceConfigServiceTest { // advance the clock so it's safe to retry config refresh fakeClock.tick(1000000000000) val newConfig = RemoteConfig(anrConfig = AnrRemoteConfig()) - every { mockApiService.getConfig() } returns newConfig - - service.onForeground(true, 1100L) - verify(exactly = 2) { mockApiService.getConfig() } + var count = 0 + + remoteConfigSource.remoteConfigSource = object : CachedRemoteConfigSource { + override fun getCachedConfig(): CachedConfig { + return CachedConfig(null, null) + } + override fun getConfig(): RemoteConfig { + count++ + return newConfig + } + } + remoteConfigSource.onForeground(true, 1100L) + assertEquals(1, count) } @Test @@ -253,22 +225,27 @@ internal class EmbraceConfigServiceTest { */ private fun createService( worker: BackgroundWorker, - action: ConfigService.() -> Unit = {}, + action: () -> Unit = {}, config: OpenTelemetryConfiguration = OpenTelemetryConfiguration( SpanSinkImpl(), LogSinkImpl(), SystemInfo() ), appId: String? = "AbCdE", - ): EmbraceConfigService = EmbraceConfigService( - openTelemetryCfg = config, - preferencesService = fakePreferenceService, - clock = fakeClock, - backgroundWorker = worker, - suppliedFramework = AppFramework.NATIVE, - foregroundAction = action, - appIdFromConfig = appId - ).apply { - remoteConfigSource = mockApiService + ): EmbraceConfigService { + thresholdCheck = BehaviorThresholdCheck { fakePreferenceService.deviceIdentifier } + remoteConfigSource = RemoteConfigSourceImpl( + clock = fakeClock, + backgroundWorker = worker, + foregroundAction = action, + ) + return EmbraceConfigService( + openTelemetryCfg = config, + preferencesService = fakePreferenceService, + suppliedFramework = AppFramework.NATIVE, + appIdFromConfig = appId, + thresholdCheck = thresholdCheck, + configProvider = remoteConfigSource::getConfig, + ) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt index 853704db39..c7e2937113 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt @@ -1,7 +1,6 @@ package io.embrace.android.embracesdk.internal.injection import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeOpenTelemetryModule import io.embrace.android.embracesdk.fakes.injection.FakeAndroidServicesModule import io.embrace.android.embracesdk.fakes.injection.FakeInitModule @@ -23,29 +22,12 @@ internal class ConfigModuleImplTest { workerThreadModule = FakeWorkerThreadModule(), androidServicesModule = FakeAndroidServicesModule(), framework = AppFramework.NATIVE, - configServiceProvider = { null }, foregroundAction = {}, appIdFromConfig = "AbCeD", ) assertNotNull(module.configService) } - @Test - fun testConfigServiceProvider() { - val fakeConfigService = FakeConfigService() - val module = ConfigModuleImpl( - initModule = FakeInitModule(), - openTelemetryModule = FakeOpenTelemetryModule(), - workerThreadModule = FakeWorkerThreadModule(), - androidServicesModule = FakeAndroidServicesModule(), - framework = AppFramework.NATIVE, - configServiceProvider = { fakeConfigService }, - foregroundAction = {}, - appIdFromConfig = "AbCeD", - ) - assertSame(fakeConfigService, module.configService) - } - @Test fun `validate appId in appIdProvider is used`() { val module = ConfigModuleImpl( @@ -54,7 +36,6 @@ internal class ConfigModuleImplTest { workerThreadModule = FakeWorkerThreadModule(), androidServicesModule = FakeAndroidServicesModule(), framework = AppFramework.NATIVE, - configServiceProvider = { null }, foregroundAction = {}, appIdFromConfig = "AbCeD", ) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt index ae2b672b74..293b0dedd2 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt @@ -15,6 +15,7 @@ import io.embrace.android.embracesdk.internal.injection.EssentialServiceModule import io.embrace.android.embracesdk.internal.injection.EssentialServiceModuleImpl import io.embrace.android.embracesdk.internal.injection.InitModule import io.embrace.android.embracesdk.internal.injection.ModuleInitBootstrapper +import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.testframework.actions.EmbraceActionInterface import io.embrace.android.embracesdk.testframework.actions.EmbraceOtelExportAssertionInterface @@ -118,9 +119,8 @@ internal class IntegrationTestRule( embraceImpl.addSpanExporter(spanExporter) if (startSdk) { - embraceImpl.start(overriddenCoreModule.context, appFramework) { - overriddenConfigService.apply { appFramework = it } - } + overriddenConfigService.appFramework = findAppFramework() + embraceImpl.start(overriddenCoreModule.context) assertEquals(expectSdkToStart, bootstrapper.essentialServiceModule.processStateService.isInitialized()) } } @@ -130,6 +130,15 @@ internal class IntegrationTestRule( otelExportAssertion(otelAssertion) } + @Suppress("DEPRECATION") + fun EmbraceSetupInterface.findAppFramework() = + when (appFramework) { + io.embrace.android.embracesdk.AppFramework.NATIVE -> AppFramework.NATIVE + io.embrace.android.embracesdk.AppFramework.REACT_NATIVE -> AppFramework.REACT_NATIVE + io.embrace.android.embracesdk.AppFramework.UNITY -> AppFramework.UNITY + io.embrace.android.embracesdk.AppFramework.FLUTTER -> AppFramework.FLUTTER + } + /** * Setup the Embrace SDK so it's ready for testing. */ diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index c05eb4b444..c9f5c3d4ec 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.testing.TestLifecycleOwner import io.embrace.android.embracesdk.AppFramework import io.embrace.android.embracesdk.fakes.FakeClock +import io.embrace.android.embracesdk.fakes.FakeConfigModule import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeDeliveryService import io.embrace.android.embracesdk.fakes.FakeEmbLogger @@ -28,6 +29,7 @@ import io.embrace.android.embracesdk.internal.injection.NativeCoreModule import io.embrace.android.embracesdk.internal.injection.OpenTelemetryModule import io.embrace.android.embracesdk.internal.injection.WorkerThreadModule import io.embrace.android.embracesdk.internal.injection.createAndroidServicesModule +import io.embrace.android.embracesdk.internal.injection.createConfigModule import io.embrace.android.embracesdk.internal.injection.createDeliveryModule import io.embrace.android.embracesdk.internal.injection.createEssentialServiceModule import io.embrace.android.embracesdk.internal.injection.createWorkerThreadModule @@ -107,6 +109,11 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( } }) }, + configModuleSupplier = { _, _, _, _, _, _ -> + FakeConfigModule( + configService = overriddenConfigService + ) + }, anrModuleSupplier = { _, _, _ -> fakeAnrModule }, nativeCoreModuleSupplier = { fakeNativeCoreModule }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> fakeNativeFeatureModule } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt index 382ff57672..ab35d7d16c 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt @@ -118,34 +118,14 @@ internal class EmbraceImpl @JvmOverloads constructor( } @Suppress("DEPRECATION") - override fun start(context: Context) = start(context, io.embrace.android.embracesdk.AppFramework.NATIVE) { null } + override fun start(context: Context) = start(context, io.embrace.android.embracesdk.AppFramework.NATIVE) @Suppress("DEPRECATION") @Deprecated("Use {@link #start(Context)} instead.", ReplaceWith("start(context)")) - override fun start(context: Context, appFramework: io.embrace.android.embracesdk.AppFramework) = - start(context, appFramework) { null } - - /** - * Starts instrumentation of the Android application using the Embrace SDK. This should be - * called during creation of the application, as early as possible. - * - * See [Embrace Docs](https://embrace.io/docs/android/) for - * integration instructions. For compatibility with other networking SDKs such as Akamai, - * the Embrace SDK must be initialized after any other SDK. - * - * @param context an instance of context - * @param appFramework the AppFramework of the application - * @param configServiceProvider provider for the config service - */ - @Suppress("DEPRECATION") - fun start( - context: Context, - appFramework: io.embrace.android.embracesdk.AppFramework, - configServiceProvider: (framework: AppFramework) -> ConfigService? = { null }, - ) { + override fun start(context: Context, appFramework: io.embrace.android.embracesdk.AppFramework) { try { startSynchronous("sdk-start") - startImpl(context, appFramework, configServiceProvider) + startImpl(context, appFramework) endSynchronous() } catch (t: Throwable) { runCatching { @@ -157,8 +137,7 @@ internal class EmbraceImpl @JvmOverloads constructor( @Suppress("DEPRECATION", "CyclomaticComplexMethod", "ComplexMethod") private fun startImpl( context: Context, - framework: io.embrace.android.embracesdk.AppFramework, - configServiceProvider: (framework: AppFramework) -> ConfigService?, + framework: io.embrace.android.embracesdk.AppFramework ) { if (application != null) { return @@ -167,7 +146,7 @@ internal class EmbraceImpl @JvmOverloads constructor( val startTimeMs = sdkClock.now() val appFramework = fromFramework(framework) - bootstrapper.init(context, appFramework, startTimeMs, configServiceProvider) + bootstrapper.init(context, appFramework, startTimeMs) startSynchronous("post-services-setup") val coreModule = bootstrapper.coreModule diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index 8bf96ae957..9446d44dff 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -6,7 +6,7 @@ import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.capture.envelope.session.OtelPayloadMapperImpl import io.embrace.android.embracesdk.internal.comms.delivery.EmbraceDeliveryService -import io.embrace.android.embracesdk.internal.config.ConfigService +import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl import io.embrace.android.embracesdk.internal.delivery.execution.HttpUrlConnectionRequestExecutionService import io.embrace.android.embracesdk.internal.delivery.execution.OkHttpRequestExecutionService import io.embrace.android.embracesdk.internal.logging.EmbLogger @@ -117,7 +117,6 @@ internal class ModuleInitBootstrapper( context: Context, appFramework: AppFramework, sdkStartTimeMs: Long, - configServiceProvider: (framework: AppFramework) -> ConfigService? = { null }, versionChecker: VersionChecker = BuildVersionChecker, ): Boolean { try { @@ -155,16 +154,16 @@ internal class ModuleInitBootstrapper( openTelemetryModule, workerThreadModule, androidServicesModule, - appFramework, - configServiceProvider, + appFramework ) { - if (sdkModeBehavior.isSdkDisabled()) { + if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { EmbraceInternalApi.getInstance().internalInterface.stopSdk() } } } postInit(ConfigModule::class) { serviceRegistry.registerService(lazy { configModule.configService }) + serviceRegistry.registerService(lazy { configModule.remoteConfigSource }) openTelemetryModule.setupSensitiveKeysBehavior(configModule.configService.sensitiveKeysBehavior) } @@ -189,7 +188,7 @@ internal class ModuleInitBootstrapper( postInit(EssentialServiceModule::class) { // Allow config service to start making HTTP requests with(essentialServiceModule) { - configModule.configService.remoteConfigSource = apiService + (configModule.remoteConfigSource as? RemoteConfigSourceImpl)?.remoteConfigSource = apiService serviceRegistry.registerServices( lazy { essentialServiceModule.processStateService }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 511da68143..2fdb3c693b 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -43,7 +43,7 @@ internal fun fakeModuleInitBootstrapper( workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, - configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, + configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _ -> FakeConfigModule() }, dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt index 0523b3c308..25e53f0dbc 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt @@ -32,7 +32,7 @@ internal class ModuleInitBootstrapperTest { logger = EmbLoggerImpl() coreModule = FakeCoreModule() moduleInitBootstrapper = ModuleInitBootstrapper( - configModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, + configModuleSupplier = { _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, coreModuleSupplier = { _ -> coreModule }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> FakeNativeFeatureModule() }, logger = logger diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt index 39bc131064..7cd996fe1b 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkBehavior import io.embrace.android.embracesdk.internal.config.ConfigService +import io.embrace.android.embracesdk.internal.config.RemoteConfigSource import io.embrace.android.embracesdk.internal.injection.ConfigModule class FakeConfigModule( @@ -10,4 +11,5 @@ class FakeConfigModule( captureHttpUrlConnectionRequests = false ) ), + override val remoteConfigSource: RemoteConfigSource = FakeRemoteConfigSource() ) : ConfigModule diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt index afab00c2e7..5b8152eb3d 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt @@ -1,7 +1,6 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.internal.config.ConfigService -import io.embrace.android.embracesdk.internal.config.RemoteConfigSource import io.embrace.android.embracesdk.internal.config.behavior.AnrBehavior import io.embrace.android.embracesdk.internal.config.behavior.AppExitInfoBehavior import io.embrace.android.embracesdk.internal.config.behavior.AutoDataCaptureBehavior @@ -42,6 +41,5 @@ class FakeConfigService( override var networkSpanForwardingBehavior: NetworkSpanForwardingBehavior = createNetworkSpanForwardingBehavior(), override var sensitiveKeysBehavior: SensitiveKeysBehavior = createSensitiveKeysBehavior(), ) : ConfigService { - override var remoteConfigSource: RemoteConfigSource? = null override fun isOnlyUsingOtelExporters(): Boolean = onlyUsingOtelExporters } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt new file mode 100644 index 0000000000..62b389db31 --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt @@ -0,0 +1,10 @@ +package io.embrace.android.embracesdk.fakes + +import io.embrace.android.embracesdk.internal.config.RemoteConfigSource +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +class FakeRemoteConfigSource( + var cfg: RemoteConfig? = null +) : RemoteConfigSource { + override fun getConfig(): RemoteConfig? = cfg +} From 0717c5e4f797471738e9c1993c30ab7f50c4f0cb Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Thu, 7 Nov 2024 11:27:02 +0000 Subject: [PATCH 04/28] refactor: create interface for instrumented config --- .../buildinfo/BuildInfoServiceImpl.kt | 77 ++++++++----------- .../internal/config/EmbraceConfigService.kt | 38 +++++---- .../config/behavior/AnrBehaviorImpl.kt | 5 +- .../behavior/AppExitInfoBehaviorImpl.kt | 5 +- .../behavior/AutoDataCaptureBehaviorImpl.kt | 5 +- .../BackgroundActivityBehaviorImpl.kt | 5 +- .../config/behavior/BreadcrumbBehaviorImpl.kt | 5 +- .../config/behavior/NetworkBehaviorImpl.kt | 9 ++- .../NetworkSpanForwardingBehaviorImpl.kt | 5 +- .../behavior/SdkEndpointBehaviorImpl.kt | 7 +- .../behavior/SensitiveKeysBehaviorImpl.kt | 9 ++- .../config/behavior/SessionBehaviorImpl.kt | 7 +- .../internal/injection/ConfigModuleImpl.kt | 6 +- .../internal/injection/CoreModuleImpl.kt | 3 +- .../internal/injection/CoreModuleSupplier.kt | 8 +- .../internal/injection/InitModule.kt | 3 + .../internal/injection/InitModuleImpl.kt | 4 + .../SessionPropertiesServiceImplTest.kt | 6 +- .../config/EmbraceConfigServiceTest.kt | 4 +- .../behavior/BreadcrumbBehaviorImplTest.kt | 15 +++- .../behavior/SensitiveKeysBehaviorImplTest.kt | 15 ++-- .../AndroidServicesModuleImplTest.kt | 2 +- .../injection/ConfigModuleImplTest.kt | 16 ---- .../internal/injection/CoreModuleImplTest.kt | 7 +- .../injection/DeliveryModuleImplTest.kt | 5 +- .../injection/PayloadSourceModuleImplTest.kt | 4 +- .../injection/SystemServiceModuleImplTest.kt | 6 +- .../internal/logs/EmbraceLogServiceTest.kt | 6 +- .../message/PayloadMessageCollatorImplTest.kt | 2 +- .../spans/EmbraceSpanFactoryImplTest.kt | 6 +- .../internal/spans/EmbraceSpanImplTest.kt | 6 +- .../injection/FeatureModuleImplTest.kt | 5 +- .../injection/NativeFeatureModuleImplTest.kt | 5 +- .../injection/StorageModuleImplTest.kt | 2 +- .../config/instrumented/InstrumentedConfig.kt | 21 ----- .../instrumented/InstrumentedConfigImpl.kt | 47 +++++++++++ .../{ => schema}/BaseUrlConfig.kt | 6 +- .../{ => schema}/EnabledFeatureConfig.kt | 6 +- .../instrumented/schema/InstrumentedConfig.kt | 14 ++++ .../{ => schema}/NetworkCaptureConfig.kt | 5 +- .../{ => schema}/ProjectConfig.kt | 6 +- .../{ => schema}/RedactionConfig.kt | 6 +- .../{ => schema}/SessionConfig.kt | 10 +-- .../testcases/features/ActivityFeatureTest.kt | 5 -- .../SensitiveKeysRedactionFeatureTest.kt | 6 +- .../actions/EmbraceSetupInterface.kt | 5 +- .../injection/ModuleInitBootstrapper.kt | 2 +- .../fakes/FakeModuleInitBootstrapper.kt | 2 +- .../injection/ModuleInitBootstrapperTest.kt | 2 +- .../android/embracesdk/fakes/Behavior.kt | 23 +++--- .../fakes/config/FakeBaseUrlConfig.kt | 13 ++++ .../fakes/config/FakeEnabledFeatureConfig.kt | 53 +++++++++++++ .../fakes/config/FakeInstrumentedConfig.kt | 21 +++++ .../fakes/config/FakeNetworkCaptureConfig.kt | 17 ++++ .../fakes/config/FakeProjectConfig.kt | 19 +++++ .../fakes/config/FakeRedactionConfig.kt | 11 +++ .../fakes/config/FakeSessionConfig.kt | 13 ++++ .../fakes/injection/FakeInitModule.kt | 3 + examples/ExampleApp/settings.gradle.kts | 2 + 59 files changed, 427 insertions(+), 204 deletions(-) delete mode 100644 embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/InstrumentedConfig.kt create mode 100644 embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/InstrumentedConfigImpl.kt rename embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/{ => schema}/BaseUrlConfig.kt (82%) rename embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/{ => schema}/EnabledFeatureConfig.kt (97%) create mode 100644 embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/InstrumentedConfig.kt rename embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/{ => schema}/NetworkCaptureConfig.kt (95%) rename embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/{ => schema}/ProjectConfig.kt (91%) rename embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/{ => schema}/RedactionConfig.kt (80%) rename embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/{ => schema}/SessionConfig.kt (79%) create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeBaseUrlConfig.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeEnabledFeatureConfig.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeInstrumentedConfig.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeNetworkCaptureConfig.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeProjectConfig.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeRedactionConfig.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeSessionConfig.kt diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/buildinfo/BuildInfoServiceImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/buildinfo/BuildInfoServiceImpl.kt index 0fdafdaf5c..7d145565b6 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/buildinfo/BuildInfoServiceImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/buildinfo/BuildInfoServiceImpl.kt @@ -2,60 +2,49 @@ package io.embrace.android.embracesdk.internal.buildinfo import android.content.res.Resources import io.embrace.android.embracesdk.internal.AndroidResourcesService -import io.embrace.android.embracesdk.internal.config.instrumented.ProjectConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig internal class BuildInfoServiceImpl( - resources: AndroidResourcesService, - packageName: String, + private val instrumentedConfig: InstrumentedConfig, + private val resources: AndroidResourcesService, + private val packageName: String, ) : BuildInfoService { + companion object { + private const val BUILD_INFO_RN_BUNDLE_ID: String = "emb_rn_bundle_id" + private const val RES_TYPE_STRING = "string" + } + private val info by lazy { - fromResources(resources, packageName) + BuildInfo( + instrumentedConfig.project.getBuildId(), + instrumentedConfig.project.getBuildType(), + instrumentedConfig.project.getBuildFlavor(), + getBuildResource(resources, packageName, BUILD_INFO_RN_BUNDLE_ID), + ) } override fun getBuildInfo(): BuildInfo = info - companion object { - private const val BUILD_INFO_RN_BUNDLE_ID: String = "emb_rn_bundle_id" - private const val RES_TYPE_STRING = "string" - - /** - * Loads the build information from resources provided by the config file packaged within the application by Gradle at - * build-time. - * - * @return the build information - */ - @JvmStatic - fun fromResources(resources: AndroidResourcesService, packageName: String): BuildInfo { - val buildInfo = BuildInfo( - ProjectConfig.getBuildId(), - ProjectConfig.getBuildType(), - ProjectConfig.getBuildFlavor(), - getBuildResource(resources, packageName, BUILD_INFO_RN_BUNDLE_ID), + /** + * Given a build property name and a build property type, retrieves the embrace build resource value. + */ + fun getBuildResource( + resources: AndroidResourcesService, + packageName: String, + buildProperty: String, + ): String? { + return try { + val resourceId = + resources.getIdentifier(buildProperty, RES_TYPE_STRING, packageName) + resources.getString(resourceId) + } catch (ex: NullPointerException) { + throw IllegalArgumentException( + "No resource found for $buildProperty property. Failed to create build info.", + ex ) - return buildInfo - } - - /** - * Given a build property name and a build property type, retrieves the embrace build resource value. - */ - fun getBuildResource( - resources: AndroidResourcesService, - packageName: String, - buildProperty: String, - ): String? { - return try { - val resourceId = - resources.getIdentifier(buildProperty, RES_TYPE_STRING, packageName) - resources.getString(resourceId) - } catch (ex: NullPointerException) { - throw IllegalArgumentException( - "No resource found for $buildProperty property. Failed to create build info.", - ex - ) - } catch (ex: Resources.NotFoundException) { - null - } + } catch (ex: Resources.NotFoundException) { + null } } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt index e299845dd5..3907211599 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt @@ -29,7 +29,7 @@ import io.embrace.android.embracesdk.internal.config.behavior.SessionBehavior import io.embrace.android.embracesdk.internal.config.behavior.SessionBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehavior import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehaviorImpl -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.OpenTelemetryConfiguration import io.embrace.android.embracesdk.internal.payload.AppFramework @@ -43,30 +43,34 @@ internal class EmbraceConfigService( openTelemetryCfg: OpenTelemetryConfiguration, preferencesService: PreferencesService, suppliedFramework: AppFramework, - appIdFromConfig: String?, configProvider: Provider, thresholdCheck: BehaviorThresholdCheck = BehaviorThresholdCheck { preferencesService.deviceIdentifier }, + instrumentedConfig: InstrumentedConfig, ) : ConfigService { override val backgroundActivityBehavior: BackgroundActivityBehavior = BackgroundActivityBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = { configProvider()?.backgroundActivityConfig } + remoteSupplier = { configProvider()?.backgroundActivityConfig }, + instrumentedConfig = instrumentedConfig ) override val autoDataCaptureBehavior: AutoDataCaptureBehavior = AutoDataCaptureBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = configProvider + remoteSupplier = configProvider, + instrumentedConfig = instrumentedConfig ) override val breadcrumbBehavior: BreadcrumbBehavior = BreadcrumbBehaviorImpl( thresholdCheck, - remoteSupplier = configProvider + remoteSupplier = configProvider, + instrumentedConfig = instrumentedConfig ) - override val sensitiveKeysBehavior: SensitiveKeysBehavior = SensitiveKeysBehaviorImpl() + override val sensitiveKeysBehavior: SensitiveKeysBehavior = + SensitiveKeysBehaviorImpl(instrumentedConfig = instrumentedConfig) override val logMessageBehavior: LogMessageBehavior = LogMessageBehaviorImpl( @@ -77,18 +81,21 @@ internal class EmbraceConfigService( override val anrBehavior: AnrBehavior = AnrBehaviorImpl( thresholdCheck, - remoteSupplier = { configProvider()?.anrConfig } + remoteSupplier = { configProvider()?.anrConfig }, + instrumentedConfig = instrumentedConfig ) override val sessionBehavior: SessionBehavior = SessionBehaviorImpl( thresholdCheck, - remoteSupplier = configProvider + remoteSupplier = configProvider, + instrumentedConfig = instrumentedConfig ) override val networkBehavior: NetworkBehavior = NetworkBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = configProvider + remoteSupplier = configProvider, + instrumentedConfig = instrumentedConfig ) override val dataCaptureEventBehavior: DataCaptureEventBehavior = DataCaptureEventBehaviorImpl( @@ -102,18 +109,21 @@ internal class EmbraceConfigService( ) override val sdkEndpointBehavior: SdkEndpointBehavior = SdkEndpointBehaviorImpl( - thresholdCheck = thresholdCheck + thresholdCheck = thresholdCheck, + instrumentedConfig = instrumentedConfig ) override val appExitInfoBehavior: AppExitInfoBehavior = AppExitInfoBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = configProvider + remoteSupplier = configProvider, + instrumentedConfig = instrumentedConfig ) override val networkSpanForwardingBehavior: NetworkSpanForwardingBehavior = NetworkSpanForwardingBehaviorImpl( thresholdCheck = thresholdCheck, - remoteSupplier = { configProvider()?.networkSpanForwardingRemoteConfig } + remoteSupplier = { configProvider()?.networkSpanForwardingRemoteConfig }, + instrumentedConfig = instrumentedConfig ) override val webViewVitalsBehavior: WebViewVitalsBehavior = @@ -122,7 +132,7 @@ internal class EmbraceConfigService( remoteSupplier = configProvider ) - override val appId: String? = resolveAppId(appIdFromConfig, openTelemetryCfg) + override val appId: String? = resolveAppId(instrumentedConfig.project.getAppId(), openTelemetryCfg) override fun isOnlyUsingOtelExporters(): Boolean = appId.isNullOrEmpty() @@ -141,7 +151,7 @@ internal class EmbraceConfigService( return id } - override val appFramework: AppFramework = InstrumentedConfig.project.getAppFramework()?.let { + override val appFramework: AppFramework = instrumentedConfig.project.getAppFramework()?.let { AppFramework.fromString(it) } ?: suppliedFramework } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImpl.kt index 02b2f5e5e7..09a0270019 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImpl.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.AllowedNdkSampleMethod import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.Unwinder @@ -13,6 +13,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider class AnrBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, remoteSupplier: Provider, + private val instrumentedConfig: InstrumentedConfig, ) : AnrBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck, remoteSupplier = remoteSupplier @@ -64,7 +65,7 @@ class AnrBehaviorImpl( override fun isUnityAnrCaptureEnabled(): Boolean { return thresholdCheck.isBehaviorEnabled(remote?.pctNativeThreadAnrSamplingEnabled) - ?: InstrumentedConfig.enabledFeatures.isUnityAnrCaptureEnabled() + ?: instrumentedConfig.enabledFeatures.isUnityAnrCaptureEnabled() } override fun isNativeThreadAnrSamplingOffsetEnabled(): Boolean = diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImpl.kt index 9c11f0bc9c..640b471e35 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImpl.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.utils.Provider @@ -12,6 +12,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider class AppExitInfoBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, remoteSupplier: Provider, + private val instrumentedConfig: InstrumentedConfig, ) : AppExitInfoBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck, remoteSupplier = remoteSupplier @@ -30,7 +31,7 @@ class AppExitInfoBehaviorImpl( override fun isAeiCaptureEnabled(): Boolean { return thresholdCheck.isBehaviorEnabled(remote?.appExitInfoConfig?.pctAeiCaptureEnabled) - ?: InstrumentedConfig.enabledFeatures.isAeiCaptureEnabled() + ?: instrumentedConfig.enabledFeatures.isAeiCaptureEnabled() } override fun appExitInfoMaxNum(): Int = remote?.appExitInfoConfig?.aeiMaxNum ?: AEI_MAX_NUM_DEFAULT diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImpl.kt index e7e61eafa9..3800b0ac1e 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImpl.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.utils.Provider @@ -12,6 +12,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider class AutoDataCaptureBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, remoteSupplier: Provider, + instrumentedConfig: InstrumentedConfig, ) : AutoDataCaptureBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck, remoteSupplier = remoteSupplier @@ -23,7 +24,7 @@ class AutoDataCaptureBehaviorImpl( const val USE_OKHTTP_DEFAULT = true } - private val cfg = InstrumentedConfig.enabledFeatures + private val cfg = instrumentedConfig.enabledFeatures @Suppress("DEPRECATION") override fun isMemoryWarningCaptureEnabled(): Boolean = cfg.isMemoryWarningCaptureEnabled() diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImpl.kt index 14ab1ac00b..2ba0f80eba 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImpl.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.utils.Provider @@ -11,6 +11,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider class BackgroundActivityBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, remoteSupplier: Provider, + private val instrumentedConfig: InstrumentedConfig, ) : BackgroundActivityBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck, @@ -19,7 +20,7 @@ class BackgroundActivityBehaviorImpl( override fun isBackgroundActivityCaptureEnabled(): Boolean { return remote?.threshold?.let(thresholdCheck::isBehaviorEnabled) - ?: InstrumentedConfig.enabledFeatures.isBackgroundActivityCaptureEnabled() + ?: instrumentedConfig.enabledFeatures.isBackgroundActivityCaptureEnabled() } override fun getManualBackgroundActivityLimit(): Int = 100 diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImpl.kt index 8a0e6fe5b4..5e2f877e41 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImpl.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.utils.Provider @@ -12,6 +12,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider class BreadcrumbBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, remoteSupplier: Provider, + instrumentedConfig: InstrumentedConfig, ) : BreadcrumbBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck, remoteSupplier = remoteSupplier @@ -25,7 +26,7 @@ class BreadcrumbBehaviorImpl( const val DEFAULT_BREADCRUMB_LIMIT = 100 } - private val cfg = InstrumentedConfig.enabledFeatures + private val cfg = instrumentedConfig.enabledFeatures override fun getCustomBreadcrumbLimit(): Int = remote?.uiConfig?.breadcrumbs ?: DEFAULT_BREADCRUMB_LIMIT diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImpl.kt index 079b64adc7..cd9d8758c1 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImpl.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.utils.Provider @@ -15,6 +15,7 @@ class NetworkBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, remoteSupplier: Provider, private val disabledUrlPatterns: List? = null, + private val instrumentedConfig: InstrumentedConfig, ) : NetworkBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck, remoteSupplier = remoteSupplier @@ -34,13 +35,13 @@ class NetworkBehaviorImpl( ) } - private val cfg = InstrumentedConfig.networkCapture + private val cfg = instrumentedConfig.networkCapture override fun isRequestContentLengthCaptureEnabled(): Boolean = - InstrumentedConfig.enabledFeatures.isRequestContentLengthCaptureEnabled() + instrumentedConfig.enabledFeatures.isRequestContentLengthCaptureEnabled() override fun isHttpUrlConnectionCaptureEnabled(): Boolean = - InstrumentedConfig.enabledFeatures.isHttpUrlConnectionCaptureEnabled() + instrumentedConfig.enabledFeatures.isHttpUrlConnectionCaptureEnabled() override fun getLimitsByDomain(): Map { val limits = remote?.networkConfig?.domainLimits ?: cfg.getLimitsByDomain() diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImpl.kt index a5a3cf8a7c..6dc6db7e4f 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImpl.kt @@ -1,13 +1,14 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.NetworkSpanForwardingRemoteConfig import io.embrace.android.embracesdk.internal.utils.Provider class NetworkSpanForwardingBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, remoteSupplier: Provider, + private val instrumentedConfig: InstrumentedConfig, ) : NetworkSpanForwardingBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck, remoteSupplier = remoteSupplier @@ -21,6 +22,6 @@ class NetworkSpanForwardingBehaviorImpl( override fun isNetworkSpanForwardingEnabled(): Boolean { return remote?.pctEnabled?.let { thresholdCheck.isBehaviorEnabled(it) } - ?: InstrumentedConfig.enabledFeatures.isNetworkSpanForwardingEnabled() + ?: instrumentedConfig.enabledFeatures.isNetworkSpanForwardingEnabled() } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt index c908301adf..7e05fec69f 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt @@ -1,13 +1,14 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig /** * Provides the behavior that the Background Activity feature should follow. */ class SdkEndpointBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, + private val instrumentedConfig: InstrumentedConfig, ) : SdkEndpointBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck ) { @@ -21,13 +22,13 @@ class SdkEndpointBehaviorImpl( if (appId == null) { return "" } - return InstrumentedConfig.baseUrls.getData() ?: "https://a-$appId.$DATA_DEFAULT" + return instrumentedConfig.baseUrls.getData() ?: "https://a-$appId.$DATA_DEFAULT" } override fun getConfig(appId: String?): String { if (appId == null) { return "" } - return InstrumentedConfig.baseUrls.getConfig() ?: "https://a-$appId.$CONFIG_DEFAULT" + return instrumentedConfig.baseUrls.getConfig() ?: "https://a-$appId.$CONFIG_DEFAULT" } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImpl.kt index 46e417b4f5..4e3f9d3d43 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImpl.kt @@ -1,16 +1,19 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig private const val SENSITIVE_KEY_MAX_LENGTH = 128 private const val SENSITIVE_KEYS_LIST_MAX_SIZE = 10000 const val REDACTED_LABEL: String = "" -class SensitiveKeysBehaviorImpl(denyList: List? = null) : SensitiveKeysBehavior { +class SensitiveKeysBehaviorImpl( + denyList: List? = null, + instrumentedConfig: InstrumentedConfig +) : SensitiveKeysBehavior { private val denyList = - (denyList ?: InstrumentedConfig.redaction.getSensitiveKeysDenylist())?.take(SENSITIVE_KEYS_LIST_MAX_SIZE) + (denyList ?: instrumentedConfig.redaction.getSensitiveKeysDenylist())?.take(SENSITIVE_KEYS_LIST_MAX_SIZE) override fun isSensitiveKey(key: String): Boolean { return denyList?.any { it.take(SENSITIVE_KEY_MAX_LENGTH) == key } ?: false diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImpl.kt index 6dd88269ea..2d4a8b655d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImpl.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.gating.SessionGatingKeys import io.embrace.android.embracesdk.internal.utils.Provider @@ -13,6 +13,7 @@ import java.util.Locale class SessionBehaviorImpl( thresholdCheck: BehaviorThresholdCheck, remoteSupplier: Provider, + private val instrumentedConfig: InstrumentedConfig, ) : SessionBehavior, MergedConfigBehavior( thresholdCheck = thresholdCheck, remoteSupplier = remoteSupplier @@ -23,12 +24,12 @@ class SessionBehaviorImpl( } override fun getFullSessionEvents(): Set { - val strings = remote?.sessionConfig?.fullSessionEvents ?: InstrumentedConfig.session.getFullSessionEvents() + val strings = remote?.sessionConfig?.fullSessionEvents ?: instrumentedConfig.session.getFullSessionEvents() return strings.map { it.lowercase(Locale.US) }.toSet() } override fun getSessionComponents(): Set? = - (remote?.sessionConfig?.sessionComponents ?: InstrumentedConfig.session.getSessionComponents())?.toSet() + (remote?.sessionConfig?.sessionComponents ?: instrumentedConfig.session.getSessionComponents())?.toSet() override fun isGatingFeatureEnabled(): Boolean = getSessionComponents() != null diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index 212e6379e6..ab2f5b0f44 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -4,7 +4,6 @@ import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.EmbraceConfigService import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfig import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.worker.Worker @@ -15,7 +14,6 @@ internal class ConfigModuleImpl( androidServicesModule: AndroidServicesModule, framework: AppFramework, foregroundAction: () -> Unit, - private val appIdFromConfig: String? = InstrumentedConfig.project.getAppId(), ) : ConfigModule { override val configService: ConfigService by singleton { @@ -24,8 +22,8 @@ internal class ConfigModuleImpl( openTelemetryCfg = openTelemetryModule.openTelemetryConfiguration, preferencesService = androidServicesModule.preferencesService, suppliedFramework = framework, - appIdFromConfig = appIdFromConfig, - configProvider = remoteConfigSource::getConfig + instrumentedConfig = initModule.instrumentedConfig, + configProvider = remoteConfigSource::getConfig, ) } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleImpl.kt index be7a764304..5c1fc3e2af 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleImpl.kt @@ -11,6 +11,7 @@ import io.embrace.android.embracesdk.internal.registry.ServiceRegistry class CoreModuleImpl( ctx: Context, + initModule: InitModule ) : CoreModule { override val context: Context by singleton { @@ -39,6 +40,6 @@ class CoreModuleImpl( } override val buildInfoService: BuildInfoService by lazy { - BuildInfoServiceImpl(resources, context.packageName) + BuildInfoServiceImpl(initModule.instrumentedConfig, resources, context.packageName) } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleSupplier.kt index 1ed6d12615..3a46c9c7c1 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleSupplier.kt @@ -5,8 +5,12 @@ import android.content.Context /** * Function that returns an instance of [CoreModule]. Matches the signature of the constructor for [CoreModuleImpl] */ -typealias CoreModuleSupplier = (context: Context) -> CoreModule +typealias CoreModuleSupplier = ( + context: Context, + initModule: InitModule, +) -> CoreModule fun createCoreModule( context: Context, -): CoreModule = CoreModuleImpl(context) + initModule: InitModule +): CoreModule = CoreModuleImpl(context, initModule) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/InitModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/InitModule.kt index 89da859bf4..d931355be7 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/InitModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/InitModule.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.SystemInfo +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer import io.embrace.android.embracesdk.internal.telemetry.TelemetryService @@ -33,4 +34,6 @@ interface InitModule { * Returns the serializer used to serialize data to JSON */ val jsonSerializer: PlatformSerializer + + val instrumentedConfig: InstrumentedConfig } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/InitModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/InitModuleImpl.kt index dfe01d1b8b..9502c0c6d7 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/InitModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/InitModuleImpl.kt @@ -4,6 +4,8 @@ import io.embrace.android.embracesdk.internal.SystemInfo import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.internal.clock.NormalizedIntervalClock import io.embrace.android.embracesdk.internal.clock.SystemClock +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl import io.embrace.android.embracesdk.internal.serialization.DecoratedSerializer @@ -27,4 +29,6 @@ internal class InitModuleImpl( override val jsonSerializer: PlatformSerializer by singleton { DecoratedSerializer(EmbraceSerializer()) } + + override val instrumentedConfig: InstrumentedConfig = InstrumentedConfigImpl } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/SessionPropertiesServiceImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/SessionPropertiesServiceImplTest.kt index 22d3276dad..b3983bb352 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/SessionPropertiesServiceImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/SessionPropertiesServiceImplTest.kt @@ -5,6 +5,7 @@ import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan import io.embrace.android.embracesdk.fakes.FakePreferenceService import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -21,7 +22,10 @@ internal class SessionPropertiesServiceImplTest { fun setUp() { val fakeConfigService = FakeConfigService( - sensitiveKeysBehavior = SensitiveKeysBehaviorImpl(listOf("password")) + sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( + listOf("password"), + InstrumentedConfigImpl + ) ) fakeCurrentSessionSpan = FakeCurrentSessionSpan() service = SessionPropertiesServiceImpl( diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt index c73e46f949..0e398bc2b3 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt @@ -5,6 +5,8 @@ import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeLogRecordExporter import io.embrace.android.embracesdk.fakes.FakePreferenceService import io.embrace.android.embracesdk.fakes.FakeProcessStateService +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeProjectConfig import io.embrace.android.embracesdk.internal.SystemInfo import io.embrace.android.embracesdk.internal.comms.api.CachedConfig import io.embrace.android.embracesdk.internal.config.behavior.BehaviorThresholdCheck @@ -243,7 +245,7 @@ internal class EmbraceConfigServiceTest { openTelemetryCfg = config, preferencesService = fakePreferenceService, suppliedFramework = AppFramework.NATIVE, - appIdFromConfig = appId, + instrumentedConfig = FakeInstrumentedConfig(project = FakeProjectConfig(appId = appId)), thresholdCheck = thresholdCheck, configProvider = remoteConfigSource::getConfig, ) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImplTest.kt index ea66b8b759..641e7fff1a 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImplTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.config.behavior +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.remote.UiRemoteConfig import io.embrace.android.embracesdk.internal.utils.Uuid @@ -23,7 +24,13 @@ internal class BreadcrumbBehaviorImplTest { @Test fun testDefaults() { - with(BreadcrumbBehaviorImpl(thresholdCheck = behaviorThresholdCheck) { null }) { + with( + BreadcrumbBehaviorImpl( + thresholdCheck = behaviorThresholdCheck, + { null }, + InstrumentedConfigImpl + ) + ) { assertEquals(100, getCustomBreadcrumbLimit()) assertEquals(100, getTapBreadcrumbLimit()) assertEquals(100, getWebViewBreadcrumbLimit()) @@ -39,7 +46,11 @@ internal class BreadcrumbBehaviorImplTest { @Test fun testRemoteAndLocal() { with( - BreadcrumbBehaviorImpl(thresholdCheck = behaviorThresholdCheck) { remote } + BreadcrumbBehaviorImpl( + thresholdCheck = behaviorThresholdCheck, + { remote }, + InstrumentedConfigImpl + ) ) { assertEquals(99, getCustomBreadcrumbLimit()) assertEquals(98, getTapBreadcrumbLimit()) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImplTest.kt index a13a0aeca5..b1691c538c 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImplTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.config.behavior +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test @@ -9,7 +10,7 @@ internal class SensitiveKeysBehaviorImplTest { @Test fun `keys are not sensitive if they are not in the sensitive keys list`() { // given an empty sensitive list - val behavior = SensitiveKeysBehaviorImpl(emptyList()) + val behavior = SensitiveKeysBehaviorImpl(emptyList(), InstrumentedConfigImpl) // when checking if a key is sensitive val isSensitive = behavior.isSensitiveKey("password") @@ -21,7 +22,7 @@ internal class SensitiveKeysBehaviorImplTest { @Test fun `keys are not sensitive with a null sensitive keys list`() { // given a null sensitive list - val behavior = SensitiveKeysBehaviorImpl(null) + val behavior = SensitiveKeysBehaviorImpl(null, InstrumentedConfigImpl) // when checking if a key is sensitive val isSensitive = behavior.isSensitiveKey("password") @@ -33,7 +34,7 @@ internal class SensitiveKeysBehaviorImplTest { @Test fun `keys are sensitive when found in the sensitive keys list`() { // given a sensitive list with a key - val behavior = SensitiveKeysBehaviorImpl(listOf("password")) + val behavior = SensitiveKeysBehaviorImpl(listOf("password"), InstrumentedConfigImpl) // when checking if a key present in the list is sensitive val isSensitive = behavior.isSensitiveKey("password") @@ -45,7 +46,7 @@ internal class SensitiveKeysBehaviorImplTest { @Test fun `keys in the sensitive list are truncated to 128 characters`() { // given a sensitive list with a long key - val behavior = SensitiveKeysBehaviorImpl(listOf("a".repeat(200))) + val behavior = SensitiveKeysBehaviorImpl(listOf("a".repeat(200)), InstrumentedConfigImpl) // when checking if a key present in the list is sensitive val sensitiveKey = behavior.isSensitiveKey("a".repeat(128)) @@ -60,7 +61,8 @@ internal class SensitiveKeysBehaviorImplTest { fun `sensitive list is truncated to 10000 keys`() { // given a sensitive list with more than 10000 keys val behavior = SensitiveKeysBehaviorImpl( - List(10000) { it.toString() } + "password" + List(10000) { it.toString() } + "password", + InstrumentedConfigImpl ) // when checking a key present in the 10001st position @@ -77,7 +79,8 @@ internal class SensitiveKeysBehaviorImplTest { listOf( "password", "passkey" - ) + ), + InstrumentedConfigImpl ) // when checking if a key present in the list is sensitive diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt index 65e40ff366..9317efd933 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt @@ -10,7 +10,7 @@ internal class AndroidServicesModuleImplTest { @Test fun testDefault() { val initModule = InitModuleImpl() - val coreModule = createCoreModule(mockk(relaxed = true)) + val coreModule = createCoreModule(mockk(relaxed = true), initModule) val module = AndroidServicesModuleImpl( initModule = initModule, coreModule = coreModule, diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt index c7e2937113..1b05922c46 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt @@ -7,7 +7,6 @@ import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule import io.embrace.android.embracesdk.internal.payload.AppFramework import org.junit.Assert.assertNotNull -import org.junit.Assert.assertSame import org.junit.Test import org.junit.runner.RunWith @@ -23,22 +22,7 @@ internal class ConfigModuleImplTest { androidServicesModule = FakeAndroidServicesModule(), framework = AppFramework.NATIVE, foregroundAction = {}, - appIdFromConfig = "AbCeD", ) assertNotNull(module.configService) } - - @Test - fun `validate appId in appIdProvider is used`() { - val module = ConfigModuleImpl( - initModule = FakeInitModule(), - openTelemetryModule = FakeOpenTelemetryModule(), - workerThreadModule = FakeWorkerThreadModule(), - androidServicesModule = FakeAndroidServicesModule(), - framework = AppFramework.NATIVE, - foregroundAction = {}, - appIdFromConfig = "AbCeD", - ) - assertSame("AbCeD", module.configService.appId) - } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/CoreModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/CoreModuleImplTest.kt index eff0148d5a..bae0e15a31 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/CoreModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/CoreModuleImplTest.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.internal.injection import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.capture.metadata.AppEnvironment import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -12,10 +13,12 @@ import org.robolectric.RuntimeEnvironment @RunWith(AndroidJUnit4::class) internal class CoreModuleImplTest { + private val initModule = FakeInitModule() + @Test fun testApplicationObject() { val ctx = RuntimeEnvironment.getApplication().applicationContext - val module = CoreModuleImpl(ctx) + val module = CoreModuleImpl(ctx, initModule) assertSame(ctx, module.context) assertSame(ctx, module.application) assertNotNull(module.serviceRegistry) @@ -26,7 +29,7 @@ internal class CoreModuleImplTest { val application = RuntimeEnvironment.getApplication() val isDebug = AppEnvironment(application.applicationInfo).isDebug val ctx = application.applicationContext - val module = CoreModuleImpl(ctx) + val module = CoreModuleImpl(ctx, initModule) assertSame(application, module.application) assertEquals(isDebug, module.isDebug) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt index a759cf9f58..c1a4695a0f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt @@ -29,12 +29,13 @@ class DeliveryModuleImplTest { @Before fun setUp() { configService = FakeConfigService() + val initModule = FakeInitModule() module = DeliveryModuleImpl( FakeConfigModule(configService), - FakeInitModule(), + initModule, FakeOpenTelemetryModule(), FakeWorkerThreadModule(), - CoreModuleImpl(ApplicationProvider.getApplicationContext()), + CoreModuleImpl(ApplicationProvider.getApplicationContext(), initModule), FakeStorageModule(), FakeEssentialServiceModule(), ::FakeRequestExecutionService, diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/PayloadSourceModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/PayloadSourceModuleImplTest.kt index 8887d888cd..8fccd3a864 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/PayloadSourceModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/PayloadSourceModuleImplTest.kt @@ -30,7 +30,7 @@ internal class PayloadSourceModuleImplTest { val initModule = FakeInitModule() val module = PayloadSourceModuleImpl( initModule, - CoreModuleImpl(RuntimeEnvironment.getApplication()), + CoreModuleImpl(RuntimeEnvironment.getApplication(), initModule), FakeWorkerThreadModule(), FakeSystemServiceModule(), FakeAndroidServicesModule(), @@ -54,7 +54,7 @@ internal class PayloadSourceModuleImplTest { val initModule = FakeInitModule() val module = PayloadSourceModuleImpl( initModule, - CoreModuleImpl(RuntimeEnvironment.getApplication()), + CoreModuleImpl(RuntimeEnvironment.getApplication(), initModule), FakeWorkerThreadModule(), FakeSystemServiceModule(), FakeAndroidServicesModule(), diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/SystemServiceModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/SystemServiceModuleImplTest.kt index 8d6920e2a9..61f797ebdc 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/SystemServiceModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/SystemServiceModuleImplTest.kt @@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.internal.injection import android.os.Build import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.fakes.FakeVersionChecker +import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Before @@ -15,17 +16,18 @@ import org.robolectric.annotation.Config internal class SystemServiceModuleImplTest { private lateinit var coreModule: CoreModule + private val initModule = FakeInitModule() @Before fun setUp() { - coreModule = createCoreModule(RuntimeEnvironment.getApplication()) + coreModule = createCoreModule(RuntimeEnvironment.getApplication(), initModule) } @Config(sdk = [Build.VERSION_CODES.O]) @Test fun testVersionChecksNew() { val new = SystemServiceModuleImpl( - createCoreModule(RuntimeEnvironment.getApplication()), + createCoreModule(RuntimeEnvironment.getApplication(), initModule), FakeVersionChecker(true) ) assertNotNull(new.storageManager) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt index 234283f906..37b8b5ff2c 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt @@ -11,6 +11,7 @@ import io.embrace.android.embracesdk.fakes.createSessionBehavior import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.remote.SessionRemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.embExceptionHandling @@ -37,7 +38,10 @@ internal class EmbraceLogServiceTest { @Before fun setUp() { fakeConfigService = FakeConfigService( - sensitiveKeysBehavior = SensitiveKeysBehaviorImpl(listOf("password")) + sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( + listOf("password"), + InstrumentedConfigImpl + ) ) fakeSessionPropertiesService = FakeSessionPropertiesService() fakeLogWriter = FakeLogWriter() diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadMessageCollatorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadMessageCollatorImplTest.kt index 62b2898fe7..be09ba24da 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadMessageCollatorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadMessageCollatorImplTest.kt @@ -37,7 +37,7 @@ internal class PayloadMessageCollatorImplTest { @Before fun setUp() { initModule = FakeInitModule() - coreModule = CoreModuleImpl(RuntimeEnvironment.getApplication()) + coreModule = CoreModuleImpl(RuntimeEnvironment.getApplication(), initModule) gatingService = FakeGatingService() val sessionEnvelopeSource = SessionEnvelopeSourceImpl( metadataSource = FakeEnvelopeMetadataSource(), diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanFactoryImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanFactoryImplTest.kt index 60a2f0f670..c9f22c50b9 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanFactoryImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanFactoryImplTest.kt @@ -8,6 +8,7 @@ import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.arch.schema.PrivateSpan import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.opentelemetry.embraceSpanBuilder import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -39,7 +40,10 @@ internal class EmbraceSpanFactoryImplTest { tracer = tracer, openTelemetryClock = initModule.openTelemetryModule.openTelemetryClock, spanRepository = spanRepository, - sensitiveKeysBehavior = SensitiveKeysBehaviorImpl(listOf("password")), + sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( + listOf("password"), + InstrumentedConfigImpl + ), ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt index b61335b2dc..43910921e0 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt @@ -23,6 +23,7 @@ import io.embrace.android.embracesdk.internal.clock.millisToNanos import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.opentelemetry.embraceSpanBuilder import io.embrace.android.embracesdk.internal.payload.Span import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer @@ -53,7 +54,10 @@ internal class EmbraceSpanImplTest { .setTracerProvider(SdkTracerProvider.builder().build()).build() .getTracer(EmbraceSpanImplTest::class.java.name) - private val sensitiveKeysBehavior = SensitiveKeysBehaviorImpl(listOf("password")) + private val sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( + listOf("password"), + InstrumentedConfigImpl + ) @Before fun setup() { diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/FeatureModuleImplTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/FeatureModuleImplTest.kt index 9dd0bb706e..2ec60b7d09 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/FeatureModuleImplTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/FeatureModuleImplTest.kt @@ -18,10 +18,11 @@ internal class FeatureModuleImplTest { @Test fun testFeatureModule() { val registry = FakeFeatureRegistry() + val initModule = FakeInitModule() val module = FeatureModuleImpl( featureRegistry = registry, - coreModule = createCoreModule(mockk(relaxed = true)), - initModule = FakeInitModule(), + coreModule = createCoreModule(mockk(relaxed = true), initModule), + initModule = initModule, otelModule = FakeOpenTelemetryModule(), workerThreadModule = FakeWorkerThreadModule(), systemServiceModule = FakeSystemServiceModule(), diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/NativeFeatureModuleImplTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/NativeFeatureModuleImplTest.kt index 2015cc7ac5..19dc14a2a4 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/NativeFeatureModuleImplTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/NativeFeatureModuleImplTest.kt @@ -20,9 +20,10 @@ internal class NativeFeatureModuleImplTest { @Test fun testDefaultImplementations() { + val initModule = FakeInitModule() val module = NativeFeatureModuleImpl( - FakeInitModule(), - CoreModuleImpl(RuntimeEnvironment.getApplication()), + initModule, + CoreModuleImpl(RuntimeEnvironment.getApplication(), initModule), FakeStorageModule(), FakeEssentialServiceModule(), FakeConfigModule(), diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/StorageModuleImplTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/StorageModuleImplTest.kt index 9d35f24f6e..0c73fd81e3 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/StorageModuleImplTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/StorageModuleImplTest.kt @@ -14,7 +14,7 @@ internal class StorageModuleImplTest { @Test fun testDefaultImplementations() { val initModule = FakeInitModule() - val coreModule = CoreModuleImpl(RuntimeEnvironment.getApplication()) + val coreModule = CoreModuleImpl(RuntimeEnvironment.getApplication(), initModule) val module = createStorageModuleSupplier( initModule = initModule, coreModule = coreModule, diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/InstrumentedConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/InstrumentedConfig.kt deleted file mode 100644 index a0fbdcfa09..0000000000 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/InstrumentedConfig.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.instrumented - -/** - * This class and its contents are instrumented by the swazzler to alter its return values - * based on what values have been set in the embrace-config.json. If no value has been set, - * the default value specified in the class will be used. - * - * It's important to: - * - * (1) always use functions, as this is somewhat easier to instrument than Kotlin properties - * (2) always keep the swazzler in sync when adding new config values or altering existing ones - */ -@Swazzled -object InstrumentedConfig { - val baseUrls: BaseUrlConfig = BaseUrlConfig - val enabledFeatures: EnabledFeatureConfig = EnabledFeatureConfig - val networkCapture: NetworkCaptureConfig = NetworkCaptureConfig - val project: ProjectConfig = ProjectConfig - val redaction: RedactionConfig = RedactionConfig - val session: SessionConfig = SessionConfig -} diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/InstrumentedConfigImpl.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/InstrumentedConfigImpl.kt new file mode 100644 index 0000000000..eed51253eb --- /dev/null +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/InstrumentedConfigImpl.kt @@ -0,0 +1,47 @@ +package io.embrace.android.embracesdk.internal.config.instrumented + +import io.embrace.android.embracesdk.internal.config.instrumented.schema.BaseUrlConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.NetworkCaptureConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.ProjectConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.RedactionConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.SessionConfig + +/** + * This class and its contents are instrumented by the swazzler to alter its return values + * based on what values have been set in the embrace-config.json. If no value has been set, + * the default value specified in the class will be used. + * + * It's important to: + * + * (1) always use functions, as this is somewhat easier to instrument than Kotlin properties + * (2) always keep the swazzler in sync when adding new config values or altering existing ones + */ +@Swazzled +object InstrumentedConfigImpl : InstrumentedConfig { + override val baseUrls: BaseUrlConfig = BaseUrlConfigImpl + override val enabledFeatures: EnabledFeatureConfig = EnabledFeatureConfigImpl + override val networkCapture: NetworkCaptureConfig = NetworkCaptureConfigImpl + override val project: ProjectConfig = ProjectConfigImpl + override val redaction: RedactionConfig = RedactionConfigImpl + override val session: SessionConfig = SessionConfigImpl +} + +@Swazzled +object BaseUrlConfigImpl : BaseUrlConfig + +@Swazzled +object EnabledFeatureConfigImpl : EnabledFeatureConfig + +@Swazzled +object NetworkCaptureConfigImpl : NetworkCaptureConfig + +@Swazzled +object ProjectConfigImpl : ProjectConfig + +@Swazzled +object RedactionConfigImpl : RedactionConfig + +@Swazzled +object SessionConfigImpl : SessionConfig diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/BaseUrlConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/BaseUrlConfig.kt similarity index 82% rename from embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/BaseUrlConfig.kt rename to embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/BaseUrlConfig.kt index 6385207e9b..187b8f54d4 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/BaseUrlConfig.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/BaseUrlConfig.kt @@ -1,11 +1,9 @@ -package io.embrace.android.embracesdk.internal.config.instrumented +package io.embrace.android.embracesdk.internal.config.instrumented.schema /** * Declares the base URLs the SDK should use in HTTP requests */ -@Suppress("FunctionOnlyReturningConstant") -@Swazzled -object BaseUrlConfig { +interface BaseUrlConfig { /** * Config base URL diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/EnabledFeatureConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/EnabledFeatureConfig.kt similarity index 97% rename from embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/EnabledFeatureConfig.kt rename to embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/EnabledFeatureConfig.kt index bb215b49c8..7ede1a4c4c 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/EnabledFeatureConfig.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/EnabledFeatureConfig.kt @@ -1,11 +1,9 @@ -package io.embrace.android.embracesdk.internal.config.instrumented +package io.embrace.android.embracesdk.internal.config.instrumented.schema /** * Declares what features are enabled/disabled across the entire SDK. */ -@Suppress("FunctionOnlyReturningConstant") -@Swazzled -object EnabledFeatureConfig { +interface EnabledFeatureConfig { /** * Gates Unity ANR capture. diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/InstrumentedConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/InstrumentedConfig.kt new file mode 100644 index 0000000000..911040a6b8 --- /dev/null +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/InstrumentedConfig.kt @@ -0,0 +1,14 @@ +package io.embrace.android.embracesdk.internal.config.instrumented.schema + +/** + * Defines the locally set configuration for the SDK. This is typically set from embrace-config.json + * and instrumented by swazzler, but can be overridden for test purposes. + */ +interface InstrumentedConfig { + val baseUrls: BaseUrlConfig + val enabledFeatures: EnabledFeatureConfig + val networkCapture: NetworkCaptureConfig + val project: ProjectConfig + val redaction: RedactionConfig + val session: SessionConfig +} diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/NetworkCaptureConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/NetworkCaptureConfig.kt similarity index 95% rename from embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/NetworkCaptureConfig.kt rename to embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/NetworkCaptureConfig.kt index 326ec2c86f..e51306d410 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/NetworkCaptureConfig.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/NetworkCaptureConfig.kt @@ -1,11 +1,10 @@ -package io.embrace.android.embracesdk.internal.config.instrumented +package io.embrace.android.embracesdk.internal.config.instrumented.schema /** * Declares how the SDK should capture network requests */ @Suppress("FunctionOnlyReturningConstant") -@Swazzled -object NetworkCaptureConfig { +interface NetworkCaptureConfig { /** * The network request capture limit per domain diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/ProjectConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/ProjectConfig.kt similarity index 91% rename from embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/ProjectConfig.kt rename to embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/ProjectConfig.kt index 51677bbb83..93a24d8592 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/ProjectConfig.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/ProjectConfig.kt @@ -1,11 +1,9 @@ -package io.embrace.android.embracesdk.internal.config.instrumented +package io.embrace.android.embracesdk.internal.config.instrumented.schema /** * Declares metadata about the app project */ -@Suppress("FunctionOnlyReturningConstant") -@Swazzled -object ProjectConfig { +interface ProjectConfig { /** * The project's appId diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/RedactionConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/RedactionConfig.kt similarity index 80% rename from embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/RedactionConfig.kt rename to embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/RedactionConfig.kt index 513ffe96f6..7de255e1af 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/RedactionConfig.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/RedactionConfig.kt @@ -1,11 +1,9 @@ -package io.embrace.android.embracesdk.internal.config.instrumented +package io.embrace.android.embracesdk.internal.config.instrumented.schema /** * Declares how the SDK should redact sensitive data */ -@Suppress("FunctionOnlyReturningConstant") -@Swazzled -object RedactionConfig { +interface RedactionConfig { /** * Provides a list of sensitive keys whose values should be redacted on capture. diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/SessionConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/SessionConfig.kt similarity index 79% rename from embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/SessionConfig.kt rename to embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/SessionConfig.kt index 7b2575a51b..8f69dabb2b 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/SessionConfig.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/SessionConfig.kt @@ -1,13 +1,9 @@ -package io.embrace.android.embracesdk.internal.config.instrumented - -import com.squareup.moshi.Json +package io.embrace.android.embracesdk.internal.config.instrumented.schema /** * Declares metadata about the app project */ -@Suppress("FunctionOnlyReturningConstant") -@Swazzled -object SessionConfig { +interface SessionConfig { /** * A whitelist of session components (i.e. Breadcrumbs, Session properties, etc) that should be @@ -16,7 +12,6 @@ object SessionConfig { * * sdk_config.session.components */ - @Json(name = "components") fun getSessionComponents(): List? = null /** @@ -25,6 +20,5 @@ object SessionConfig { * * sdk_config.session.send_full_for */ - @Json(name = "send_full_for") fun getFullSessionEvents(): List = emptyList() } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ActivityFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ActivityFeatureTest.kt index ffbda44ee0..6eaecd1c66 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ActivityFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ActivityFeatureTest.kt @@ -27,11 +27,6 @@ internal class ActivityFeatureTest { var startTimeMs: Long = 0 testRule.runTest( - setupAction = { - overriddenConfigService.breadcrumbBehavior = FakeBreadcrumbBehavior( - automaticActivityCaptureEnabled = true - ) - }, testCaseAction = { recordSession { startTimeMs = clock.now() diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SensitiveKeysRedactionFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SensitiveKeysRedactionFeatureTest.kt index dcbef06937..0b8bf78680 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SensitiveKeysRedactionFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SensitiveKeysRedactionFeatureTest.kt @@ -4,6 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findSpanByName import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.testframework.IntegrationTestRule import io.embrace.android.embracesdk.testframework.assertions.assertMatches import org.junit.Rule @@ -17,7 +18,8 @@ internal class SensitiveKeysRedactionFeatureTest { val testRule: IntegrationTestRule = IntegrationTestRule() private val sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( - listOf("password") + listOf("password"), + InstrumentedConfigImpl ) @Test @@ -78,4 +80,4 @@ internal class SensitiveKeysRedactionFeatureTest { } ) } -} \ No newline at end of file +} diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index c9f5c3d4ec..6c3af513a0 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -59,7 +59,6 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( workerThreadModule = overriddenWorkerThreadModule ), val fakeAnrModule: AnrModule = FakeAnrModule(), - val fakeNativeCoreModule: NativeCoreModule = FakeNativeCoreModule(), val fakeNativeFeatureModule: FakeNativeFeatureModule = FakeNativeFeatureModule(), var cacheStorageServiceProvider: Provider = { null }, var payloadStorageServiceProvider: Provider = { null }, @@ -69,7 +68,7 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( fun createBootstrapper(): ModuleInitBootstrapper = ModuleInitBootstrapper( initModule = overriddenInitModule, openTelemetryModule = overriddenInitModule.openTelemetryModule, - coreModuleSupplier = { _ -> overriddenCoreModule }, + coreModuleSupplier = { _, _ -> overriddenCoreModule }, workerThreadModuleSupplier = { overriddenWorkerThreadModule }, androidServicesModuleSupplier = { _, _, _ -> overriddenAndroidServicesModule }, essentialServiceModuleSupplier = { initModule, configModule, openTelemetryModule, coreModule, workerThreadModule, systemServiceModule, androidServicesModule, storageModule, _, _ -> @@ -115,7 +114,7 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( ) }, anrModuleSupplier = { _, _, _ -> fakeAnrModule }, - nativeCoreModuleSupplier = { fakeNativeCoreModule }, + nativeCoreModuleSupplier = { FakeNativeCoreModule() }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> fakeNativeFeatureModule } ) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index 9446d44dff..3189b9e5ed 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -127,7 +127,7 @@ internal class ModuleInitBootstrapper( synchronized(initialized) { val result = if (!isInitialized()) { - coreModule = init(CoreModule::class) { coreModuleSupplier(context) } + coreModule = init(CoreModule::class) { coreModuleSupplier(context, initModule) } val serviceRegistry = coreModule.serviceRegistry workerThreadModule = init(WorkerThreadModule::class) { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 2fdb3c693b..097b1b36df 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -37,7 +37,7 @@ internal fun fakeModuleInitBootstrapper( fakeEmbLogger: FakeEmbLogger = FakeEmbLogger(), fakeInitModule: FakeInitModule = FakeInitModule(logger = fakeEmbLogger), fakeOpenTelemetryModule: FakeOpenTelemetryModule = FakeOpenTelemetryModule(), - coreModuleSupplier: CoreModuleSupplier = { _ -> FakeCoreModule() }, + coreModuleSupplier: CoreModuleSupplier = { _, _ -> FakeCoreModule() }, systemServiceModuleSupplier: SystemServiceModuleSupplier = { _, _ -> FakeSystemServiceModule() }, androidServicesModuleSupplier: AndroidServicesModuleSupplier = { _, _, _ -> FakeAndroidServicesModule() }, workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt index 25e53f0dbc..77801b3b23 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt @@ -33,7 +33,7 @@ internal class ModuleInitBootstrapperTest { coreModule = FakeCoreModule() moduleInitBootstrapper = ModuleInitBootstrapper( configModuleSupplier = { _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, - coreModuleSupplier = { _ -> coreModule }, + coreModuleSupplier = { _, _ -> coreModule }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> FakeNativeFeatureModule() }, logger = logger ) diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt index df6668df75..3e8b00dbd1 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt @@ -26,6 +26,7 @@ import io.embrace.android.embracesdk.internal.config.behavior.SessionBehavior import io.embrace.android.embracesdk.internal.config.behavior.SessionBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehavior import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehaviorImpl +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.LogRemoteConfig @@ -42,7 +43,7 @@ private val behaviorThresholdCheck = BehaviorThresholdCheck(Uuid::getEmbUuid) fun createAnrBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, remoteCfg: Provider = { null }, -): AnrBehavior = AnrBehaviorImpl(thresholdCheck, remoteCfg) +): AnrBehavior = AnrBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) /** * A [SessionBehaviorImpl] that returns default values. @@ -50,7 +51,7 @@ fun createAnrBehavior( fun createSessionBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, remoteCfg: Provider = { null }, -): SessionBehavior = SessionBehaviorImpl(thresholdCheck, remoteCfg) +): SessionBehavior = SessionBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) /** * A [NetworkBehaviorImpl] that returns default values. @@ -59,7 +60,7 @@ fun createNetworkBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, remoteCfg: Provider = { null }, disabledUrlPatterns: List? = null, -): NetworkBehavior = NetworkBehaviorImpl(thresholdCheck, remoteCfg, disabledUrlPatterns) +): NetworkBehavior = NetworkBehaviorImpl(thresholdCheck, remoteCfg, disabledUrlPatterns, InstrumentedConfigImpl) /** * A [BackgroundActivityBehaviorImpl] that returns default values. @@ -67,7 +68,7 @@ fun createNetworkBehavior( fun createBackgroundActivityBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, remoteCfg: Provider = { null }, -): BackgroundActivityBehavior = BackgroundActivityBehaviorImpl(thresholdCheck, remoteCfg) +): BackgroundActivityBehavior = BackgroundActivityBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) /** * A [AutoDataCaptureBehaviorImpl] that returns default values. @@ -75,7 +76,7 @@ fun createBackgroundActivityBehavior( fun createAutoDataCaptureBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, remoteCfg: Provider = { null }, -): AutoDataCaptureBehavior = AutoDataCaptureBehaviorImpl(thresholdCheck, remoteCfg) +): AutoDataCaptureBehavior = AutoDataCaptureBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) /** * A [LogMessageBehaviorImpl] that returns default values. @@ -106,7 +107,7 @@ fun createSdkModeBehavior( */ fun createSdkEndpointBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, -): SdkEndpointBehavior = SdkEndpointBehaviorImpl(thresholdCheck) +): SdkEndpointBehavior = SdkEndpointBehaviorImpl(thresholdCheck, InstrumentedConfigImpl) /** * A [AppExitInfoBehavior] that returns default values. @@ -114,7 +115,7 @@ fun createSdkEndpointBehavior( fun createAppExitInfoBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, remoteCfg: Provider = { null }, -): AppExitInfoBehavior = AppExitInfoBehaviorImpl(thresholdCheck, remoteCfg) +): AppExitInfoBehavior = AppExitInfoBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) /** * A [NetworkSpanForwardingBehaviorImpl] that returns default values. @@ -122,7 +123,11 @@ fun createAppExitInfoBehavior( fun createNetworkSpanForwardingBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, remoteConfig: Provider = { null }, -): NetworkSpanForwardingBehavior = NetworkSpanForwardingBehaviorImpl(thresholdCheck, remoteConfig) +): NetworkSpanForwardingBehavior = NetworkSpanForwardingBehaviorImpl( + thresholdCheck, + remoteConfig, + InstrumentedConfigImpl +) /** * A [WebViewVitalsBehaviorImpl] that returns default values. @@ -135,4 +140,4 @@ fun createWebViewVitalsBehavior( /** * A [SensitiveKeysBehaviorImpl] that returns default values. */ -internal fun createSensitiveKeysBehavior() = SensitiveKeysBehaviorImpl() +internal fun createSensitiveKeysBehavior() = SensitiveKeysBehaviorImpl(instrumentedConfig = InstrumentedConfigImpl) diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeBaseUrlConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeBaseUrlConfig.kt new file mode 100644 index 0000000000..e7eb30647d --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeBaseUrlConfig.kt @@ -0,0 +1,13 @@ +package io.embrace.android.embracesdk.fakes.config + +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.internal.config.instrumented.schema.BaseUrlConfig + +class FakeBaseUrlConfig( + base: BaseUrlConfig = InstrumentedConfigImpl.baseUrls, + private val configImpl: String? = base.getConfig(), + private val dataImpl: String? = base.getData(), +) : BaseUrlConfig { + override fun getConfig(): String? = configImpl + override fun getData(): String? = dataImpl +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeEnabledFeatureConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeEnabledFeatureConfig.kt new file mode 100644 index 0000000000..e6e93d59c8 --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeEnabledFeatureConfig.kt @@ -0,0 +1,53 @@ +package io.embrace.android.embracesdk.fakes.config + +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig + +@Suppress("DEPRECATION") +class FakeEnabledFeatureConfig( + base: EnabledFeatureConfig = InstrumentedConfigImpl.enabledFeatures, + private val unityAnrCapture: Boolean = base.isUnityAnrCaptureEnabled(), + private val activityBreadcrumbCapture: Boolean = base.isActivityBreadcrumbCaptureEnabled(), + private val composeClickCapture: Boolean = base.isComposeClickCaptureEnabled(), + private val viewClickCoordCapture: Boolean = base.isViewClickCoordinateCaptureEnabled(), + private val memoryWarningCapture: Boolean = base.isMemoryWarningCaptureEnabled(), + private val powerSaveCapture: Boolean = base.isPowerSaveModeCaptureEnabled(), + private val networkConnectivityCapture: Boolean = base.isNetworkConnectivityCaptureEnabled(), + private val anrCapture: Boolean = base.isAnrCaptureEnabled(), + private val diskUsageCapture: Boolean = base.isDiskUsageCaptureEnabled(), + private val jvmCrashCapture: Boolean = base.isJvmCrashCaptureEnabled(), + private val nativeCrashCapture: Boolean = base.isNativeCrashCaptureEnabled(), + private val aeiCapture: Boolean = base.isAeiCaptureEnabled(), + private val sigHandlerDetection: Boolean = base.is3rdPartySigHandlerDetectionEnabled(), + private val bgActivityCapture: Boolean = base.isBackgroundActivityCaptureEnabled(), + private val webviewBreadcrumbCapture: Boolean = base.isWebViewBreadcrumbCaptureEnabled(), + private val webviewQueryCapture: Boolean = base.isWebViewBreadcrumbQueryParamCaptureEnabled(), + private val fcmPiiCapture: Boolean = base.isFcmPiiDataCaptureEnabled(), + private val requestContentLengthCapture: Boolean = base.isRequestContentLengthCaptureEnabled(), + private val httpUrlConnectionCapture: Boolean = base.isHttpUrlConnectionCaptureEnabled(), + private val networkSpanForwarding: Boolean = base.isNetworkSpanForwardingEnabled(), +) : EnabledFeatureConfig { + + override fun isUnityAnrCaptureEnabled(): Boolean = unityAnrCapture + override fun isActivityBreadcrumbCaptureEnabled(): Boolean = activityBreadcrumbCapture + override fun isComposeClickCaptureEnabled(): Boolean = composeClickCapture + override fun isViewClickCoordinateCaptureEnabled(): Boolean = viewClickCoordCapture + + @Deprecated("Will be removed in a future release.") + override fun isMemoryWarningCaptureEnabled(): Boolean = memoryWarningCapture + override fun isPowerSaveModeCaptureEnabled(): Boolean = powerSaveCapture + override fun isNetworkConnectivityCaptureEnabled(): Boolean = networkConnectivityCapture + override fun isAnrCaptureEnabled(): Boolean = anrCapture + override fun isDiskUsageCaptureEnabled(): Boolean = diskUsageCapture + override fun isJvmCrashCaptureEnabled(): Boolean = jvmCrashCapture + override fun isNativeCrashCaptureEnabled(): Boolean = nativeCrashCapture + override fun isAeiCaptureEnabled(): Boolean = aeiCapture + override fun is3rdPartySigHandlerDetectionEnabled(): Boolean = sigHandlerDetection + override fun isBackgroundActivityCaptureEnabled(): Boolean = bgActivityCapture + override fun isWebViewBreadcrumbCaptureEnabled(): Boolean = webviewBreadcrumbCapture + override fun isWebViewBreadcrumbQueryParamCaptureEnabled(): Boolean = webviewQueryCapture + override fun isFcmPiiDataCaptureEnabled(): Boolean = fcmPiiCapture + override fun isRequestContentLengthCaptureEnabled(): Boolean = requestContentLengthCapture + override fun isHttpUrlConnectionCaptureEnabled(): Boolean = httpUrlConnectionCapture + override fun isNetworkSpanForwardingEnabled(): Boolean = networkSpanForwarding +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeInstrumentedConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeInstrumentedConfig.kt new file mode 100644 index 0000000000..18f1227577 --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeInstrumentedConfig.kt @@ -0,0 +1,21 @@ +package io.embrace.android.embracesdk.fakes.config + +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.NetworkCaptureConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.ProjectConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.RedactionConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.SessionConfig + +/** + * A fake [InstrumentedConfig] implementation that defaults to the real implementation unless an override is supplied. + */ +class FakeInstrumentedConfig( + private val base: InstrumentedConfig = InstrumentedConfigImpl, + override val baseUrls: FakeBaseUrlConfig = FakeBaseUrlConfig(base.baseUrls), + override val enabledFeatures: FakeEnabledFeatureConfig = FakeEnabledFeatureConfig(base.enabledFeatures), + override val networkCapture: NetworkCaptureConfig = FakeNetworkCaptureConfig(base.networkCapture), + override val project: ProjectConfig = FakeProjectConfig(base.project, appId = "abcde"), + override val redaction: RedactionConfig = FakeRedactionConfig(base.redaction), + override val session: SessionConfig = FakeSessionConfig(base.session), +) : InstrumentedConfig diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeNetworkCaptureConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeNetworkCaptureConfig.kt new file mode 100644 index 0000000000..10564764fb --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeNetworkCaptureConfig.kt @@ -0,0 +1,17 @@ +package io.embrace.android.embracesdk.fakes.config + +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.internal.config.instrumented.schema.NetworkCaptureConfig + +class FakeNetworkCaptureConfig( + base: NetworkCaptureConfig = InstrumentedConfigImpl.networkCapture, + private val requestLimit: Int = base.getRequestLimitPerDomain(), + private val limits: Map = base.getLimitsByDomain(), + private val ignoredRequestPatterns: List = base.getIgnoredRequestPatternList(), + private val publicKey: String? = base.getNetworkBodyCapturePublicKey(), +) : NetworkCaptureConfig { + override fun getRequestLimitPerDomain(): Int = requestLimit + override fun getLimitsByDomain(): Map = limits + override fun getIgnoredRequestPatternList(): List = ignoredRequestPatterns + override fun getNetworkBodyCapturePublicKey(): String? = publicKey +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeProjectConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeProjectConfig.kt new file mode 100644 index 0000000000..8bf65d93fd --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeProjectConfig.kt @@ -0,0 +1,19 @@ +package io.embrace.android.embracesdk.fakes.config + +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.internal.config.instrumented.schema.ProjectConfig + +class FakeProjectConfig( + base: ProjectConfig = InstrumentedConfigImpl.project, + private val appId: String? = base.getAppId(), + private val appFramework: String? = base.getAppFramework(), + private val buildId: String? = base.getBuildId(), + private val buildType: String? = base.getBuildType(), + private val buildFlavor: String? = base.getBuildFlavor(), +) : ProjectConfig { + override fun getAppId(): String? = appId + override fun getAppFramework(): String? = appFramework + override fun getBuildId(): String? = buildId + override fun getBuildType(): String? = buildType + override fun getBuildFlavor(): String? = buildFlavor +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeRedactionConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeRedactionConfig.kt new file mode 100644 index 0000000000..0ec308725e --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeRedactionConfig.kt @@ -0,0 +1,11 @@ +package io.embrace.android.embracesdk.fakes.config + +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.internal.config.instrumented.schema.RedactionConfig + +class FakeRedactionConfig( + base: RedactionConfig = InstrumentedConfigImpl.redaction, + private val sensitiveKeys: List? = base.getSensitiveKeysDenylist(), +) : RedactionConfig { + override fun getSensitiveKeysDenylist(): List? = sensitiveKeys +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeSessionConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeSessionConfig.kt new file mode 100644 index 0000000000..c902b7fdbe --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeSessionConfig.kt @@ -0,0 +1,13 @@ +package io.embrace.android.embracesdk.fakes.config + +import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.internal.config.instrumented.schema.SessionConfig + +class FakeSessionConfig( + base: SessionConfig = InstrumentedConfigImpl.session, + private val sessionComponentsImpl: List? = base.getSessionComponents(), + private val fullSessionEventsImpl: List = base.getFullSessionEvents(), +) : SessionConfig { + override fun getSessionComponents(): List? = sessionComponentsImpl + override fun getFullSessionEvents(): List = fullSessionEventsImpl +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeInitModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeInitModule.kt index a0f142a21c..f463c129a0 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeInitModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeInitModule.kt @@ -2,8 +2,10 @@ package io.embrace.android.embracesdk.fakes.injection import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeEmbLogger +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.SystemInfo import io.embrace.android.embracesdk.internal.clock.Clock +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.injection.InitModule import io.embrace.android.embracesdk.internal.injection.OpenTelemetryModule import io.embrace.android.embracesdk.internal.injection.createInitModule @@ -23,6 +25,7 @@ class FakeInitModule( logger = logger, systemInfo = systemInfo ), + override val instrumentedConfig: InstrumentedConfig = FakeInstrumentedConfig() ) : InitModule by initModule { val openTelemetryModule: OpenTelemetryModule by lazy { createOpenTelemetryModule(initModule) } diff --git a/examples/ExampleApp/settings.gradle.kts b/examples/ExampleApp/settings.gradle.kts index 3418d70bdb..35c7e2f08a 100644 --- a/examples/ExampleApp/settings.gradle.kts +++ b/examples/ExampleApp/settings.gradle.kts @@ -8,6 +8,7 @@ pluginManagement { } } mavenCentral() + mavenLocal() gradlePluginPortal() } } @@ -16,6 +17,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + mavenLocal() } } From fc50030ea24e1568c5d6a333eec4cc103ee5ff80 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Thu, 7 Nov 2024 16:43:38 +0000 Subject: [PATCH 05/28] test: alter integration tests to use config service --- .../internal/injection/ConfigModuleImpl.kt | 15 ++-- .../injection/ConfigModuleSupplier.kt | 7 +- .../testcases/EmbraceInternalInterfaceTest.kt | 37 ++++------ .../testcases/FlutterInternalInterfaceTest.kt | 18 +++-- .../testcases/NetworkRequestApiTest.kt | 30 ++++---- .../embracesdk/testcases/PublicApiTest.kt | 31 ++++---- .../testcases/PushNotificationApiTest.kt | 11 ++- .../ReactNativeInternalInterfaceTest.kt | 21 ++++-- .../embracesdk/testcases/SessionApiTest.kt | 10 ++- .../embracesdk/testcases/TracingApiTest.kt | 6 +- .../testcases/UnityInternalInterfaceTest.kt | 19 +++-- .../features/BreadcrumbFeatureTest.kt | 3 + .../features/EmbraceLoggingFeatureTest.kt | 17 +++++ .../testcases/features/JvmCrashFeatureTest.kt | 16 +++-- .../testcases/features/PruningFeatureTest.kt | 5 -- .../features/ResurrectionFeatureTest.kt | 10 ++- .../SensitiveKeysRedactionFeatureTest.kt | 22 +++--- .../features/SessionPropertiesTest.kt | 5 +- .../testcases/features/ThermalStateTest.kt | 9 --- .../features/V1DeliveryFeatureTest.kt | 21 +++--- .../testcases/features/WebviewFeatureTest.kt | 4 -- .../session/BackgroundActivityDisabledTest.kt | 6 +- .../session/BackgroundActivityTest.kt | 3 + .../testcases/session/ManualSessionTest.kt | 12 ++-- .../session/OtelSessionGatingTest.kt | 35 +++------- .../testcases/session/SessionSpamTest.kt | 3 + .../testcases/session/SessionSpanTest.kt | 3 + .../testframework/IntegrationTestRule.kt | 70 ++++++++++--------- .../actions/EmbraceActionInterface.kt | 3 - .../actions/EmbraceSetupInterface.kt | 43 ++++++------ .../testframework/server/FakeApiServer.kt | 45 +++++++++--- .../injection/ModuleInitBootstrapper.kt | 17 +++-- .../fakes/FakeModuleInitBootstrapper.kt | 2 +- .../injection/ModuleInitBootstrapperTest.kt | 2 +- .../fakes/config/FakeInstrumentedConfig.kt | 2 +- .../fakes/injection/FakeInitModule.kt | 2 +- 36 files changed, 302 insertions(+), 263 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index ab2f5b0f44..e21d146a05 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -3,8 +3,10 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.EmbraceConfigService +import io.embrace.android.embracesdk.internal.config.RemoteConfigSource import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl import io.embrace.android.embracesdk.internal.payload.AppFramework +import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.internal.worker.Worker internal class ConfigModuleImpl( @@ -14,6 +16,7 @@ internal class ConfigModuleImpl( androidServicesModule: AndroidServicesModule, framework: AppFramework, foregroundAction: () -> Unit, + remoteConfigSourceProvider: Provider = { null }, ) : ConfigModule { override val configService: ConfigService by singleton { @@ -28,9 +31,11 @@ internal class ConfigModuleImpl( } } - override val remoteConfigSource = RemoteConfigSourceImpl( - clock = initModule.clock, - backgroundWorker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), - foregroundAction = foregroundAction, - ) + override val remoteConfigSource by singleton { + remoteConfigSourceProvider() ?: RemoteConfigSourceImpl( + clock = initModule.clock, + backgroundWorker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), + foregroundAction = foregroundAction, + ) + } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt index 627e0246b3..1d725d7b59 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.internal.injection +import io.embrace.android.embracesdk.internal.config.RemoteConfigSource import io.embrace.android.embracesdk.internal.payload.AppFramework +import io.embrace.android.embracesdk.internal.utils.Provider /** * Function that returns an instance of [ConfigModule]. Matches the signature of the constructor for [ConfigModuleImpl] @@ -12,6 +14,7 @@ typealias ConfigModuleSupplier = ( androidServicesModule: AndroidServicesModule, framework: AppFramework, foregroundAction: () -> Unit, + remoteConfigSourceProvider: Provider, ) -> ConfigModule fun createConfigModule( @@ -21,11 +24,13 @@ fun createConfigModule( androidServicesModule: AndroidServicesModule, framework: AppFramework, foregroundAction: () -> Unit, + remoteConfigSourceProvider: Provider, ): ConfigModule = ConfigModuleImpl( initModule, openTelemetryModule, workerThreadModule, androidServicesModule, framework, - foregroundAction + foregroundAction, + remoteConfigSourceProvider ) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt index 4cd612964b..ec6c2d23d9 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt @@ -7,8 +7,6 @@ import io.embrace.android.embracesdk.LogType import io.embrace.android.embracesdk.assertions.findEventOfType import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.assertions.findSpansByName -import io.embrace.android.embracesdk.fakes.behavior.FakeSdkModeBehavior -import io.embrace.android.embracesdk.fakes.createNetworkBehavior import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig @@ -205,23 +203,18 @@ internal class EmbraceInternalInterfaceTest { @Test fun `access check methods work as expected`() { testRule.runTest( - setupAction = { - overriddenConfigService.networkBehavior = - createNetworkBehavior(remoteCfg = { - RemoteConfig( - disabledUrlPatterns = setOf("dontlogmebro.pizza"), - networkCaptureRules = setOf( - NetworkCaptureRuleRemoteConfig( - id = "test", - duration = 10000, - method = "GET", - urlRegex = "capture.me", - expiresIn = 10000 - ) - ) - ) - }) - }, + remoteConfig = RemoteConfig( + disabledUrlPatterns = setOf("dontlogmebro.pizza"), + networkCaptureRules = setOf( + NetworkCaptureRuleRemoteConfig( + id = "test", + duration = 10000, + method = "GET", + urlRegex = "capture.me", + expiresIn = 10000 + ) + ) + ), testCaseAction = { recordSession { assertTrue( @@ -237,7 +230,7 @@ internal class EmbraceInternalInterfaceTest { ) ) assertFalse(EmbraceInternalApi.getInstance().internalInterface.shouldCaptureNetworkBody(URL, "GET")) - assertTrue(EmbraceInternalApi.getInstance().internalInterface.isNetworkSpanForwardingEnabled()) + assertFalse(EmbraceInternalApi.getInstance().internalInterface.isNetworkSpanForwardingEnabled()) } } ) @@ -312,10 +305,8 @@ internal class EmbraceInternalInterfaceTest { @Test fun `SDK will not start if feature flag has it being disabled`() { testRule.runTest( + remoteConfig = RemoteConfig(threshold = 0), expectSdkToStart = false, - setupAction = { - overriddenConfigService.sdkModeBehavior = FakeSdkModeBehavior(sdkDisabled = true) - }, testCaseAction = { assertFalse(embrace.isStarted) } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/FlutterInternalInterfaceTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/FlutterInternalInterfaceTest.kt index b9ce27625d..0747068f44 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/FlutterInternalInterfaceTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/FlutterInternalInterfaceTest.kt @@ -1,11 +1,10 @@ -@file:Suppress("DEPRECATION") - package io.embrace.android.embracesdk.testcases import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.embrace.android.embracesdk.AppFramework.FLUTTER import io.embrace.android.embracesdk.LogExceptionType import io.embrace.android.embracesdk.fakes.FakeClock +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeProjectConfig import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule import io.embrace.android.embracesdk.internal.EmbraceInternalApi @@ -30,13 +29,17 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) internal class FlutterInternalInterfaceTest { + private val instrumentedConfig = FakeInstrumentedConfig(project = FakeProjectConfig( + appId = "abcde", + appFramework = "flutter" + )) + @Rule @JvmField val testRule: IntegrationTestRule = IntegrationTestRule { val clock = FakeClock(IntegrationTestRule.DEFAULT_SDK_START_TIME_MS) val fakeInitModule = FakeInitModule(clock = clock) EmbraceSetupInterface( - appFramework = FLUTTER, overriddenClock = clock, overriddenInitModule = fakeInitModule, overriddenWorkerThreadModule = FakeWorkerThreadModule( @@ -49,6 +52,7 @@ internal class FlutterInternalInterfaceTest { @Test fun `flutter without values should return defaults`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession() }, @@ -65,6 +69,7 @@ internal class FlutterInternalInterfaceTest { @Test fun `flutter methods work in current session`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().flutterInternalInterface.setDartVersion("28.9.1") @@ -84,6 +89,7 @@ internal class FlutterInternalInterfaceTest { @Test fun `flutter metadata already present from previous session`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().flutterInternalInterface.setDartVersion("28.9.1") @@ -104,6 +110,7 @@ internal class FlutterInternalInterfaceTest { @Test fun `setting null is ignored`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().flutterInternalInterface.setDartVersion("28.9.1") @@ -128,6 +135,7 @@ internal class FlutterInternalInterfaceTest { @Test fun `flutter values from current session override previous values`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().flutterInternalInterface.setDartVersion("28.9.1") @@ -158,6 +166,7 @@ internal class FlutterInternalInterfaceTest { val expectedLibrary = "library" testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().flutterInternalInterface.logHandledDartException( @@ -202,6 +211,7 @@ internal class FlutterInternalInterfaceTest { val expectedLibrary = "library" testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().flutterInternalInterface.logUnhandledDartException( diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt index b867a2df33..043225757c 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt @@ -1,7 +1,6 @@ package io.embrace.android.embracesdk.testcases import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.embrace.android.embracesdk.fakes.createNetworkBehavior import io.embrace.android.embracesdk.internal.clock.millisToNanos import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig @@ -180,23 +179,18 @@ internal class NetworkRequestApiTest { @Test fun `disabled URLs not recorded`() { testRule.runTest( - setupAction = { - overriddenConfigService.networkBehavior = - createNetworkBehavior(remoteCfg = { - RemoteConfig( - disabledUrlPatterns = setOf("dontlogmebro.pizza"), - networkCaptureRules = setOf( - NetworkCaptureRuleRemoteConfig( - id = "test", - duration = 10000, - method = "GET", - urlRegex = "capture.me", - expiresIn = 10000 - ) - ) - ) - }) - }, + remoteConfig = RemoteConfig( + disabledUrlPatterns = setOf("dontlogmebro.pizza"), + networkCaptureRules = setOf( + NetworkCaptureRuleRemoteConfig( + id = "test", + duration = 10000, + method = "GET", + urlRegex = "capture.me", + expiresIn = 10000 + ) + ) + ), testCaseAction = { recordSession { clock.tick(5) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt index 142d57d5b1..fe89c565c8 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt @@ -2,12 +2,12 @@ package io.embrace.android.embracesdk.testcases import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.LastRunEndState -import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkSpanForwardingBehavior -import io.embrace.android.embracesdk.fakes.createSdkModeBehavior +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.testframework.IntegrationTestRule -import io.embrace.android.embracesdk.testframework.actions.EmbraceSetupInterface import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotEquals @@ -28,24 +28,20 @@ internal class PublicApiTest { val validPattern = Regex("^00-" + "[0-9a-fA-F]{32}" + "-" + "[0-9a-fA-F]{16}" + "-01$") } + private val instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(networkSpanForwarding = true)) + @Rule @JvmField - val testRule: IntegrationTestRule = IntegrationTestRule { - EmbraceSetupInterface().apply { - overriddenConfigService.networkSpanForwardingBehavior = - FakeNetworkSpanForwardingBehavior(true) - } - } + val testRule: IntegrationTestRule = IntegrationTestRule() @Test fun `SDK can start`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, preSdkStartAction = { assertFalse(embrace.isStarted) }, testCaseAction = { - assertEquals(AppFramework.NATIVE, configService.appFramework) - assertFalse(configService.sdkModeBehavior.isSdkDisabled()) assertTrue(embrace.isStarted) } ) @@ -54,8 +50,8 @@ internal class PublicApiTest { @Test fun `SDK start defaults to native app framework`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { - assertEquals(AppFramework.NATIVE, configService.appFramework) assertTrue(embrace.isStarted) } ) @@ -64,10 +60,9 @@ internal class PublicApiTest { @Test fun `SDK disabled via config cannot start`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, + remoteConfig = RemoteConfig(0), expectSdkToStart = false, - setupAction = { - overriddenConfigService.sdkModeBehavior = createSdkModeBehavior { RemoteConfig(0) } - }, testCaseAction = { assertFalse(embrace.isStarted) } @@ -77,6 +72,7 @@ internal class PublicApiTest { @Test fun `getCurrentSessionId returns null when SDK is not started`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, startSdk = false, testCaseAction = { assertNull(embrace.currentSessionId) @@ -87,6 +83,7 @@ internal class PublicApiTest { @Test fun `getCurrentSessionId returns sessionId when SDK is started and foreground session is active`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { assertEquals( @@ -104,6 +101,8 @@ internal class PublicApiTest { var foregroundSessionId: String? = null var backgroundSessionId: String? = null testRule.runTest( + remoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { foregroundSessionId = embrace.currentSessionId @@ -120,6 +119,7 @@ internal class PublicApiTest { @Test fun `getLastRunEndState() behave as expected`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, preSdkStartAction = { assertEquals(LastRunEndState.INVALID, embrace.lastRunEndState) }, @@ -132,6 +132,7 @@ internal class PublicApiTest { @Test fun `ensure all generated W3C traceparent conforms to the expected format`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { repeat(100) { assertTrue(validPattern.matches(checkNotNull(embrace.generateW3cTraceparent()))) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PushNotificationApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PushNotificationApiTest.kt index 6472060b28..65858c0fc4 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PushNotificationApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PushNotificationApiTest.kt @@ -3,7 +3,8 @@ package io.embrace.android.embracesdk.testcases import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findEventOfType import io.embrace.android.embracesdk.assertions.findSessionSpan -import io.embrace.android.embracesdk.fakes.FakeBreadcrumbBehavior +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.SessionPayload @@ -84,11 +85,9 @@ internal class PushNotificationApiTest { @Test fun `log push notification with pii`() { testRule.runTest( - setupAction = { - overriddenConfigService.breadcrumbBehavior = FakeBreadcrumbBehavior( - captureFcmPiiDataEnabled = true - ) - }, + instrumentedConfig = FakeInstrumentedConfig( + enabledFeatures = FakeEnabledFeatureConfig(fcmPiiCapture = true), + ), testCaseAction = { recordSession { embrace.logPushNotification("title", "body", "from", "id", 1, 2, true, true) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/ReactNativeInternalInterfaceTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/ReactNativeInternalInterfaceTest.kt index f51582828e..e8af191bfd 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/ReactNativeInternalInterfaceTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/ReactNativeInternalInterfaceTest.kt @@ -1,12 +1,11 @@ -@file:Suppress("DEPRECATION") - package io.embrace.android.embracesdk.testcases import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.embrace.android.embracesdk.AppFramework.REACT_NATIVE import io.embrace.android.embracesdk.assertions.findSpanOfType import io.embrace.android.embracesdk.assertions.findSpanSnapshotOfType import io.embrace.android.embracesdk.assertions.findSpansByName +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeProjectConfig import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.clock.nanosToMillis @@ -26,15 +25,19 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) internal class ReactNativeInternalInterfaceTest { + private val instrumentedConfig = FakeInstrumentedConfig(project = FakeProjectConfig( + appId = "abcde", + appFramework = "react_native" + )) + @Rule @JvmField - val testRule: IntegrationTestRule = IntegrationTestRule { - EmbraceSetupInterface(appFramework = REACT_NATIVE) - } + val testRule: IntegrationTestRule = IntegrationTestRule() @Test fun `react native without values should return defaults`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession() }, @@ -51,6 +54,7 @@ internal class ReactNativeInternalInterfaceTest { @Test fun `react native methods work in current session`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().reactNativeInternalInterface.setReactNativeVersionNumber("28.9.1") @@ -72,6 +76,7 @@ internal class ReactNativeInternalInterfaceTest { @Test fun `react native metadata already present from previous session`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().reactNativeInternalInterface.setReactNativeVersionNumber("28.9.1") @@ -95,6 +100,7 @@ internal class ReactNativeInternalInterfaceTest { @Test fun `react native values from current session override previous values`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().reactNativeInternalInterface.setReactNativeVersionNumber("28.9.1") @@ -123,6 +129,7 @@ internal class ReactNativeInternalInterfaceTest { @Test fun `react native action`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().reactNativeInternalInterface.logRnAction( @@ -163,6 +170,7 @@ internal class ReactNativeInternalInterfaceTest { @Test fun `react native log RN view`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().reactNativeInternalInterface.logRnView("HomeScreen") @@ -197,6 +205,7 @@ internal class ReactNativeInternalInterfaceTest { @Test fun `react native log RN view same name`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().reactNativeInternalInterface.logRnView("HomeScreen") diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/SessionApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/SessionApiTest.kt index d98371c4b4..8f1b716641 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/SessionApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/SessionApiTest.kt @@ -3,6 +3,8 @@ package io.embrace.android.embracesdk.testcases import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.ResourceReader import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.clock.millisToNanos import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.opentelemetry.embFreeDiskBytes @@ -23,12 +25,7 @@ internal class SessionApiTest { @Rule @JvmField - val testRule: IntegrationTestRule = IntegrationTestRule { - EmbraceSetupInterface().apply { - overriddenConfigService.autoDataCaptureBehavior = - FakeAutoDataCaptureBehavior(diskUsageReportingEnabled = false) - } - } + val testRule: IntegrationTestRule = IntegrationTestRule() /** * Verifies that a session end message is sent. @@ -39,6 +36,7 @@ internal class SessionApiTest { var startTime: Long = -1 testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(diskUsageCapture = false, bgActivityCapture = true)), testCaseAction = { startTime = clock.now() recordSession { diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt index 5ba8ae6103..b55fbb5613 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt @@ -5,6 +5,8 @@ import io.embrace.android.embracesdk.arch.assertIsTypePerformance import io.embrace.android.embracesdk.assertions.assertEmbraceSpanData import io.embrace.android.embracesdk.concurrency.SingleThreadTestScheduledExecutor import io.embrace.android.embracesdk.fakes.FakeSpanExporter +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fixtures.TOO_LONG_ATTRIBUTE_KEY import io.embrace.android.embracesdk.fixtures.TOO_LONG_ATTRIBUTE_VALUE @@ -58,6 +60,7 @@ internal class TracingApiTest { val spanExporter = FakeSpanExporter() testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(bgActivityCapture = true)), preSdkStartAction = { testStartTimeMs = clock.now() clock.tick(100L) @@ -322,9 +325,6 @@ internal class TracingApiTest { @Test fun `can only create span if there is a valid session`() { testRule.runTest( - setupAction = { - overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } - }, preSdkStartAction = { assertNull(embrace.startSpan("test")) }, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/UnityInternalInterfaceTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/UnityInternalInterfaceTest.kt index 029b6d7612..874008e65f 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/UnityInternalInterfaceTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/UnityInternalInterfaceTest.kt @@ -1,13 +1,11 @@ -@file:Suppress("DEPRECATION") - package io.embrace.android.embracesdk.testcases import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.embrace.android.embracesdk.AppFramework.UNITY +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeProjectConfig import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.testframework.IntegrationTestRule -import io.embrace.android.embracesdk.testframework.actions.EmbraceSetupInterface import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Rule @@ -20,15 +18,19 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) internal class UnityInternalInterfaceTest { + private val instrumentedConfig = FakeInstrumentedConfig(project = FakeProjectConfig( + appId = "abcde", + appFramework = "unity" + )) + @Rule @JvmField - val testRule: IntegrationTestRule = IntegrationTestRule { - EmbraceSetupInterface(appFramework = UNITY) - } + val testRule: IntegrationTestRule = IntegrationTestRule() @Test fun `unity without values should return defaults`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession() }, @@ -45,6 +47,7 @@ internal class UnityInternalInterfaceTest { @Test fun `unity methods work in current session`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().unityInternalInterface.setUnityMetaData( @@ -68,6 +71,7 @@ internal class UnityInternalInterfaceTest { @Test fun `unity metadata already present from previous session`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().unityInternalInterface.setUnityMetaData( @@ -92,6 +96,7 @@ internal class UnityInternalInterfaceTest { @Test fun `unity values from current session override previous values`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { EmbraceInternalApi.getInstance().unityInternalInterface.setUnityMetaData( diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/BreadcrumbFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/BreadcrumbFeatureTest.kt index 92ac750c92..f8794e8f08 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/BreadcrumbFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/BreadcrumbFeatureTest.kt @@ -4,6 +4,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findEventOfType import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.internal.arch.schema.EmbType +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.payload.ApplicationState import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.SessionPayload @@ -23,6 +25,7 @@ internal class BreadcrumbFeatureTest { @Test fun `custom breadcrumb feature`() { testRule.runTest( + remoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), testCaseAction = { recordSession { embrace.addBreadcrumb("Hello, world!") diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/EmbraceLoggingFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/EmbraceLoggingFeatureTest.kt index 55b27ff546..70666ce0de 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/EmbraceLoggingFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/EmbraceLoggingFeatureTest.kt @@ -4,6 +4,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.LogExceptionType import io.embrace.android.embracesdk.Severity import io.embrace.android.embracesdk.fakes.FakeClock +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule import io.embrace.android.embracesdk.internal.payload.Envelope @@ -21,6 +23,8 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) internal class EmbraceLoggingFeatureTest { + private val instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(bgActivityCapture = true)) + @Rule @JvmField val testRule: IntegrationTestRule = IntegrationTestRule { @@ -39,6 +43,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log info message sent in foreground`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { embrace.logInfo("test message") @@ -61,6 +66,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log warning message sent in background`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { embrace.logWarning("test message") flushLogs() @@ -80,6 +86,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log error message sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { embrace.logError("test message") flushLogs() @@ -99,6 +106,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log messages with different severities sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { Severity.values().forEach { severity -> val expectedMessage = "test message ${severity.name}" @@ -125,6 +133,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log messages with different severities and properties sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { Severity.values().forEach { severity -> val expectedMessage = "test message ${severity.name}" @@ -151,6 +160,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log exception message sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { embrace.logException(testException) flushLogs() @@ -175,6 +185,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log exception with different severities sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { embrace.logException(testException, Severity.INFO) flushLogs() @@ -199,6 +210,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log exception with different severities and properties sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { Severity.values().forEach { severity -> embrace.logException( @@ -232,6 +244,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log exception with different severities, properties, and custom message sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { Severity.values().forEach { severity -> val expectedMessage = "test message ${severity.name}" @@ -264,6 +277,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log custom stacktrace message sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { embrace.logCustomStacktrace(stacktrace) flushLogs() @@ -286,6 +300,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log custom stacktrace with different severities sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { Severity.values().forEach { severity -> embrace.logCustomStacktrace(stacktrace, severity) @@ -313,6 +328,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log custom stacktrace with different severities and properties sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { Severity.values().forEach { severity -> embrace.logCustomStacktrace(stacktrace, severity, customProperties) @@ -341,6 +357,7 @@ internal class EmbraceLoggingFeatureTest { @Test fun `log custom stacktrace with different severities, properties, and custom message sent`() { testRule.runTest( + instrumentedConfig = instrumentedConfig, testCaseAction = { Severity.values().forEach { severity -> val expectedMessage = "test message ${severity.name}" diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/JvmCrashFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/JvmCrashFeatureTest.kt index 8a7c369948..37c5dc5937 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/JvmCrashFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/JvmCrashFeatureTest.kt @@ -1,10 +1,13 @@ package io.embrace.android.embracesdk.testcases.features import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeProjectConfig import io.embrace.android.embracesdk.internal.EmbraceInternalApi +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.embCrashId import io.embrace.android.embracesdk.internal.opentelemetry.embState -import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.payload.ApplicationState import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.LegacyExceptionInfo @@ -59,6 +62,7 @@ internal class JvmCrashFeatureTest { @Test fun `app crash in the background generates a crash log`() { testRule.runTest( + remoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), testCaseAction = { simulateJvmUncaughtException(testException) }, @@ -72,13 +76,15 @@ internal class JvmCrashFeatureTest { ) } - @Suppress("DEPRECATION") @Test fun `React Native crash generates an OTel Log and matches the crashId in the session`() { testRule.runTest( - setupAction = { - overriddenConfigService.appFramework = AppFramework.REACT_NATIVE - }, + instrumentedConfig = FakeInstrumentedConfig( + project = FakeProjectConfig( + appId = "abcde", + appFramework = "react_native" + ) + ), testCaseAction = { recordSession { EmbraceInternalApi.getInstance().reactNativeInternalInterface.logUnhandledJsException( diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/PruningFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/PruningFeatureTest.kt index 48ece0206d..1937446427 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/PruningFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/PruningFeatureTest.kt @@ -4,11 +4,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeEmbLogger -import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fixtures.fakeSessionStoredTelemetryMetadata import io.embrace.android.embracesdk.internal.comms.delivery.NetworkStatus -import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.delivery.StoredTelemetryMetadata import io.embrace.android.embracesdk.internal.spans.findAttributeValue import io.embrace.android.embracesdk.internal.worker.Worker.Priority.DataPersistenceWorker @@ -42,9 +40,6 @@ internal class PruningFeatureTest { @Test fun `stored payloads are pruned appropriately`() { testRule.runTest( - setupAction = { - overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } - }, testCaseAction = { simulateNetworkChange(NetworkStatus.NOT_REACHABLE) repeat(STORAGE_LIMIT + OVERAGE) { k -> diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ResurrectionFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ResurrectionFeatureTest.kt index a7a0e92ae0..939b0b1770 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ResurrectionFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ResurrectionFeatureTest.kt @@ -5,9 +5,12 @@ import io.embrace.android.embracesdk.assertions.getSessionId import io.embrace.android.embracesdk.fakes.FakeNativeCrashService import io.embrace.android.embracesdk.fakes.FakePayloadStorageService import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.fakeIncompleteSessionEnvelope import io.embrace.android.embracesdk.fixtures.fakeCachedSessionStoredTelemetryMetadata import io.embrace.android.embracesdk.internal.clock.nanosToMillis +import io.embrace.android.embracesdk.internal.config.remote.KillSwitchRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.embCrashId import io.embrace.android.embracesdk.internal.payload.NativeCrashData import io.embrace.android.embracesdk.internal.payload.Span @@ -45,9 +48,6 @@ internal class ResurrectionFeatureTest { ) testRule.runTest( setupAction = { - overriddenConfigService.autoDataCaptureBehavior = FakeAutoDataCaptureBehavior( - v2StorageEnabled = true - ) cacheStorageService.addPayload(sessionMetadata, deadSessionEnvelope) cacheStorageServiceProvider = { cacheStorageService } val nativeCrashService = fakeNativeFeatureModule.nativeCrashService as FakeNativeCrashService @@ -84,11 +84,9 @@ internal class ResurrectionFeatureTest { @Test fun `resurrection attempt with v2 delivery layer off does not crash the SDK`() { testRule.runTest( + remoteConfig = RemoteConfig(killSwitchConfig = KillSwitchRemoteConfig(v2StoragePct = 0f)), setupAction = { useMockWebServer = false - overriddenConfigService.autoDataCaptureBehavior = FakeAutoDataCaptureBehavior( - v2StorageEnabled = false - ) }, testCaseAction = { recordSession() diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SensitiveKeysRedactionFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SensitiveKeysRedactionFeatureTest.kt index 0b8bf78680..ab8b057587 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SensitiveKeysRedactionFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SensitiveKeysRedactionFeatureTest.kt @@ -2,9 +2,9 @@ package io.embrace.android.embracesdk.testcases.features import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findSpanByName +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeRedactionConfig import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL -import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.testframework.IntegrationTestRule import io.embrace.android.embracesdk.testframework.assertions.assertMatches import org.junit.Rule @@ -13,21 +13,19 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) internal class SensitiveKeysRedactionFeatureTest { + + private val instrumentedConfig = FakeInstrumentedConfig( + redaction = FakeRedactionConfig(sensitiveKeys = listOf("password")) + ) + @Rule @JvmField val testRule: IntegrationTestRule = IntegrationTestRule() - private val sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( - listOf("password"), - InstrumentedConfigImpl - ) - @Test fun `custom span properties are redacted if they are sensitive`() { testRule.runTest( - setupAction = { - overriddenConfigService.sensitiveKeysBehavior = sensitiveKeysBehavior - }, + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { embrace.startSpan("test span")?.apply { @@ -51,9 +49,7 @@ internal class SensitiveKeysRedactionFeatureTest { @Test fun `custom span events are redacted if they are sensitive`() { testRule.runTest( - setupAction = { - overriddenConfigService.sensitiveKeysBehavior = sensitiveKeysBehavior - }, + instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { embrace.startSpan("test span")?.apply { diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SessionPropertiesTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SessionPropertiesTest.kt index 46ad746650..ad3587b6e4 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SessionPropertiesTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/SessionPropertiesTest.kt @@ -3,6 +3,8 @@ package io.embrace.android.embracesdk.testcases.features import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.Embrace import io.embrace.android.embracesdk.assertions.findSessionSpan +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.payload.ApplicationState @@ -27,6 +29,7 @@ internal class SessionPropertiesTest { @Test fun `session properties additions and removal works at all stages app state transition`() { testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(bgActivityCapture = true)), setupAction = { setupPermanentProperties() }, @@ -69,7 +72,6 @@ internal class SessionPropertiesTest { fun `session properties work with background activity disabled`() { testRule.runTest( setupAction = { - overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } setupPermanentProperties() }, testCaseAction = { @@ -116,7 +118,6 @@ internal class SessionPropertiesTest { fun `session properties are persisted in cached payloads when bg activities are disabled`() { testRule.runTest( setupAction = { - overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } setupPermanentProperties() }, testCaseAction = { diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ThermalStateTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ThermalStateTest.kt index 0a91c43f2d..24ea35d84b 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ThermalStateTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ThermalStateTest.kt @@ -5,7 +5,6 @@ import android.os.PowerManager import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findSpanSnapshotOfType import io.embrace.android.embracesdk.assertions.findSpansOfType -import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.spans.findAttributeValue @@ -26,16 +25,11 @@ internal class ThermalStateFeatureTest { @JvmField val testRule: IntegrationTestRule = IntegrationTestRule() - private val autoDataCaptureBehavior = FakeAutoDataCaptureBehavior(thermalStatusCaptureEnabled = true) - @Test fun `single thermal state change generates a snapshot`() { var startTimeMs = 0L testRule.runTest( - setupAction = { - overriddenConfigService.autoDataCaptureBehavior = autoDataCaptureBehavior - }, testCaseAction = { recordSession { startTimeMs = clock.now() @@ -64,9 +58,6 @@ internal class ThermalStateFeatureTest { var startTimeMs = 0L testRule.runTest( - setupAction = { - overriddenConfigService.autoDataCaptureBehavior = autoDataCaptureBehavior - }, testCaseAction = { recordSession { startTimeMs = clock.now() diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/V1DeliveryFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/V1DeliveryFeatureTest.kt index 42225d2031..2e18f3444c 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/V1DeliveryFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/V1DeliveryFeatureTest.kt @@ -7,7 +7,11 @@ import android.content.Context import android.os.Build import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.KillSwitchRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.payload.ApplicationState import io.embrace.android.embracesdk.testframework.IntegrationTestRule import io.embrace.android.embracesdk.testframework.assertions.assertMatches @@ -30,15 +34,17 @@ internal class V1DeliveryFeatureTest { @JvmField val testRule: IntegrationTestRule = IntegrationTestRule() - private val behavior = FakeAutoDataCaptureBehavior(v2StorageEnabled = false) + private val remoteConfig = RemoteConfig( + killSwitchConfig = KillSwitchRemoteConfig(v2StoragePct = 0f), + backgroundActivityConfig = BackgroundActivityRemoteConfig(100f) + ) - @Suppress("DEPRECATION") @Test fun `v1 session delivery`() { testRule.runTest( + remoteConfig = remoteConfig, setupAction = { useMockWebServer = false - overriddenConfigService.autoDataCaptureBehavior = behavior }, testCaseAction = { recordSession { @@ -52,13 +58,12 @@ internal class V1DeliveryFeatureTest { ) } - @Suppress("DEPRECATION") @Test fun `v1 background activity delivery`() { testRule.runTest( + remoteConfig = remoteConfig, setupAction = { useMockWebServer = false - overriddenConfigService.autoDataCaptureBehavior = behavior }, testCaseAction = { embrace.setUserIdentifier("foo") @@ -74,9 +79,9 @@ internal class V1DeliveryFeatureTest { @Test fun `v1 crash delivery`() { testRule.runTest( + remoteConfig = remoteConfig, setupAction = { useMockWebServer = false - overriddenConfigService.autoDataCaptureBehavior = behavior }, testCaseAction = { recordSession { @@ -93,9 +98,9 @@ internal class V1DeliveryFeatureTest { @Test fun `v1 log delivery`() { testRule.runTest( + remoteConfig = remoteConfig, setupAction = { useMockWebServer = false - overriddenConfigService.autoDataCaptureBehavior = behavior setupFakeAeiData() }, testCaseAction = { diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/WebviewFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/WebviewFeatureTest.kt index 896696fd73..1a28dbf3d4 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/WebviewFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/WebviewFeatureTest.kt @@ -5,7 +5,6 @@ import com.squareup.moshi.Types import io.embrace.android.embracesdk.ResourceReader import io.embrace.android.embracesdk.assertions.findEventsOfType import io.embrace.android.embracesdk.assertions.findSessionSpan -import io.embrace.android.embracesdk.fakes.behavior.FakeWebViewVitalsBehavior import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.payload.WebVital import io.embrace.android.embracesdk.internal.payload.WebVitalType @@ -33,9 +32,6 @@ internal class WebviewFeatureTest { @Test fun `webview info feature`() { testRule.runTest( - setupAction = { - overriddenConfigService.webViewVitalsBehavior = FakeWebViewVitalsBehavior(50, true) - }, testCaseAction = { recordSession { embrace.trackWebViewPerformance("myWebView", expectedCompleteData) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityDisabledTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityDisabledTest.kt index ee2037f498..2d2e445343 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityDisabledTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityDisabledTest.kt @@ -5,12 +5,10 @@ import io.embrace.android.embracesdk.assertions.findEventsOfType import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.assertions.getSessionId import io.embrace.android.embracesdk.fakes.FakeClock -import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.clock.nanosToMillis -import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.embCleanExit import io.embrace.android.embracesdk.internal.opentelemetry.embColdStart import io.embrace.android.embracesdk.internal.opentelemetry.embProcessIdentifier @@ -58,9 +56,7 @@ internal class BackgroundActivityDisabledTest { overriddenClock = clock, overriddenInitModule = initModule, overriddenWorkerThreadModule = workerThreadModule, - ).apply { - overriddenConfigService.backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 0f) } - } + ) } @Test diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityTest.kt index bcbd43259f..5e9d3ee066 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityTest.kt @@ -5,6 +5,8 @@ import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.assertions.getSessionId import io.embrace.android.embracesdk.assertions.hasSpanSnapshotsOfType import io.embrace.android.embracesdk.internal.arch.schema.EmbType +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.embSessionNumber import io.embrace.android.embracesdk.internal.payload.ApplicationState import io.embrace.android.embracesdk.testframework.IntegrationTestRule @@ -29,6 +31,7 @@ internal class BackgroundActivityTest { @Test fun `bg activity messages are recorded`() { testRule.runTest( + remoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), testCaseAction = { recordSession() clock.tick(30000) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/ManualSessionTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/ManualSessionTest.kt index fc7688de3f..520ac7ebb7 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/ManualSessionTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/ManualSessionTest.kt @@ -2,7 +2,8 @@ package io.embrace.android.embracesdk.testcases.session import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findSessionSpan -import io.embrace.android.embracesdk.fakes.behavior.FakeSessionBehavior +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.SessionRemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.embSessionNumber import io.embrace.android.embracesdk.testframework.IntegrationTestRule import io.embrace.android.embracesdk.testframework.assertions.assertMatches @@ -47,10 +48,7 @@ internal class ManualSessionTest { @Test fun `calling endSession when session control enabled does not end sessions`() { testRule.runTest( - setupAction = { - overriddenConfigService.sessionBehavior = - FakeSessionBehavior(sessionControlEnabled = true) - }, + remoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig(isEnabled = true)), testCaseAction = { recordSession { clock.tick(10000) @@ -67,9 +65,7 @@ internal class ManualSessionTest { @Test fun `calling endSession when state session is below 5s has no effect`() { testRule.runTest( - setupAction = { - overriddenConfigService.sessionBehavior = FakeSessionBehavior(sessionControlEnabled = true) - }, + remoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig(isEnabled = true)), testCaseAction = { recordSession { clock.tick(1000) // not enough to trigger new session diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/OtelSessionGatingTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/OtelSessionGatingTest.kt index ccbf055999..ac9ca4240a 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/OtelSessionGatingTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/OtelSessionGatingTest.kt @@ -5,8 +5,6 @@ import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.assertions.hasEventOfType import io.embrace.android.embracesdk.assertions.hasSpanOfType import io.embrace.android.embracesdk.fakes.FakeAnrService -import io.embrace.android.embracesdk.fakes.FakeConfigService -import io.embrace.android.embracesdk.fakes.createSessionBehavior import io.embrace.android.embracesdk.fakes.fakeCompletedAnrInterval import io.embrace.android.embracesdk.fakes.fakeInProgressAnrInterval import io.embrace.android.embracesdk.internal.EmbraceInternalApi @@ -17,7 +15,6 @@ import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.SessionPayload import io.embrace.android.embracesdk.testframework.IntegrationTestRule import io.embrace.android.embracesdk.testframework.actions.EmbraceActionInterface -import io.embrace.android.embracesdk.testframework.actions.EmbraceSetupInterface import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Rule @@ -30,26 +27,14 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) internal class OtelSessionGatingTest { - private var gatingConfig = SessionRemoteConfig( - fullSessionEvents = setOf(), - sessionComponents = setOf() - ) - @Rule @JvmField - val testRule: IntegrationTestRule = IntegrationTestRule { - EmbraceSetupInterface( - overriddenConfigService = FakeConfigService( - sessionBehavior = createSessionBehavior(remoteCfg = { RemoteConfig(sessionConfig = gatingConfig) }) - ) - ) - } + val testRule: IntegrationTestRule = IntegrationTestRule() @Test fun `session sent in full without gating`() { - gatingConfig = SessionRemoteConfig() - testRule.runTest( + remoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig()), testCaseAction = { simulateSession() }, @@ -62,14 +47,16 @@ internal class OtelSessionGatingTest { @Test fun `session gated`() { - gatingConfig = SessionRemoteConfig( - sessionComponents = emptySet(), - fullSessionEvents = setOf( - "crashes", - "errors" - ) - ) testRule.runTest( + remoteConfig = RemoteConfig( + sessionConfig = SessionRemoteConfig( + sessionComponents = emptySet(), + fullSessionEvents = setOf( + "crashes", + "errors" + ) + ) + ), testCaseAction = { simulateSession() }, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/SessionSpamTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/SessionSpamTest.kt index 80c7aa2b15..e94a3a8731 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/SessionSpamTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/SessionSpamTest.kt @@ -2,6 +2,8 @@ package io.embrace.android.embracesdk.testcases.session import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.getSessionId +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.payload.ApplicationState import io.embrace.android.embracesdk.testframework.IntegrationTestRule import org.junit.Assert.assertEquals @@ -21,6 +23,7 @@ internal class SessionSpamTest { @Test fun `session messages are recorded`() { testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(bgActivityCapture = true)), testCaseAction = { repeat(SESSION_COUNT) { recordSession { diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/SessionSpanTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/SessionSpanTest.kt index 478f7642ee..785182b51d 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/SessionSpanTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/SessionSpanTest.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.testcases.session import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.payload.getSessionSpan import io.embrace.android.embracesdk.testframework.IntegrationTestRule import org.junit.Assert.assertEquals @@ -21,6 +23,7 @@ internal class SessionSpanTest { val ids = mutableListOf() testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(bgActivityCapture = true)), testCaseAction = { recordSession { ids.add(embrace.currentSessionId) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt index 293b0dedd2..582bc87496 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt @@ -6,16 +6,17 @@ import io.embrace.android.embracesdk.EmbraceImpl import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeDeliveryService -import io.embrace.android.embracesdk.fakes.behavior.FakeSdkEndpointBehavior +import io.embrace.android.embracesdk.fakes.config.FakeBaseUrlConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.injection.FakeCoreModule import io.embrace.android.embracesdk.fakes.injection.FakeDeliveryModule +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.injection.CoreModule import io.embrace.android.embracesdk.internal.injection.DeliveryModule import io.embrace.android.embracesdk.internal.injection.EssentialServiceModule import io.embrace.android.embracesdk.internal.injection.EssentialServiceModuleImpl import io.embrace.android.embracesdk.internal.injection.InitModule import io.embrace.android.embracesdk.internal.injection.ModuleInitBootstrapper -import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.testframework.actions.EmbraceActionInterface import io.embrace.android.embracesdk.testframework.actions.EmbraceOtelExportAssertionInterface @@ -94,6 +95,7 @@ internal class IntegrationTestRule( private lateinit var otelAssertion: EmbraceOtelExportAssertionInterface private lateinit var spanExporter: FilteredSpanExporter private lateinit var embraceImpl: EmbraceImpl + private lateinit var baseUrl: String lateinit var bootstrapper: ModuleInitBootstrapper @@ -104,6 +106,8 @@ internal class IntegrationTestRule( */ inline fun runTest( startSdk: Boolean = true, + instrumentedConfig: FakeInstrumentedConfig = FakeInstrumentedConfig(), + remoteConfig: RemoteConfig = RemoteConfig(), expectSdkToStart: Boolean = startSdk, setupAction: EmbraceSetupInterface.() -> Unit = {}, preSdkStartAction: EmbracePreSdkStartInterface.() -> Unit = {}, @@ -111,6 +115,26 @@ internal class IntegrationTestRule( assertAction: EmbracePayloadAssertionInterface.() -> Unit = {}, otelExportAssertion: EmbraceOtelExportAssertionInterface.() -> Unit = {}, ) { + setup = embraceSetupInterfaceSupplier() + var apiServer: FakeApiServer? = null + + if (setup.useMockWebServer) { + apiServer = FakeApiServer(remoteConfig) + val server: MockWebServer = MockWebServer().apply { + protocols = listOf(Protocol.HTTP_2, Protocol.HTTP_1_1) + dispatcher = apiServer + start() + } + baseUrl = server.url("api").toString() + } + + preSdkStart = EmbracePreSdkStartInterface(setup) + bootstrapper = setup.createBootstrapper(prepareConfig(instrumentedConfig), remoteConfig) + action = EmbraceActionInterface(setup, bootstrapper) + payloadAssertion = EmbracePayloadAssertionInterface(bootstrapper, apiServer) + spanExporter = FilteredSpanExporter() + otelAssertion = EmbraceOtelExportAssertionInterface(spanExporter) + setupAction(setup) with(setup) { embraceImpl = EmbraceImpl(bootstrapper) @@ -119,7 +143,6 @@ internal class IntegrationTestRule( embraceImpl.addSpanExporter(spanExporter) if (startSdk) { - overriddenConfigService.appFramework = findAppFramework() embraceImpl.start(overriddenCoreModule.context) assertEquals(expectSdkToStart, bootstrapper.essentialServiceModule.processStateService.isInitialized()) } @@ -130,43 +153,24 @@ internal class IntegrationTestRule( otelExportAssertion(otelAssertion) } - @Suppress("DEPRECATION") - fun EmbraceSetupInterface.findAppFramework() = - when (appFramework) { - io.embrace.android.embracesdk.AppFramework.NATIVE -> AppFramework.NATIVE - io.embrace.android.embracesdk.AppFramework.REACT_NATIVE -> AppFramework.REACT_NATIVE - io.embrace.android.embracesdk.AppFramework.UNITY -> AppFramework.UNITY - io.embrace.android.embracesdk.AppFramework.FLUTTER -> AppFramework.FLUTTER + fun prepareConfig(instrumentedConfig: FakeInstrumentedConfig) = + when { + setup.useMockWebServer -> { + instrumentedConfig.copy( + baseUrls = FakeBaseUrlConfig(configImpl = baseUrl, dataImpl = baseUrl) + ) + } + + else -> { + instrumentedConfig + } } /** * Setup the Embrace SDK so it's ready for testing. */ override fun before() { - setup = embraceSetupInterfaceSupplier.invoke() - var apiServer: FakeApiServer? = null - if (setup.useMockWebServer) { - apiServer = FakeApiServer() - val server: MockWebServer = MockWebServer().apply { - protocols = listOf(Protocol.HTTP_2, Protocol.HTTP_1_1) - dispatcher = apiServer - start() - } - val baseUrl = server.url("api").toString() - - setup.overriddenConfigService.sdkEndpointBehavior = FakeSdkEndpointBehavior( - baseUrl, - baseUrl - ) - } - - preSdkStart = EmbracePreSdkStartInterface(setup) - bootstrapper = setup.createBootstrapper() - action = EmbraceActionInterface(setup, bootstrapper) - payloadAssertion = EmbracePayloadAssertionInterface(bootstrapper, apiServer) - spanExporter = FilteredSpanExporter() - otelAssertion = EmbraceOtelExportAssertionInterface(spanExporter) } /** diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceActionInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceActionInterface.kt index b5c768cdb8..58920fd1de 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceActionInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceActionInterface.kt @@ -26,9 +26,6 @@ internal class EmbraceActionInterface( val clock: FakeClock get() = setup.overriddenClock - val configService: FakeConfigService - get() = setup.overriddenConfigService - /** * Starts & ends a session for the purposes of testing. An action can be supplied as a lambda * parameter: any code inside the lambda will be executed, so can be used to add breadcrumbs, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index 6c3af513a0..58821db99d 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -1,31 +1,24 @@ -@file:Suppress("DEPRECATION") - package io.embrace.android.embracesdk.testframework.actions import androidx.lifecycle.Lifecycle import androidx.lifecycle.testing.TestLifecycleOwner -import io.embrace.android.embracesdk.AppFramework import io.embrace.android.embracesdk.fakes.FakeClock -import io.embrace.android.embracesdk.fakes.FakeConfigModule -import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeDeliveryService import io.embrace.android.embracesdk.fakes.FakeEmbLogger import io.embrace.android.embracesdk.fakes.FakeNativeFeatureModule import io.embrace.android.embracesdk.fakes.FakeNetworkConnectivityService +import io.embrace.android.embracesdk.fakes.FakeRemoteConfigSource import io.embrace.android.embracesdk.fakes.FakeRequestExecutionService -import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior -import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkSpanForwardingBehavior -import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.injection.FakeAnrModule import io.embrace.android.embracesdk.fakes.injection.FakeCoreModule import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeNativeCoreModule -import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.delivery.storage.PayloadStorageService import io.embrace.android.embracesdk.internal.injection.AndroidServicesModule import io.embrace.android.embracesdk.internal.injection.AnrModule import io.embrace.android.embracesdk.internal.injection.ModuleInitBootstrapper -import io.embrace.android.embracesdk.internal.injection.NativeCoreModule import io.embrace.android.embracesdk.internal.injection.OpenTelemetryModule import io.embrace.android.embracesdk.internal.injection.WorkerThreadModule import io.embrace.android.embracesdk.internal.injection.createAndroidServicesModule @@ -42,16 +35,10 @@ import io.embrace.android.embracesdk.testframework.IntegrationTestRule internal class EmbraceSetupInterface @JvmOverloads constructor( currentTimeMs: Long = IntegrationTestRule.DEFAULT_SDK_START_TIME_MS, var useMockWebServer: Boolean = true, - val appFramework: AppFramework = AppFramework.NATIVE, val overriddenClock: FakeClock = FakeClock(currentTime = currentTimeMs), val overriddenInitModule: FakeInitModule = FakeInitModule(clock = overriddenClock, logger = FakeEmbLogger()), val overriddenOpenTelemetryModule: OpenTelemetryModule = overriddenInitModule.openTelemetryModule, val overriddenCoreModule: FakeCoreModule = FakeCoreModule(), - val overriddenConfigService: FakeConfigService = FakeConfigService( - networkSpanForwardingBehavior = FakeNetworkSpanForwardingBehavior(true), - autoDataCaptureBehavior = FakeAutoDataCaptureBehavior(thermalStatusCaptureEnabled = false), - backgroundActivityBehavior = createBackgroundActivityBehavior { BackgroundActivityRemoteConfig(threshold = 100f) } - ), val overriddenWorkerThreadModule: WorkerThreadModule = createWorkerThreadModule(), val overriddenAndroidServicesModule: AndroidServicesModule = createAndroidServicesModule( initModule = overriddenInitModule, @@ -65,8 +52,13 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( val networkConnectivityService: FakeNetworkConnectivityService = FakeNetworkConnectivityService(), val lifecycleOwner: TestLifecycleOwner = TestLifecycleOwner(initialState = Lifecycle.State.INITIALIZED), ) { - fun createBootstrapper(): ModuleInitBootstrapper = ModuleInitBootstrapper( - initModule = overriddenInitModule, + fun createBootstrapper( + instrumentedConfig: FakeInstrumentedConfig, + remoteConfig: RemoteConfig, + ): ModuleInitBootstrapper = ModuleInitBootstrapper( + initModule = overriddenInitModule.apply { + this.instrumentedConfig = instrumentedConfig + }, openTelemetryModule = overriddenInitModule.openTelemetryModule, coreModuleSupplier = { _, _ -> overriddenCoreModule }, workerThreadModuleSupplier = { overriddenWorkerThreadModule }, @@ -108,10 +100,17 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( } }) }, - configModuleSupplier = { _, _, _, _, _, _ -> - FakeConfigModule( - configService = overriddenConfigService - ) + configModuleSupplier = { initModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, foregroundAction, _ -> + createConfigModule( + initModule, + openTelemetryModule, + workerThreadModule, + androidServicesModule, + appFramework, + foregroundAction + ) { + FakeRemoteConfigSource(remoteConfig) + } }, anrModuleSupplier = { _, _, _ -> fakeAnrModule }, nativeCoreModuleSupplier = { FakeNativeCoreModule() }, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt index d4967a9cbe..85bdb39c09 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt @@ -2,7 +2,7 @@ package io.embrace.android.embracesdk.testframework.server import io.embrace.android.embracesdk.fakes.TestPlatformSerializer import io.embrace.android.embracesdk.internal.TypeUtils -import io.embrace.android.embracesdk.internal.comms.api.Endpoint +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.LogPayload import io.embrace.android.embracesdk.internal.payload.SessionPayload @@ -18,11 +18,20 @@ import org.junit.Assert.assertNotNull /** * Fake API server that is used to capture log/session requests made by the SDK in integration tests. */ -internal class FakeApiServer : Dispatcher() { +internal class FakeApiServer(private val remoteConfig: RemoteConfig) : Dispatcher() { + + private enum class Endpoint { + LOGS, + SESSIONS, + CONFIG + } private val serializer by threadLocal { TestPlatformSerializer() } private val sessionRequests = ConcurrentLinkedQueue>() private val logRequests = ConcurrentLinkedQueue>() + private val configResponse by lazy { + serializer.toJson(remoteConfig) + } /** * Returns a list of session envelopes in the order in which the server received them. @@ -34,20 +43,32 @@ internal class FakeApiServer : Dispatcher() { */ fun getLogEnvelopes(): List> = logRequests.toList() - @Suppress("UNCHECKED_CAST") override fun dispatch(request: RecordedRequest): MockResponse { - val endpoint = findRequestEndpoint(request) + return when (val endpoint = request.asEndpoint()) { + Endpoint.LOGS, Endpoint.SESSIONS -> handleEnvelopeRequest(request, endpoint) + Endpoint.CONFIG -> handleConfigRequest() // IMPORTANT NOTE: this response is not used until the SDK next starts! + } + } + + @Suppress("UNCHECKED_CAST") + private fun handleEnvelopeRequest( + request: RecordedRequest, + endpoint: Endpoint, + ): MockResponse { val envelope = deserializeEnvelope(request, endpoint) validateHeaders(request.headers.toMultimap().mapValues { it.value.joinToString() }) + val response = MockResponse().setResponseCode(200) when (endpoint) { Endpoint.SESSIONS -> sessionRequests.add(envelope as Envelope) Endpoint.LOGS -> logRequests.add(envelope as Envelope) - else -> error("Unsupported request type $endpoint") + Endpoint.CONFIG -> response.setBody(configResponse) } - return MockResponse().setResponseCode(200) + return response } + private fun handleConfigRequest() = MockResponse().setBody(configResponse).setResponseCode(200) + private fun validateHeaders(headers: Map) { with(headers) { assertEquals("application/json", get("accept")) @@ -58,11 +79,13 @@ internal class FakeApiServer : Dispatcher() { } } - private fun findRequestEndpoint(request: RecordedRequest): Endpoint { - return when (val path = request.path?.removePrefix("/api/v2/")) { - Endpoint.LOGS.path -> Endpoint.LOGS - Endpoint.SESSIONS.path -> Endpoint.SESSIONS - else -> error("Unsupported path $path") + private fun RecordedRequest.asEndpoint(): Endpoint { + val path = requestUrl?.toUrl()?.path + return when (val endpoint = path?.removePrefix("/api/v2/")) { + "logs" -> Endpoint.LOGS + "spans" -> Endpoint.SESSIONS + "config" -> Endpoint.CONFIG + else -> error("Unsupported path $endpoint") } } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index 3189b9e5ed..f2a29a03ac 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -154,12 +154,14 @@ internal class ModuleInitBootstrapper( openTelemetryModule, workerThreadModule, androidServicesModule, - appFramework - ) { - if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { - EmbraceInternalApi.getInstance().internalInterface.stopSdk() - } - } + appFramework, + { + if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { + EmbraceInternalApi.getInstance().internalInterface.stopSdk() + } + }, + { null } + ) } postInit(ConfigModule::class) { serviceRegistry.registerService(lazy { configModule.configService }) @@ -188,7 +190,8 @@ internal class ModuleInitBootstrapper( postInit(EssentialServiceModule::class) { // Allow config service to start making HTTP requests with(essentialServiceModule) { - (configModule.remoteConfigSource as? RemoteConfigSourceImpl)?.remoteConfigSource = apiService + (configModule.remoteConfigSource as? RemoteConfigSourceImpl)?.remoteConfigSource = + apiService serviceRegistry.registerServices( lazy { essentialServiceModule.processStateService }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 097b1b36df..35f458da7d 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -43,7 +43,7 @@ internal fun fakeModuleInitBootstrapper( workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, - configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _ -> FakeConfigModule() }, + configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt index 77801b3b23..855214923e 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt @@ -32,7 +32,7 @@ internal class ModuleInitBootstrapperTest { logger = EmbLoggerImpl() coreModule = FakeCoreModule() moduleInitBootstrapper = ModuleInitBootstrapper( - configModuleSupplier = { _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, + configModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, coreModuleSupplier = { _, _ -> coreModule }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> FakeNativeFeatureModule() }, logger = logger diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeInstrumentedConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeInstrumentedConfig.kt index 18f1227577..d2b39df33a 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeInstrumentedConfig.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeInstrumentedConfig.kt @@ -10,7 +10,7 @@ import io.embrace.android.embracesdk.internal.config.instrumented.schema.Session /** * A fake [InstrumentedConfig] implementation that defaults to the real implementation unless an override is supplied. */ -class FakeInstrumentedConfig( +data class FakeInstrumentedConfig( private val base: InstrumentedConfig = InstrumentedConfigImpl, override val baseUrls: FakeBaseUrlConfig = FakeBaseUrlConfig(base.baseUrls), override val enabledFeatures: FakeEnabledFeatureConfig = FakeEnabledFeatureConfig(base.enabledFeatures), diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeInitModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeInitModule.kt index f463c129a0..cc7ea9c2ec 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeInitModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeInitModule.kt @@ -25,7 +25,7 @@ class FakeInitModule( logger = logger, systemInfo = systemInfo ), - override val instrumentedConfig: InstrumentedConfig = FakeInstrumentedConfig() + override var instrumentedConfig: InstrumentedConfig = FakeInstrumentedConfig() ) : InitModule by initModule { val openTelemetryModule: OpenTelemetryModule by lazy { createOpenTelemetryModule(initModule) } From 5b409a1209c326d238bf4655d2a4f67139778b29 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Thu, 7 Nov 2024 17:50:43 +0000 Subject: [PATCH 06/28] refactor: move remote config tests --- .../embracesdk/testcases/PublicApiTest.kt | 27 ----------- .../testcases/features/RemoteConfigTest.kt | 46 +++++++++++++++++++ 2 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/RemoteConfigTest.kt diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt index fe89c565c8..c4c8eb7c8a 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt @@ -6,10 +6,8 @@ import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.testframework.IntegrationTestRule import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -34,19 +32,6 @@ internal class PublicApiTest { @JvmField val testRule: IntegrationTestRule = IntegrationTestRule() - @Test - fun `SDK can start`() { - testRule.runTest( - instrumentedConfig = instrumentedConfig, - preSdkStartAction = { - assertFalse(embrace.isStarted) - }, - testCaseAction = { - assertTrue(embrace.isStarted) - } - ) - } - @Test fun `SDK start defaults to native app framework`() { testRule.runTest( @@ -57,18 +42,6 @@ internal class PublicApiTest { ) } - @Test - fun `SDK disabled via config cannot start`() { - testRule.runTest( - instrumentedConfig = instrumentedConfig, - remoteConfig = RemoteConfig(0), - expectSdkToStart = false, - testCaseAction = { - assertFalse(embrace.isStarted) - } - ) - } - @Test fun `getCurrentSessionId returns null when SDK is not started`() { testRule.runTest( diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/RemoteConfigTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/RemoteConfigTest.kt new file mode 100644 index 0000000000..2a5e20ee5d --- /dev/null +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/RemoteConfigTest.kt @@ -0,0 +1,46 @@ +package io.embrace.android.embracesdk.testcases.features + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.testframework.IntegrationTestRule +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Tests how the SDK behaves when controlling by 'remote config'. This is a HTTP response from the + * Embrace server that can enable or disable individual features or the entire SDK. + */ +@RunWith(AndroidJUnit4::class) +internal class RemoteConfigTest { + + @Rule + @JvmField + val testRule: IntegrationTestRule = IntegrationTestRule() + + @Test + fun `SDK can start`() { + testRule.runTest( + remoteConfig = RemoteConfig(100), + preSdkStartAction = { + assertFalse(embrace.isStarted) + }, + testCaseAction = { + assertTrue(embrace.isStarted) + } + ) + } + + @Test + fun `SDK disabled via config cannot start`() { + testRule.runTest( + remoteConfig = RemoteConfig(0), + expectSdkToStart = false, + testCaseAction = { + assertFalse(embrace.isStarted) + } + ) + } +} From 2adedb10adee190679db5701d4cdc0e412f352a9 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Fri, 8 Nov 2024 10:43:39 +0000 Subject: [PATCH 07/28] test: add assertions for checking config request made --- ...eConfigService.kt => ConfigServiceImpl.kt} | 2 +- .../internal/injection/ConfigModuleImpl.kt | 4 +- ...erviceTest.kt => ConfigServiceImplTest.kt} | 10 ++--- .../EmbracePayloadAssertionInterface.kt | 41 +++++++++++-------- .../testframework/server/FakeApiServer.kt | 21 +++++++--- 5 files changed, 49 insertions(+), 29 deletions(-) rename embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/{EmbraceConfigService.kt => ConfigServiceImpl.kt} (99%) rename embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/{EmbraceConfigServiceTest.kt => ConfigServiceImplTest.kt} (97%) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt similarity index 99% rename from embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt rename to embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt index 3907211599..3758da0864 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/EmbraceConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt @@ -39,7 +39,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider /** * Loads configuration for the app from the Embrace API. */ -internal class EmbraceConfigService( +internal class ConfigServiceImpl( openTelemetryCfg: OpenTelemetryConfiguration, preferencesService: PreferencesService, suppliedFramework: AppFramework, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index e21d146a05..dbc8d3539e 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -2,7 +2,7 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.config.ConfigService -import io.embrace.android.embracesdk.internal.config.EmbraceConfigService +import io.embrace.android.embracesdk.internal.config.ConfigServiceImpl import io.embrace.android.embracesdk.internal.config.RemoteConfigSource import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl import io.embrace.android.embracesdk.internal.payload.AppFramework @@ -21,7 +21,7 @@ internal class ConfigModuleImpl( override val configService: ConfigService by singleton { Systrace.traceSynchronous("config-service-init") { - EmbraceConfigService( + ConfigServiceImpl( openTelemetryCfg = openTelemetryModule.openTelemetryConfiguration, preferencesService = androidServicesModule.preferencesService, suppliedFramework = framework, diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt similarity index 97% rename from embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt rename to embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt index 0e398bc2b3..f8761dc5e6 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/EmbraceConfigServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt @@ -34,10 +34,10 @@ import org.junit.Before import org.junit.BeforeClass import org.junit.Test -internal class EmbraceConfigServiceTest { +internal class ConfigServiceImplTest { private lateinit var fakePreferenceService: PreferencesService - private lateinit var service: EmbraceConfigService + private lateinit var service: ConfigServiceImpl private lateinit var worker: BackgroundWorker private lateinit var executor: BlockingScheduledExecutorService private lateinit var thresholdCheck: BehaviorThresholdCheck @@ -222,7 +222,7 @@ internal class EmbraceConfigServiceTest { } /** - * Create a new instance of the [EmbraceConfigService] using the passed in [worker] to run + * Create a new instance of the [ConfigServiceImpl] using the passed in [worker] to run * tasks for its internal [BackgroundWorker] */ private fun createService( @@ -234,14 +234,14 @@ internal class EmbraceConfigServiceTest { SystemInfo() ), appId: String? = "AbCdE", - ): EmbraceConfigService { + ): ConfigServiceImpl { thresholdCheck = BehaviorThresholdCheck { fakePreferenceService.deviceIdentifier } remoteConfigSource = RemoteConfigSourceImpl( clock = fakeClock, backgroundWorker = worker, foregroundAction = action, ) - return EmbraceConfigService( + return ConfigServiceImpl( openTelemetryCfg = config, preferencesService = fakePreferenceService, suppliedFramework = AppFramework.NATIVE, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt index f9be427ff3..6e25e0bf52 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt @@ -6,7 +6,6 @@ import io.embrace.android.embracesdk.assertions.getSessionId import io.embrace.android.embracesdk.assertions.returnIfConditionMet import io.embrace.android.embracesdk.fakes.FakeDeliveryService import io.embrace.android.embracesdk.fakes.FakeNativeCrashService -import io.embrace.android.embracesdk.fakes.FakeRequestExecutionService import io.embrace.android.embracesdk.internal.injection.ModuleInitBootstrapper import io.embrace.android.embracesdk.internal.opentelemetry.embCleanExit import io.embrace.android.embracesdk.internal.opentelemetry.embState @@ -32,8 +31,11 @@ internal class EmbracePayloadAssertionInterface( private val apiServer: FakeApiServer?, ) { + companion object { + private const val WAIT_TIME_MS = 10000 + } + private val deliveryService by lazy { bootstrapper.deliveryModule.deliveryService as FakeDeliveryService } - private val requestExecutionService by lazy { bootstrapper.deliveryModule.requestExecutionService as FakeRequestExecutionService } private val serializer by lazy { bootstrapper.initModule.jsonSerializer } private val nativeCrashService by lazy { bootstrapper.nativeFeatureModule.nativeCrashService as FakeNativeCrashService @@ -55,17 +57,6 @@ internal class EmbracePayloadAssertionInterface( return getLogEnvelopes(1).single() } - /** - * Returns a list of logs that were completed by the SDK & sent to a mock web server. - */ - internal fun getLogEnvelopesFromMockServer( - expectedSize: Int, - ): List> { - return retrievePayload(expectedSize) { - checkNotNull(apiServer).getLogEnvelopes() - } - } - private fun retrieveLogEnvelopes( expectedSize: Int, ): List> { @@ -136,7 +127,7 @@ internal class EmbracePayloadAssertionInterface( internal fun getSessionEnvelopes( expectedSize: Int, state: ApplicationState = ApplicationState.FOREGROUND, - waitTimeMs: Int = 1000, + waitTimeMs: Int = WAIT_TIME_MS, ): List> { return retrieveSessionEnvelopes(expectedSize, state, waitTimeMs) } @@ -181,6 +172,24 @@ internal class EmbracePayloadAssertionInterface( return ApplicationState.valueOf(value) } + + /*** Config ***/ + + + internal fun assertConfigRequested(expectedRequests: Int) { + val supplier = { + checkNotNull(apiServer).getConfigRequests() + } + try { + retrievePayload(expectedRequests, supplier) + } catch (exc: TimeoutException) { + throw IllegalStateException( + "Expected $expectedRequests config requests, but got ${supplier().size}.", + exc + ) + } + } + /*** Native ***/ internal fun getSentNativeCrashes() = nativeCrashService.nativeCrashesSent.toList() @@ -247,14 +256,14 @@ internal class EmbracePayloadAssertionInterface( private inline fun retrievePayload( expectedSize: Int?, supplier: () -> List, - ): List = retrievePayload(expectedSize, 1000, supplier) + ): List = retrievePayload(expectedSize, WAIT_TIME_MS, supplier) /** * Retrieves a payload that was stored in the delivery service. */ private inline fun retrievePayload( expectedSize: Int?, - waitTimeMs: Int = 1000, + waitTimeMs: Int = WAIT_TIME_MS, supplier: () -> List, ): List { return when (expectedSize) { diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt index 85bdb39c09..6c852cedaa 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt @@ -29,6 +29,7 @@ internal class FakeApiServer(private val remoteConfig: RemoteConfig) : Dispatche private val serializer by threadLocal { TestPlatformSerializer() } private val sessionRequests = ConcurrentLinkedQueue>() private val logRequests = ConcurrentLinkedQueue>() + private val configRequests = ConcurrentLinkedQueue() private val configResponse by lazy { serializer.toJson(remoteConfig) } @@ -43,10 +44,18 @@ internal class FakeApiServer(private val remoteConfig: RemoteConfig) : Dispatche */ fun getLogEnvelopes(): List> = logRequests.toList() + /** + * Returns a list of config requests in the order in which the server received them. + * The returned value is the query parameter. + */ + fun getConfigRequests(): List = configRequests.toList() + override fun dispatch(request: RecordedRequest): MockResponse { return when (val endpoint = request.asEndpoint()) { Endpoint.LOGS, Endpoint.SESSIONS -> handleEnvelopeRequest(request, endpoint) - Endpoint.CONFIG -> handleConfigRequest() // IMPORTANT NOTE: this response is not used until the SDK next starts! + + // IMPORTANT NOTE: this response is not used until the SDK next starts! + Endpoint.CONFIG -> handleConfigRequest(request) } } @@ -58,16 +67,18 @@ internal class FakeApiServer(private val remoteConfig: RemoteConfig) : Dispatche val envelope = deserializeEnvelope(request, endpoint) validateHeaders(request.headers.toMultimap().mapValues { it.value.joinToString() }) - val response = MockResponse().setResponseCode(200) when (endpoint) { Endpoint.SESSIONS -> sessionRequests.add(envelope as Envelope) Endpoint.LOGS -> logRequests.add(envelope as Envelope) - Endpoint.CONFIG -> response.setBody(configResponse) + else -> error("Unsupported endpoint $endpoint") } - return response + return MockResponse().setResponseCode(200) } - private fun handleConfigRequest() = MockResponse().setBody(configResponse).setResponseCode(200) + private fun handleConfigRequest(request: RecordedRequest): MockResponse { + configRequests.add(request.requestUrl?.toUrl()?.query) + return MockResponse().setBody(configResponse).setResponseCode(200) + } private fun validateHeaders(headers: Map) { with(headers) { From 5b172a74e5880bfa1572ec31d1c2fa043232760a Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Fri, 8 Nov 2024 17:04:30 +0000 Subject: [PATCH 08/28] disable inline --- .../android/embracesdk/testframework/IntegrationTestRule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt index 582bc87496..e75cc7d11f 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt @@ -104,7 +104,7 @@ internal class IntegrationTestRule( * assertions. This aims to enforce the better compartmentalisation & reuse of test code within * the integration test suite. */ - inline fun runTest( + fun runTest( startSdk: Boolean = true, instrumentedConfig: FakeInstrumentedConfig = FakeInstrumentedConfig(), remoteConfig: RemoteConfig = RemoteConfig(), From 7b05876aca8aed820e981cba8c32a47db864815b Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Fri, 8 Nov 2024 16:57:01 +0000 Subject: [PATCH 09/28] wip: okhttp implementation --- embrace-android-core/build.gradle.kts | 1 + .../internal/comms/api/ApiUrlBuilder.kt | 10 ++ .../comms/api/EmbraceApiUrlBuilder.kt | 9 +- .../config/OkHttpRemoteConfigSource.kt | 57 +++++++ .../injection/EssentialServiceModuleImpl.kt | 2 +- .../comms/api/EmbraceApiServiceTest.kt | 2 +- .../comms/api/EmbraceApiUrlBuilderTest.kt | 2 +- .../config/OkHttpRemoteConfigSourceTest.kt | 155 ++++++++++++++++++ .../embracesdk/fakes/FakeApiUrlBuilder.kt | 18 +- 9 files changed, 242 insertions(+), 14 deletions(-) create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt create mode 100644 embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt diff --git a/embrace-android-core/build.gradle.kts b/embrace-android-core/build.gradle.kts index 42b5da4d67..80a89bba67 100644 --- a/embrace-android-core/build.gradle.kts +++ b/embrace-android-core/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { compileOnly(libs.opentelemetry.semconv.incubating) implementation(libs.lifecycle.runtime) implementation(libs.lifecycle.process) + implementation(libs.okhttp) testImplementation(platform(libs.opentelemetry.bom)) testImplementation(libs.opentelemetry.api) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt index 3425794a53..cf55926384 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt @@ -5,6 +5,16 @@ package io.embrace.android.embracesdk.internal.comms.api */ interface ApiUrlBuilder { + /** + * The App ID that will be used in API requests. + */ + val appId: String + + /** + * The Device ID that will be used in API requests. + */ + val deviceId: String + /** * Returns the url used to fetch the config. */ diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt index 1fded563a5..2e6666f84a 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt @@ -5,21 +5,24 @@ import android.os.Build internal class EmbraceApiUrlBuilder( private val coreBaseUrl: String, private val configBaseUrl: String, - private val appId: String, - private val lazyDeviceId: Lazy, + override val appId: String, + deviceIdImpl: Lazy, private val lazyAppVersionName: Lazy, ) : ApiUrlBuilder { + companion object { private const val CONFIG_API_VERSION = 2 } + override val deviceId: String by deviceIdImpl + private fun getConfigBaseUrl() = "$configBaseUrl/v$CONFIG_API_VERSION/${"config"}" private fun getOperatingSystemCode() = Build.VERSION.SDK_INT.toString() + ".0.0" override fun getConfigUrl(): String { return "${getConfigBaseUrl()}?appId=$appId&osVersion=${getOperatingSystemCode()}" + - "&appVersion=${lazyAppVersionName.value}&deviceId=${lazyDeviceId.value}" + "&appVersion=${lazyAppVersionName.value}&deviceId=$deviceId" } override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String { diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt new file mode 100644 index 0000000000..580045de67 --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt @@ -0,0 +1,57 @@ +package io.embrace.android.embracesdk.internal.config + +import io.embrace.android.embracesdk.core.BuildConfig +import io.embrace.android.embracesdk.internal.comms.api.ApiRequest +import io.embrace.android.embracesdk.internal.comms.api.ApiRequestUrl +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder +import io.embrace.android.embracesdk.internal.comms.api.getHeaders +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer +import io.embrace.android.embracesdk.network.http.HttpMethod +import okhttp3.OkHttpClient +import okhttp3.Request +import java.io.IOException + +internal class OkHttpRemoteConfigSource( + private val okhttpClient: OkHttpClient, + private val apiUrlBuilder: ApiUrlBuilder, + private val serializer: PlatformSerializer, +) : RemoteConfigSource { + + override fun getConfig(): RemoteConfig? = try { + fetchConfigImpl() + } catch (exc: IOException) { + null + } + + private fun fetchConfigImpl(): RemoteConfig? { + val url = apiUrlBuilder.getConfigUrl() + val headers = prepareConfigRequest(url).getHeaders() + val builder = Request.Builder().url(url) + + headers.forEach { entry -> + builder.header(entry.key, entry.value) + } + val call = okhttpClient.newCall( + builder.build() + ) + + val response = call.execute() + if (!response.isSuccessful) { + return null + } + val config = response.body?.source()?.inputStream()?.buffered()?.use { + serializer.fromJson(it, RemoteConfig::class.java) + } + return config + } + + private fun prepareConfigRequest(url: String) = ApiRequest( + userAgent = "Embrace/a/" + BuildConfig.VERSION_NAME, + url = ApiRequestUrl(url), + httpMethod = HttpMethod.GET, + acceptEncoding = "gzip", + appId = apiUrlBuilder.appId, + deviceId = apiUrlBuilder.deviceId, + ) +} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt index f3aa4aec4a..230a46af9d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt @@ -69,7 +69,7 @@ class EssentialServiceModuleImpl( coreBaseUrl = coreBaseUrl, configBaseUrl = configBaseUrl, appId = appId, - lazyDeviceId = lazyDeviceId, + deviceIdImpl = lazyDeviceId, lazyAppVersionName = lazy { coreModule.packageVersionInfo.versionName } ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt index db8ca53ac9..5af2be9c7f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt @@ -52,7 +52,7 @@ internal class EmbraceApiServiceTest { coreBaseUrl = "https://a-$fakeAppId.data.emb-api.com", configBaseUrl = "https://a-$fakeAppId.config.emb-api.com", appId = fakeAppId, - lazyDeviceId = lazy { fakeDeviceId }, + deviceIdImpl = lazy { fakeDeviceId }, lazyAppVersionName = lazy { fakeAppVersionName } ) fakeApiClient = FakeApiClient() diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt index 5c33f75f9e..77470af413 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt @@ -22,7 +22,7 @@ internal class EmbraceApiUrlBuilderTest { coreBaseUrl = baseUrlLocalConfig.getData(APP_ID), configBaseUrl = baseUrlLocalConfig.getConfig(APP_ID), appId = APP_ID, - lazyDeviceId = lazy { DEVICE_ID }, + deviceIdImpl = lazy { DEVICE_ID }, lazyAppVersionName = lazy { APP_VERSION_NAME }, ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt new file mode 100644 index 0000000000..7ad7704f52 --- /dev/null +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -0,0 +1,155 @@ +package io.embrace.android.embracesdk.internal.config + +import io.embrace.android.embracesdk.fakes.TestPlatformSerializer +import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import okhttp3.OkHttpClient +import okhttp3.Protocol +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import okhttp3.mockwebserver.RecordedRequest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import java.util.concurrent.TimeUnit + +@RunWith(RobolectricTestRunner::class) +class OkHttpRemoteConfigSourceTest { + + private lateinit var server: MockWebServer + private lateinit var client: OkHttpClient + private lateinit var urlBuilder: EmbraceApiUrlBuilder + + private val remoteConfig = RemoteConfig( + backgroundActivityConfig = BackgroundActivityRemoteConfig(100f) + ) + private val configResponse = TestPlatformSerializer().toJson(remoteConfig) + + @Before + fun setUp() { + server = MockWebServer().apply { + protocols = listOf(Protocol.HTTP_1_1, Protocol.HTTP_2) + start() + } + val baseUrl = server.url("api").toString() + client = OkHttpClient.Builder().build() + urlBuilder = EmbraceApiUrlBuilder( + coreBaseUrl = baseUrl, + configBaseUrl = baseUrl, + appId = "abcde", + deviceIdImpl = lazy { "deviceId" }, + lazyAppVersionName = lazy { "1.0.0" }, + ) + } + + @Test + fun `test config 2xx`() { + val (cfg, request) = executeRequest( + MockResponse().setResponseCode(200).setBody(configResponse) + ) + assertConfigRequestReceived(request) + assertConfigResponseDeserialized(cfg) + } + + @Test + fun `test config 4xx`() { + val (cfg, request) = executeRequest( + MockResponse().setResponseCode(400) + ) + assertConfigRequestReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + @Test + fun `test config 5xx`() { + val (cfg, request) = executeRequest( + MockResponse().setResponseCode(500) + ) + assertConfigRequestReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + @Test + fun `test no response from server`() { + client = client.newBuilder().callTimeout(1, TimeUnit.MILLISECONDS).build() + val (cfg, request) = executeRequest(null) + assertConfigRequestNotReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + @Test + fun `test call timeout in middle of server response`() { + client = client.newBuilder().callTimeout(5, TimeUnit.MILLISECONDS).build() + val (cfg, request) = executeRequest( + MockResponse() + .setResponseCode(200) + .setBody(configResponse) + .throttleBody(1, 1, TimeUnit.MILLISECONDS) + ) + assertConfigRequestReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + @Test + fun `test invalid response from server`() { + val (cfg, request) = executeRequest( + MockResponse().setResponseCode(200).setBody("{") + ) + assertConfigRequestReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + private fun executeRequest(response: MockResponse?): Pair { + if (response == null) { + return Pair(null, null) + } + server.enqueue(response) + val source = OkHttpRemoteConfigSource(client, urlBuilder, TestPlatformSerializer()) + val cfg = source.getConfig() + val request = pollRequest() + CallData(request, cfg) + return Pair(cfg, request) + } + + private fun assertEmbraceHeadersAdded(request: RecordedRequest?) { + val headers = request?.headers?.toMap() ?: error("Request headers cannot be null") + assertEquals("application/json", headers["Accept"]) + assertEquals("application/json", headers["Content-Type"]) + assertEquals("gzip", headers["Accept-Encoding"]) + assertEquals("abcde", headers["X-EM-AID"]) + assertEquals("deviceId", headers["X-EM-DID"]) + } + + private fun assertConfigRequestNotReceived(request: RecordedRequest?) { + assertNull(request) + } + + private fun assertConfigRequestReceived(request: RecordedRequest?) { + assertNotNull(request) + assertEmbraceHeadersAdded(request) + val requestUrl = request?.requestUrl?.toUrl() ?: error("Request URL cannot be null") + assertEquals("/api/v2/config", requestUrl.path) + assertEquals("appId=abcde&osVersion=21.0.0&appVersion=1.0.0&deviceId=deviceId", requestUrl.query) + } + + private fun assertConfigResponseDeserialized(cfg: RemoteConfig?) { + checkNotNull(cfg) + assertEquals(remoteConfig.backgroundActivityConfig, cfg.backgroundActivityConfig) + } + + private fun assertConfigResponseNotDeserialized(cfg: RemoteConfig?) { + assertNull(cfg) + } + + private data class CallData( + val request: RecordedRequest?, + val deserializedConfig: RemoteConfig?, + ) + + private fun pollRequest(): RecordedRequest? = server.takeRequest(1, TimeUnit.SECONDS) +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt index 9de47a61ce..821eb6676d 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt @@ -2,12 +2,14 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder -class FakeApiUrlBuilder : ApiUrlBuilder { - override fun getConfigUrl(): String { - return "" - } - - override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String { - return "" - } +class FakeApiUrlBuilder( + private val config: String = "", + private val other: String = "", + override val appId: String = "", + override val deviceId: String = "", +) : ApiUrlBuilder { + + override fun getConfigUrl(): String = config + + override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String = other } From 8a9f3f6e23f270dc0408e82d23253211ed34805f Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Mon, 11 Nov 2024 14:43:02 +0000 Subject: [PATCH 10/28] refactor: reorganise initial bootstrapping --- .../config/detekt/baseline.xml | 2 +- .../injection/AndroidServicesModuleImpl.kt | 10 +-- .../AndroidServicesModuleSupplier.kt | 4 +- .../internal/injection/DeliveryModuleImpl.kt | 62 +++++++++++------ .../injection/DeliveryModuleSupplier.kt | 19 +++--- .../prefs/EmbracePreferencesService.kt | 34 +--------- .../session/EmbraceSessionPropertiesTest.kt | 6 +- .../AndroidServicesModuleImplTest.kt | 6 +- .../injection/DeliveryModuleImplTest.kt | 6 +- .../prefs/EmbracePreferencesServiceTest.kt | 8 +-- .../actions/EmbraceSetupInterface.kt | 37 ++++++----- .../injection/ModuleInitBootstrapper.kt | 66 ++++--------------- .../fakes/FakeModuleInitBootstrapper.kt | 4 +- 13 files changed, 106 insertions(+), 158 deletions(-) diff --git a/embrace-android-core/config/detekt/baseline.xml b/embrace-android-core/config/detekt/baseline.xml index f7162bde92..5b94df8f86 100644 --- a/embrace-android-core/config/detekt/baseline.xml +++ b/embrace-android-core/config/detekt/baseline.xml @@ -2,6 +2,6 @@ - LongParameterList:DeliveryModuleSupplier.kt$( configModule: ConfigModule, initModule: InitModule, otelModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, payloadStorageServiceProvider: Provider<PayloadStorageService?>, cacheStorageServiceProvider: Provider<PayloadStorageService?>, requestExecutionServiceProvider: Provider<RequestExecutionService?>, deliveryServiceProvider: Provider<DeliveryService?>, ) + LongParameterList:DeliveryModuleSupplier.kt$( configModule: ConfigModule, initModule: InitModule, otelModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, androidServicesModule: AndroidServicesModule, payloadStorageServiceProvider: Provider<PayloadStorageService>?, cacheStorageServiceProvider: Provider<PayloadStorageService>?, requestExecutionServiceProvider: Provider<RequestExecutionService>?, deliveryServiceProvider: Provider<DeliveryService>?, ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImpl.kt index 807aabf0a6..ca361b6d8c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImpl.kt @@ -5,23 +5,17 @@ package io.embrace.android.embracesdk.internal.injection import android.preference.PreferenceManager import io.embrace.android.embracesdk.internal.prefs.EmbracePreferencesService import io.embrace.android.embracesdk.internal.prefs.PreferencesService -import io.embrace.android.embracesdk.internal.worker.Worker internal class AndroidServicesModuleImpl( initModule: InitModule, coreModule: CoreModule, - workerThreadModule: WorkerThreadModule, ) : AndroidServicesModule { override val preferencesService: PreferencesService by singleton { - val lazyPrefs = lazy { + EmbracePreferencesService( PreferenceManager.getDefaultSharedPreferences( coreModule.context - ) - } - EmbracePreferencesService( - workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), - lazyPrefs, + ), initModule.clock, initModule.jsonSerializer ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleSupplier.kt index 2ff61d7093..183dcca485 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleSupplier.kt @@ -6,11 +6,9 @@ package io.embrace.android.embracesdk.internal.injection typealias AndroidServicesModuleSupplier = ( initModule: InitModule, coreModule: CoreModule, - workerThreadModule: WorkerThreadModule, ) -> AndroidServicesModule fun createAndroidServicesModule( initModule: InitModule, coreModule: CoreModule, - workerThreadModule: WorkerThreadModule, -): AndroidServicesModule = AndroidServicesModuleImpl(initModule, coreModule, workerThreadModule) +): AndroidServicesModule = AndroidServicesModuleImpl(initModule, coreModule) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt index 98938667ad..5bc5064b00 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt @@ -1,10 +1,13 @@ package io.embrace.android.embracesdk.internal.injection +import io.embrace.android.embracesdk.core.BuildConfig import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryService import io.embrace.android.embracesdk.internal.comms.delivery.EmbraceDeliveryService import io.embrace.android.embracesdk.internal.delivery.StoredTelemetryMetadata import io.embrace.android.embracesdk.internal.delivery.caching.PayloadCachingService import io.embrace.android.embracesdk.internal.delivery.caching.PayloadCachingServiceImpl +import io.embrace.android.embracesdk.internal.delivery.execution.HttpUrlConnectionRequestExecutionService +import io.embrace.android.embracesdk.internal.delivery.execution.OkHttpRequestExecutionService import io.embrace.android.embracesdk.internal.delivery.execution.RequestExecutionService import io.embrace.android.embracesdk.internal.delivery.intake.IntakeService import io.embrace.android.embracesdk.internal.delivery.intake.IntakeServiceImpl @@ -28,21 +31,11 @@ internal class DeliveryModuleImpl( coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, - requestExecutionServiceProvider: Provider, - payloadStorageServiceProvider: Provider, - cacheStorageServiceProvider: Provider, - deliveryServiceProvider: () -> DeliveryService? = { - val apiService = essentialServiceModule.apiService - if (configModule.configService.isOnlyUsingOtelExporters() || apiService == null) { - null - } else { - EmbraceDeliveryService( - storageModule.deliveryCacheManager, - apiService, - initModule.jsonSerializer - ) - } - }, + androidServicesModule: AndroidServicesModule, + requestExecutionServiceProvider: Provider?, + payloadStorageServiceProvider: Provider?, + cacheStorageServiceProvider: Provider?, + deliveryServiceProvider: Provider? ) : DeliveryModule { private val processIdProvider = { otelModule.openTelemetryConfiguration.processIdentifier } @@ -64,7 +57,15 @@ internal class DeliveryModuleImpl( } override val deliveryService: DeliveryService? by singleton { - deliveryServiceProvider() + deliveryServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { + null + } else { + EmbraceDeliveryService( + storageModule.deliveryCacheManager, + essentialServiceModule.apiService ?: return@singleton null, + initModule.jsonSerializer + ) + } } private val dataPersistenceWorker: PriorityWorker by singleton { @@ -111,7 +112,7 @@ internal class DeliveryModuleImpl( } override val payloadStorageService: PayloadStorageService? by singleton { - payloadStorageServiceProvider() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { + payloadStorageServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { null } else { PayloadStorageServiceImpl( @@ -125,7 +126,7 @@ internal class DeliveryModuleImpl( } override val cacheStorageService: PayloadStorageService? by singleton { - cacheStorageServiceProvider() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { + cacheStorageServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { null } else { PayloadStorageServiceImpl( @@ -139,7 +140,30 @@ internal class DeliveryModuleImpl( } override val requestExecutionService: RequestExecutionService? by singleton { - requestExecutionServiceProvider() + requestExecutionServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { + null + } else { + val appId = checkNotNull(configModule.configService.appId) + val coreBaseUrl = configModule.configService.sdkEndpointBehavior.getData(appId) + val lazyDeviceId = lazy(androidServicesModule.preferencesService::deviceIdentifier) + if (configModule.configService.autoDataCaptureBehavior.shouldUseOkHttp()) { + OkHttpRequestExecutionService( + coreBaseUrl, + lazyDeviceId, + appId, + BuildConfig.VERSION_NAME, + initModule.logger, + ) + } else { + HttpUrlConnectionRequestExecutionService( + coreBaseUrl, + lazyDeviceId, + appId, + BuildConfig.VERSION_NAME, + initModule.logger, + ) + } + } } override val schedulingService: SchedulingService? by singleton { diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleSupplier.kt index c8dff6de20..6b98dbef5d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleSupplier.kt @@ -16,10 +16,11 @@ typealias DeliveryModuleSupplier = ( coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, - payloadStorageServiceProvider: Provider, - cacheStorageServiceProvider: Provider, - requestExecutionServiceProvider: Provider, - deliveryServiceProvider: Provider, + androidServicesModule: AndroidServicesModule, + payloadStorageServiceProvider: Provider?, + cacheStorageServiceProvider: Provider?, + requestExecutionServiceProvider: Provider?, + deliveryServiceProvider: Provider?, ) -> DeliveryModule fun createDeliveryModule( @@ -30,10 +31,11 @@ fun createDeliveryModule( coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, - payloadStorageServiceProvider: Provider, - cacheStorageServiceProvider: Provider, - requestExecutionServiceProvider: Provider, - deliveryServiceProvider: Provider, + androidServicesModule: AndroidServicesModule, + payloadStorageServiceProvider: Provider?, + cacheStorageServiceProvider: Provider?, + requestExecutionServiceProvider: Provider?, + deliveryServiceProvider: Provider?, ): DeliveryModule = DeliveryModuleImpl( configModule, initModule, @@ -42,6 +44,7 @@ fun createDeliveryModule( coreModule, storageModule, essentialServiceModule, + androidServicesModule, requestExecutionServiceProvider, payloadStorageServiceProvider, cacheStorageServiceProvider, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt index 388f34de76..f9c7b7574d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt @@ -1,48 +1,16 @@ package io.embrace.android.embracesdk.internal.prefs import android.content.SharedPreferences -import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer import io.embrace.android.embracesdk.internal.utils.Uuid.getEmbUuid -import io.embrace.android.embracesdk.internal.worker.BackgroundWorker -import java.util.concurrent.Callable -import java.util.concurrent.Future -import java.util.concurrent.TimeUnit internal class EmbracePreferencesService( - private val backgroundWorker: BackgroundWorker, - private val lazyPrefs: Lazy, + private val prefs: SharedPreferences, private val clock: Clock, private val serializer: PlatformSerializer, ) : PreferencesService { - // We get SharedPreferences on a background thread because it loads data from disk - // and can block. When client code needs to set/get a preference, getSharedPrefs() will - // block if necessary with Future.get(). Eagerly offloading buys us more time - // for SharedPreferences to load the File and reduces the likelihood of blocking - // when invoked by client code. - private val preferences: Future = Systrace.traceSynchronous("trigger-load-prefs") { - backgroundWorker.submit( - callable = Callable { - Systrace.traceSynchronous("load-prefs") { - lazyPrefs.value - } - } - ) - } - - // fallback from this very unlikely case by just loading on the main thread - private val prefs: SharedPreferences - get() = try { - Systrace.traceSynchronous("get-prefs") { - preferences.get(2, TimeUnit.SECONDS) - } - } catch (exc: Throwable) { - // fallback from this very unlikely case by just loading on the main thread - lazyPrefs.value - } - private fun SharedPreferences.getStringPreference(key: String): String? { return getString(key, null) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt index a4a80a65e1..8aa13c942b 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt @@ -10,7 +10,6 @@ import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan import io.embrace.android.embracesdk.fakes.behavior.FakeSessionBehavior -import io.embrace.android.embracesdk.fakes.fakeBackgroundWorker import io.embrace.android.embracesdk.internal.prefs.EmbracePreferencesService import io.embrace.android.embracesdk.internal.prefs.PreferencesService import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer @@ -43,11 +42,10 @@ internal class EmbraceSessionPropertiesTest { @Before fun setUp() { - val worker = fakeBackgroundWorker() context = ApplicationProvider.getApplicationContext() - val prefs = lazy { PreferenceManager.getDefaultSharedPreferences(context) } + val prefs = PreferenceManager.getDefaultSharedPreferences(context) preferencesService = - EmbracePreferencesService(worker, prefs, fakeClock, EmbraceSerializer()) + EmbracePreferencesService(prefs, fakeClock, EmbraceSerializer()) configService = FakeConfigService( sessionBehavior = FakeSessionBehavior(MAX_SESSION_PROPERTIES_DEFAULT) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt index 9317efd933..9024062d3d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt @@ -1,10 +1,13 @@ package io.embrace.android.embracesdk.internal.injection +import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.internal.prefs.EmbracePreferencesService import io.mockk.mockk import org.junit.Assert.assertTrue import org.junit.Test +import org.junit.runner.RunWith +@RunWith(AndroidJUnit4::class) internal class AndroidServicesModuleImplTest { @Test @@ -13,8 +16,7 @@ internal class AndroidServicesModuleImplTest { val coreModule = createCoreModule(mockk(relaxed = true), initModule) val module = AndroidServicesModuleImpl( initModule = initModule, - coreModule = coreModule, - workerThreadModule = WorkerThreadModuleImpl() + coreModule = coreModule ) assertTrue(module.preferencesService is EmbracePreferencesService) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt index c1a4695a0f..2f69cb6e37 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt @@ -8,6 +8,7 @@ import io.embrace.android.embracesdk.fakes.FakeDeliveryService import io.embrace.android.embracesdk.fakes.FakeOpenTelemetryModule import io.embrace.android.embracesdk.fakes.FakeRequestExecutionService import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior +import io.embrace.android.embracesdk.fakes.injection.FakeAndroidServicesModule import io.embrace.android.embracesdk.fakes.injection.FakeEssentialServiceModule import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeStorageModule @@ -38,9 +39,10 @@ class DeliveryModuleImplTest { CoreModuleImpl(ApplicationProvider.getApplicationContext(), initModule), FakeStorageModule(), FakeEssentialServiceModule(), + FakeAndroidServicesModule(), ::FakeRequestExecutionService, - { null }, - { null }, + null, + null, ::FakeDeliveryService ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt index 1d53f3a400..0bdef72e48 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt @@ -9,7 +9,6 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeSharedPreferences -import io.embrace.android.embracesdk.fakes.fakeBackgroundWorker import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -27,7 +26,6 @@ internal class EmbracePreferencesServiceTest { private lateinit var service: EmbracePreferencesService private lateinit var fakeClock: FakeClock - private val executorService = fakeBackgroundWorker() private val context = ApplicationProvider.getApplicationContext() @Before @@ -35,8 +33,7 @@ internal class EmbracePreferencesServiceTest { prefs = PreferenceManager.getDefaultSharedPreferences(context) fakeClock = FakeClock() service = EmbracePreferencesService( - executorService, - lazy { prefs }, + prefs, fakeClock, EmbraceSerializer() ) @@ -190,8 +187,7 @@ internal class EmbracePreferencesServiceTest { @Test fun `test incrementAndGet returns -1 on an exception`() { service = EmbracePreferencesService( - executorService, - lazy { FakeSharedPreferences(throwExceptionOnGet = true) }, + FakeSharedPreferences(throwExceptionOnGet = true), fakeClock, EmbraceSerializer() ) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index 58821db99d..b43e3ea152 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -14,7 +14,9 @@ import io.embrace.android.embracesdk.fakes.injection.FakeAnrModule import io.embrace.android.embracesdk.fakes.injection.FakeCoreModule import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeNativeCoreModule +import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryService import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.delivery.execution.RequestExecutionService import io.embrace.android.embracesdk.internal.delivery.storage.PayloadStorageService import io.embrace.android.embracesdk.internal.injection.AndroidServicesModule import io.embrace.android.embracesdk.internal.injection.AnrModule @@ -42,13 +44,12 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( val overriddenWorkerThreadModule: WorkerThreadModule = createWorkerThreadModule(), val overriddenAndroidServicesModule: AndroidServicesModule = createAndroidServicesModule( initModule = overriddenInitModule, - coreModule = overriddenCoreModule, - workerThreadModule = overriddenWorkerThreadModule + coreModule = overriddenCoreModule ), val fakeAnrModule: AnrModule = FakeAnrModule(), val fakeNativeFeatureModule: FakeNativeFeatureModule = FakeNativeFeatureModule(), - var cacheStorageServiceProvider: Provider = { null }, - var payloadStorageServiceProvider: Provider = { null }, + var cacheStorageServiceProvider: Provider? = null, + var payloadStorageServiceProvider: Provider? = null, val networkConnectivityService: FakeNetworkConnectivityService = FakeNetworkConnectivityService(), val lifecycleOwner: TestLifecycleOwner = TestLifecycleOwner(initialState = Lifecycle.State.INITIALIZED), ) { @@ -62,7 +63,7 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( openTelemetryModule = overriddenInitModule.openTelemetryModule, coreModuleSupplier = { _, _ -> overriddenCoreModule }, workerThreadModuleSupplier = { overriddenWorkerThreadModule }, - androidServicesModuleSupplier = { _, _, _ -> overriddenAndroidServicesModule }, + androidServicesModuleSupplier = { _, _ -> overriddenAndroidServicesModule }, essentialServiceModuleSupplier = { initModule, configModule, openTelemetryModule, coreModule, workerThreadModule, systemServiceModule, androidServicesModule, storageModule, _, _ -> createEssentialServiceModule( initModule, @@ -76,7 +77,15 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( { lifecycleOwner } ) { networkConnectivityService } }, - deliveryModuleSupplier = { configModule, otelModule, initModule, workerThreadModule, coreModule, storageModule, essentialServiceModule, _, _, requestExecutionServiceProvider, deliveryServiceProvider -> + deliveryModuleSupplier = { configModule, otelModule, initModule, workerThreadModule, coreModule, storageModule, essentialServiceModule, androidServicesModule, _, _, _, _ -> + val requestExecutionServiceProvider: Provider? = when { + useMockWebServer -> null + else -> ::FakeRequestExecutionService + } + val deliveryServiceProvider: Provider? = when { + useMockWebServer -> null + else -> ::FakeDeliveryService + } createDeliveryModule( configModule, otelModule, @@ -85,20 +94,12 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( coreModule, storageModule, essentialServiceModule, + androidServicesModule, payloadStorageServiceProvider = payloadStorageServiceProvider, cacheStorageServiceProvider = cacheStorageServiceProvider, - requestExecutionServiceProvider = { - when { - useMockWebServer -> requestExecutionServiceProvider() - else -> FakeRequestExecutionService() - } - }, - deliveryServiceProvider = { - when { - useMockWebServer -> deliveryServiceProvider() - else -> FakeDeliveryService() - } - }) + requestExecutionServiceProvider = requestExecutionServiceProvider, + deliveryServiceProvider = deliveryServiceProvider + ) }, configModuleSupplier = { initModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, foregroundAction, _ -> createConfigModule( diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index f2a29a03ac..b2453e13a6 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -1,14 +1,10 @@ package io.embrace.android.embracesdk.internal.injection import android.content.Context -import io.embrace.android.embracesdk.core.BuildConfig import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.capture.envelope.session.OtelPayloadMapperImpl -import io.embrace.android.embracesdk.internal.comms.delivery.EmbraceDeliveryService import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl -import io.embrace.android.embracesdk.internal.delivery.execution.HttpUrlConnectionRequestExecutionService -import io.embrace.android.embracesdk.internal.delivery.execution.OkHttpRequestExecutionService import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl import io.embrace.android.embracesdk.internal.logging.InternalErrorType @@ -129,7 +125,6 @@ internal class ModuleInitBootstrapper( val result = if (!isInitialized()) { coreModule = init(CoreModule::class) { coreModuleSupplier(context, initModule) } - val serviceRegistry = coreModule.serviceRegistry workerThreadModule = init(WorkerThreadModule::class) { workerThreadModuleSupplier() } @@ -140,12 +135,8 @@ internal class ModuleInitBootstrapper( } } - systemServiceModule = init(SystemServiceModule::class) { - systemServiceModuleSupplier(coreModule, versionChecker) - } - androidServicesModule = init(AndroidServicesModule::class) { - androidServicesModuleSupplier(initModule, coreModule, workerThreadModule) + androidServicesModuleSupplier(initModule, coreModule) } configModule = init(ConfigModule::class) { @@ -163,12 +154,17 @@ internal class ModuleInitBootstrapper( { null } ) } + val serviceRegistry = coreModule.serviceRegistry postInit(ConfigModule::class) { serviceRegistry.registerService(lazy { configModule.configService }) serviceRegistry.registerService(lazy { configModule.remoteConfigSource }) openTelemetryModule.setupSensitiveKeysBehavior(configModule.configService.sensitiveKeysBehavior) } + systemServiceModule = init(SystemServiceModule::class) { + systemServiceModuleSupplier(coreModule, versionChecker) + } + storageModule = init(StorageModule::class) { storageModuleSupplier(initModule, coreModule, workerThreadModule) } @@ -286,49 +282,15 @@ internal class ModuleInitBootstrapper( coreModule, storageModule, essentialServiceModule, - { null }, - { null }, - { - if (configModule.configService.isOnlyUsingOtelExporters()) { - null - } else { - val appId = checkNotNull(configModule.configService.appId) - val coreBaseUrl = configModule.configService.sdkEndpointBehavior.getData(appId) - val lazyDeviceId = lazy(androidServicesModule.preferencesService::deviceIdentifier) - if (configModule.configService.autoDataCaptureBehavior.shouldUseOkHttp()) { - OkHttpRequestExecutionService( - coreBaseUrl, - lazyDeviceId, - appId, - BuildConfig.VERSION_NAME, - logger, - ) - } else { - HttpUrlConnectionRequestExecutionService( - coreBaseUrl, - lazyDeviceId, - appId, - BuildConfig.VERSION_NAME, - logger, - ) - } - } - }, - { - val apiService = essentialServiceModule.apiService - if (configModule.configService.isOnlyUsingOtelExporters() || apiService == null) { - null - } else { - EmbraceDeliveryService( - storageModule.deliveryCacheManager, - apiService, - initModule.jsonSerializer - ) - } - } + androidServicesModule, + null, + null, + null, + null ) - }.apply { - payloadCachingService?.run { + } + postInit(DeliveryModule::class) { + deliveryModule.payloadCachingService?.run { openTelemetryModule.spanRepository.setSpanUpdateNotifier { reportBackgroundActivityStateChange() } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 35f458da7d..13e234ddab 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -39,14 +39,14 @@ internal fun fakeModuleInitBootstrapper( fakeOpenTelemetryModule: FakeOpenTelemetryModule = FakeOpenTelemetryModule(), coreModuleSupplier: CoreModuleSupplier = { _, _ -> FakeCoreModule() }, systemServiceModuleSupplier: SystemServiceModuleSupplier = { _, _ -> FakeSystemServiceModule() }, - androidServicesModuleSupplier: AndroidServicesModuleSupplier = { _, _, _ -> FakeAndroidServicesModule() }, + androidServicesModuleSupplier: AndroidServicesModuleSupplier = { _, _ -> FakeAndroidServicesModule() }, workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, - deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, + deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, anrModuleSupplier: AnrModuleSupplier = { _, _, _ -> FakeAnrModule() }, logModuleSupplier: LogModuleSupplier = { _, _, _, _, _, _, _, _ -> FakeLogModule() }, nativeCoreModuleSupplier: NativeCoreModuleSupplier = { _ -> FakeNativeCoreModule() }, From aebe158b2a408394ce199bb7311bdfb0ddeeaf4a Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Mon, 11 Nov 2024 17:22:07 +0000 Subject: [PATCH 11/28] refactor: extract url construction --- .../internal/comms/api/ApiRequestMapper.kt | 2 +- .../internal/comms/api/ApiUrlBuilder.kt | 6 +-- .../internal/comms/api/EmbraceApiService.kt | 2 +- .../comms/api/EmbraceApiUrlBuilder.kt | 44 ++++++++++-------- .../internal/comms/api/EmbraceUrl.kt | 1 + .../comms/delivery/PendingApiCallQueue.kt | 2 +- .../internal/config/ConfigService.kt | 6 --- .../internal/config/ConfigServiceImpl.kt | 7 --- .../config/OkHttpRemoteConfigSource.kt | 3 +- .../config/behavior/SdkEndpointBehavior.kt | 14 ------ .../behavior/SdkEndpointBehaviorImpl.kt | 34 -------------- .../internal/injection/ConfigModule.kt | 2 + .../internal/injection/ConfigModuleImpl.kt | 14 ++++++ .../injection/ConfigModuleSupplier.kt | 3 ++ .../internal/injection/DeliveryModuleImpl.kt | 4 +- .../injection/EssentialServiceModule.kt | 2 - .../injection/EssentialServiceModuleImpl.kt | 23 +--------- .../internal/injection/LogModuleImpl.kt | 1 + .../logging/EmbraceNetworkCaptureService.kt | 9 ++-- .../comms/api/ApiRequestMapperTest.kt | 15 ++++-- .../comms/api/EmbraceApiServiceTest.kt | 46 ++++++------------- .../comms/api/EmbraceApiUrlBuilderTest.kt | 31 ++++--------- .../EmbracePendingApiCallsSenderTest.kt | 21 ++++----- .../comms/delivery/PendingApiCallsTest.kt | 2 +- .../config/OkHttpRemoteConfigSourceTest.kt | 19 ++++---- .../behavior/SdkEndpointBehaviorImplTest.kt | 16 ------- .../injection/ConfigModuleImplTest.kt | 6 ++- .../EmbraceNetworkCaptureServiceTest.kt | 12 +++-- .../OkHttpRequestExecutionService.kt | 2 +- .../OkHttpRequestExecutionServiceTest.kt | 4 +- .../embracesdk/internal/comms/api/Endpoint.kt | 3 +- .../actions/EmbraceSetupInterface.kt | 3 +- .../injection/ModuleInitBootstrapper.kt | 1 + .../fakes/FakeModuleInitBootstrapper.kt | 2 +- .../EssentialServiceModuleImplTest.kt | 1 - .../injection/ModuleInitBootstrapperTest.kt | 2 +- .../android/embracesdk/fakes/Behavior.kt | 9 ---- .../embracesdk/fakes/FakeApiUrlBuilder.kt | 9 ++-- .../embracesdk/fakes/FakeConfigModule.kt | 4 +- .../embracesdk/fakes/FakeConfigService.kt | 2 - .../fakes/behavior/FakeSdkEndpointBehavior.kt | 11 ----- .../injection/FakeEssentialServiceModule.kt | 3 -- 42 files changed, 147 insertions(+), 256 deletions(-) delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehavior.kt delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt delete mode 100644 embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImplTest.kt delete mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkEndpointBehavior.kt diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapper.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapper.kt index 8f6b40db0b..bccd822c6c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapper.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapper.kt @@ -13,7 +13,7 @@ internal class ApiRequestMapper( ) { private val apiUrlBuilders = Endpoint.values().associateWith { - urlBuilder.getEmbraceUrlWithSuffix(it.version, it.path) + urlBuilder.resolveUrl(it) } private fun Endpoint.asEmbraceUrl(): String = checkNotNull(apiUrlBuilders[this]) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt index cf55926384..85f7b47b90 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt @@ -16,12 +16,12 @@ interface ApiUrlBuilder { val deviceId: String /** - * Returns the url used to fetch the config. + * Base URL for the data endpoint */ - fun getConfigUrl(): String + val baseDataUrl: String /** * Returns the url used to send data to Embrace. */ - fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String + fun resolveUrl(endpoint: Endpoint): String } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt index 1c7ba875f2..d4098107d9 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt @@ -34,7 +34,7 @@ internal class EmbraceApiService( } private val configUrl by lazy { Systrace.traceSynchronous("config-url-init") { - urlBuilder.getConfigUrl() + urlBuilder.resolveUrl(Endpoint.CONFIG) } } private var lastNetworkStatus: NetworkStatus = NetworkStatus.UNKNOWN diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt index 2e6666f84a..02ce4ae2a8 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt @@ -1,32 +1,36 @@ package io.embrace.android.embracesdk.internal.comms.api import android.os.Build +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig internal class EmbraceApiUrlBuilder( - private val coreBaseUrl: String, - private val configBaseUrl: String, - override val appId: String, - deviceIdImpl: Lazy, - private val lazyAppVersionName: Lazy, + override val deviceId: String, + private val appVersionName: String, + instrumentedConfig: InstrumentedConfig, ) : ApiUrlBuilder { companion object { - private const val CONFIG_API_VERSION = 2 + private const val CONFIG_DEFAULT: String = "config.emb-api.com" + private const val DATA_DEFAULT: String = "data.emb-api.com" } - override val deviceId: String by deviceIdImpl - - private fun getConfigBaseUrl() = "$configBaseUrl/v$CONFIG_API_VERSION/${"config"}" - - private fun getOperatingSystemCode() = Build.VERSION.SDK_INT.toString() + ".0.0" - - override fun getConfigUrl(): String { - return "${getConfigBaseUrl()}?appId=$appId&osVersion=${getOperatingSystemCode()}" + - "&appVersion=${lazyAppVersionName.value}&deviceId=$deviceId" - } - - override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String { - val fullSuffix = if (apiVersion == "v1") "log/$suffix" else suffix - return "$coreBaseUrl/$apiVersion/$fullSuffix" + override val appId: String = checkNotNull(instrumentedConfig.project.getAppId()) + private val coreBaseUrl = instrumentedConfig.baseUrls.getData() ?: "https://a-$appId.$DATA_DEFAULT/api" + private val configBaseUrl = instrumentedConfig.baseUrls.getConfig() ?: "https://a-$appId.$CONFIG_DEFAULT" + private val operatingSystemCode = Build.VERSION.SDK_INT.toString() + ".0.0" + override val baseDataUrl: String = resolveUrl(Endpoint.SESSIONS).split(Endpoint.SESSIONS.path).first() + + override fun resolveUrl(endpoint: Endpoint): String { + val baseUrl = when (endpoint) { + Endpoint.CONFIG -> configBaseUrl + else -> coreBaseUrl + } + val queryParams = when (endpoint) { + Endpoint.CONFIG -> + "?appId=$appId&osVersion=$operatingSystemCode" + + "&appVersion=$appVersionName&deviceId=$deviceId" + else -> "" + } + return "$baseUrl/${endpoint.version}/${endpoint.path}$queryParams" } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceUrl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceUrl.kt index 45ec98f1a9..db25dbe087 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceUrl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceUrl.kt @@ -18,6 +18,7 @@ internal class EmbraceUrl(val url: URL) { return when (url.path.substringAfterLast("/")) { Endpoint.LOGS.path -> Endpoint.LOGS Endpoint.SESSIONS.path -> Endpoint.SESSIONS + Endpoint.CONFIG.path -> Endpoint.CONFIG else -> Endpoint.UNKNOWN } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallQueue.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallQueue.kt index f68f776c7e..2e506c2876 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallQueue.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallQueue.kt @@ -88,7 +88,7 @@ class PendingApiCallQueue( return when (this) { Endpoint.LOGS -> 10 Endpoint.SESSIONS -> 100 - Endpoint.UNKNOWN -> 50 + else -> 50 } } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt index 486e237c57..0995e588bc 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt @@ -9,7 +9,6 @@ import io.embrace.android.embracesdk.internal.config.behavior.DataCaptureEventBe import io.embrace.android.embracesdk.internal.config.behavior.LogMessageBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior import io.embrace.android.embracesdk.internal.config.behavior.SessionBehavior @@ -68,11 +67,6 @@ interface ConfigService { */ val sdkModeBehavior: SdkModeBehavior - /** - * Provides base endpoints the SDK should send data to - */ - val sdkEndpointBehavior: SdkEndpointBehavior - /** * Provides whether the SDK should enable certain 'behavior' of web vitals */ diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt index 3758da0864..5c7cb65e98 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt @@ -19,8 +19,6 @@ import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior @@ -108,11 +106,6 @@ internal class ConfigServiceImpl( remoteSupplier = configProvider ) - override val sdkEndpointBehavior: SdkEndpointBehavior = SdkEndpointBehaviorImpl( - thresholdCheck = thresholdCheck, - instrumentedConfig = instrumentedConfig - ) - override val appExitInfoBehavior: AppExitInfoBehavior = AppExitInfoBehaviorImpl( thresholdCheck = thresholdCheck, remoteSupplier = configProvider, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt index 580045de67..e6bccc02b4 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt @@ -4,6 +4,7 @@ import io.embrace.android.embracesdk.core.BuildConfig import io.embrace.android.embracesdk.internal.comms.api.ApiRequest import io.embrace.android.embracesdk.internal.comms.api.ApiRequestUrl import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder +import io.embrace.android.embracesdk.internal.comms.api.Endpoint import io.embrace.android.embracesdk.internal.comms.api.getHeaders import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer @@ -25,7 +26,7 @@ internal class OkHttpRemoteConfigSource( } private fun fetchConfigImpl(): RemoteConfig? { - val url = apiUrlBuilder.getConfigUrl() + val url = apiUrlBuilder.resolveUrl(Endpoint.CONFIG) val headers = prepareConfigRequest(url).getHeaders() val builder = Request.Builder().url(url) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehavior.kt deleted file mode 100644 index cf00721f0b..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehavior.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.behavior - -interface SdkEndpointBehavior { - - /** - * Data base URL. - */ - fun getData(appId: String?): String - - /** - * Config base URL. - */ - fun getConfig(appId: String?): String -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt deleted file mode 100644 index 7e05fec69f..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.behavior - -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig - -/** - * Provides the behavior that the Background Activity feature should follow. - */ -class SdkEndpointBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - private val instrumentedConfig: InstrumentedConfig, -) : SdkEndpointBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck -) { - - companion object { - const val CONFIG_DEFAULT: String = "config.emb-api.com" - const val DATA_DEFAULT: String = "data.emb-api.com" - } - - override fun getData(appId: String?): String { - if (appId == null) { - return "" - } - return instrumentedConfig.baseUrls.getData() ?: "https://a-$appId.$DATA_DEFAULT" - } - - override fun getConfig(appId: String?): String { - if (appId == null) { - return "" - } - return instrumentedConfig.baseUrls.getConfig() ?: "https://a-$appId.$CONFIG_DEFAULT" - } -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt index b051b60039..61501e64c9 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt @@ -1,9 +1,11 @@ package io.embrace.android.embracesdk.internal.injection +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.RemoteConfigSource interface ConfigModule { val configService: ConfigService val remoteConfigSource: RemoteConfigSource + val urlBuilder: ApiUrlBuilder? } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index dbc8d3539e..a7aca7c04e 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.Systrace +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder +import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.ConfigServiceImpl import io.embrace.android.embracesdk.internal.config.RemoteConfigSource @@ -11,6 +13,7 @@ import io.embrace.android.embracesdk.internal.worker.Worker internal class ConfigModuleImpl( initModule: InitModule, + coreModule: CoreModule, openTelemetryModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, @@ -38,4 +41,15 @@ internal class ConfigModuleImpl( foregroundAction = foregroundAction, ) } + + override val urlBuilder: ApiUrlBuilder? by singleton { + configService.appId ?: return@singleton null + Systrace.traceSynchronous("url-builder-init") { + EmbraceApiUrlBuilder( + deviceId = androidServicesModule.preferencesService.deviceIdentifier, + appVersionName = coreModule.packageVersionInfo.versionName, + instrumentedConfig = initModule.instrumentedConfig, + ) + } + } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt index 1d725d7b59..768a8be251 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt @@ -9,6 +9,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider */ typealias ConfigModuleSupplier = ( initModule: InitModule, + coreModule: CoreModule, openTelemetryModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, @@ -19,6 +20,7 @@ typealias ConfigModuleSupplier = ( fun createConfigModule( initModule: InitModule, + coreModule: CoreModule, openTelemetryModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, @@ -27,6 +29,7 @@ fun createConfigModule( remoteConfigSourceProvider: Provider, ): ConfigModule = ConfigModuleImpl( initModule, + coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt index 5bc5064b00..5bbd842c96 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt @@ -143,8 +143,8 @@ internal class DeliveryModuleImpl( requestExecutionServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { null } else { - val appId = checkNotNull(configModule.configService.appId) - val coreBaseUrl = configModule.configService.sdkEndpointBehavior.getData(appId) + val appId = configModule.configService.appId ?: return@singleton null + val coreBaseUrl = configModule.urlBuilder?.baseDataUrl ?: return@singleton null val lazyDeviceId = lazy(androidServicesModule.preferencesService::deviceIdentifier) if (configModule.configService.autoDataCaptureBehavior.shouldUseOkHttp()) { OkHttpRequestExecutionService( diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModule.kt index d742ac7bf0..39eaed4023 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModule.kt @@ -6,7 +6,6 @@ import io.embrace.android.embracesdk.internal.capture.session.SessionPropertiesS import io.embrace.android.embracesdk.internal.capture.user.UserService import io.embrace.android.embracesdk.internal.comms.api.ApiClient import io.embrace.android.embracesdk.internal.comms.api.ApiService -import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.delivery.PendingApiCallsSender import io.embrace.android.embracesdk.internal.session.id.SessionIdTracker import io.embrace.android.embracesdk.internal.session.lifecycle.ActivityTracker @@ -20,7 +19,6 @@ interface EssentialServiceModule { val processStateService: ProcessStateService val activityLifecycleTracker: ActivityTracker val userService: UserService - val urlBuilder: ApiUrlBuilder val apiClient: ApiClient val apiService: ApiService? val networkConnectivityService: NetworkConnectivityService diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt index 230a46af9d..7f6b819776 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt @@ -15,9 +15,7 @@ import io.embrace.android.embracesdk.internal.comms.api.ApiClient import io.embrace.android.embracesdk.internal.comms.api.ApiClientImpl import io.embrace.android.embracesdk.internal.comms.api.ApiRequest import io.embrace.android.embracesdk.internal.comms.api.ApiService -import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiService -import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.delivery.EmbracePendingApiCallsSender import io.embrace.android.embracesdk.internal.comms.delivery.PendingApiCallsSender import io.embrace.android.embracesdk.internal.session.id.SessionIdTracker @@ -56,25 +54,6 @@ class EssentialServiceModuleImpl( ActivityLifecycleTracker(coreModule.application, initModule.logger) } - override val urlBuilder: ApiUrlBuilder by singleton { - Systrace.traceSynchronous("url-builder-init") { - // We use SdkEndpointBehavior and localConfig directly to avoid a circular dependency - // but we want to access behaviors from ConfigService when possible. - val sdkEndpointBehavior = configService.sdkEndpointBehavior - val appId = checkNotNull(configService.appId) - val coreBaseUrl = sdkEndpointBehavior.getData(appId) - val configBaseUrl = sdkEndpointBehavior.getConfig(appId) - - EmbraceApiUrlBuilder( - coreBaseUrl = coreBaseUrl, - configBaseUrl = configBaseUrl, - appId = appId, - deviceIdImpl = lazyDeviceId, - lazyAppVersionName = lazy { coreModule.packageVersionInfo.versionName } - ) - } - } - override val userService: UserService by singleton { Systrace.traceSynchronous("user-service-init") { EmbraceUserService( @@ -120,7 +99,7 @@ class EssentialServiceModuleImpl( pendingApiCallsSender = pendingApiCallsSender, lazyDeviceId = lazyDeviceId, appId = appId, - urlBuilder = urlBuilder + urlBuilder = checkNotNull(configModule.urlBuilder) ) } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/LogModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/LogModuleImpl.kt index 2e02aca1a4..21fb7d63c9 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/LogModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/LogModuleImpl.kt @@ -30,6 +30,7 @@ internal class LogModuleImpl( androidServicesModule.preferencesService, { networkCaptureDataSource }, configModule.configService, + configModule.urlBuilder, initModule.jsonSerializer, initModule.logger ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureService.kt index d52bafad29..bed4991c1b 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureService.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.network.logging +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig import io.embrace.android.embracesdk.internal.logging.EmbLogger @@ -19,6 +20,7 @@ internal class EmbraceNetworkCaptureService( private val preferencesService: PreferencesService, private val networkCaptureDataSource: Provider, private val configService: ConfigService, + private val urlBuilder: ApiUrlBuilder?, private val serializer: PlatformSerializer, private val logger: EmbLogger, ) : NetworkCaptureService { @@ -41,9 +43,10 @@ internal class EmbraceNetworkCaptureService( } // Embrace data endpoint cannot be captured, even if there is a rule for that. - val appId = configService.appId - if (url.contentEquals(configService.sdkEndpointBehavior.getData(appId))) { - return emptySet() + urlBuilder?.baseDataUrl?.let { + if (url.startsWith(it)) { + return emptySet() + } } val applicableRules = networkCaptureRules.filter { rule -> diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapperTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapperTest.kt index 27065ba416..de8105a82a 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapperTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapperTest.kt @@ -1,5 +1,7 @@ package io.embrace.android.embracesdk.internal.comms.api +import io.embrace.android.embracesdk.fakes.config.FakeBaseUrlConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.payload.Attribute import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.Log @@ -19,11 +21,14 @@ internal class ApiRequestMapperTest { private val deviceId = lazy { "deviceId" } private val mapper = ApiRequestMapper( urlBuilder = EmbraceApiUrlBuilder( - BASE_URL, - CONFIG_URL, - "appId", - deviceId, - lazy { "appVersionName" } + deviceId.value, + "1.0", + FakeInstrumentedConfig( + baseUrls = FakeBaseUrlConfig( + configImpl = CONFIG_URL, + dataImpl = BASE_URL + ) + ) ), lazyDeviceId = deviceId, appId = "appId" diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt index 5af2be9c7f..1333294c72 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt @@ -5,6 +5,7 @@ import io.embrace.android.embracesdk.concurrency.BlockingScheduledExecutorServic import io.embrace.android.embracesdk.fakes.FakeApiClient import io.embrace.android.embracesdk.fakes.FakeDeliveryCacheManager import io.embrace.android.embracesdk.fakes.FakePendingApiCallsSender +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.TypeUtils import io.embrace.android.embracesdk.internal.comms.api.ApiClient.Companion.NO_HTTP_RESPONSE import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryCacheManager @@ -49,11 +50,9 @@ internal class EmbraceApiServiceTest { @Before fun setUp() { apiUrlBuilder = EmbraceApiUrlBuilder( - coreBaseUrl = "https://a-$fakeAppId.data.emb-api.com", - configBaseUrl = "https://a-$fakeAppId.config.emb-api.com", - appId = fakeAppId, - deviceIdImpl = lazy { fakeDeviceId }, - lazyAppVersionName = lazy { fakeAppVersionName } + deviceId = fakeDeviceId, + appVersionName = fakeAppVersionName, + instrumentedConfig = FakeInstrumentedConfig() ) fakeApiClient = FakeApiClient() cachedConfig = CachedConfig( @@ -151,7 +150,7 @@ internal class EmbraceApiServiceTest { var finished = false apiService.sendSession({ it.write(payload) }) { finished = true } verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/spans", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/spans", expectedPayload = payload ) assertTrue(finished) @@ -181,7 +180,7 @@ internal class EmbraceApiServiceTest { val type: ParameterizedType = TypeUtils.parameterizedType(Envelope::class, LogPayload::class) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getGenericsExpectedPayloadSerialized(logsEnvelope, type) ) } @@ -211,28 +210,11 @@ internal class EmbraceApiServiceTest { val request = fakePendingApiCallsSender.retryQueue.single().first val payload = fakePendingApiCallsSender.retryQueue.single().second - assertEquals("https://a-$fakeAppId.data.emb-api.com/v2/logs", request.url.url) + assertEquals("https://a-$fakeAppId.data.emb-api.com/api/v2/logs", request.url.url) val type: ParameterizedType = TypeUtils.parameterizedType(Envelope::class, LogPayload::class) assertArrayEquals(getGenericsExpectedPayloadSerialized(logsEnvelope, type), payload) } - @Test - fun `validate all API endpoint URLs`() { - Endpoint.values().forEach { - if (it.version == "v1") { - assertEquals( - "https://a-$fakeAppId.data.emb-api.com/v1/log/${it.path}", - apiUrlBuilder.getEmbraceUrlWithSuffix("v1", it.path) - ) - } else { - assertEquals( - "https://a-$fakeAppId.data.emb-api.com/v2/${it.path}", - apiUrlBuilder.getEmbraceUrlWithSuffix("v2", it.path) - ) - } - } - } - @Test fun `network request runnable is used`() { initApiService() @@ -247,7 +229,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) assertEquals(0, fakeApiClient.sentRequests.size) val request = fakePendingApiCallsSender.retryQueue.single().first - assertEquals("https://a-$fakeAppId.data.emb-api.com/v2/logs", request.url.url) + assertEquals("https://a-$fakeAppId.data.emb-api.com/api/v2/logs", request.url.url) } @Test @@ -263,7 +245,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(1, fakePendingApiCallsSender.retryQueue.size) @@ -281,7 +263,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(1, fakePendingApiCallsSender.retryQueue.size) @@ -302,7 +284,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) @@ -320,7 +302,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) @@ -341,7 +323,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) @@ -441,7 +423,7 @@ internal class EmbraceApiServiceTest { } companion object { - private const val fakeAppId = "A1B2C" + private const val fakeAppId = "abcde" private const val fakeDeviceId = "ajflkadsflkadslkfjds" private const val fakeAppVersionName = "6.1.0" private val defaultConfigResponseBody = diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt index 77470af413..ae2942194c 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt @@ -1,13 +1,11 @@ package io.embrace.android.embracesdk.internal.comms.api -import io.embrace.android.embracesdk.fakes.createSdkEndpointBehavior -import io.mockk.unmockkAll -import org.junit.After +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test -private const val APP_ID = "o0o0o" +private const val APP_ID = "abcde" private const val APP_VERSION_NAME = "1.0.0" private const val DEVICE_ID = "07D85B44E4E245F4A30E559BFC0D07FF" @@ -16,36 +14,27 @@ internal class EmbraceApiUrlBuilderTest { @Before fun setup() { - val baseUrlLocalConfig = createSdkEndpointBehavior() - apiUrlBuilder = EmbraceApiUrlBuilder( - coreBaseUrl = baseUrlLocalConfig.getData(APP_ID), - configBaseUrl = baseUrlLocalConfig.getConfig(APP_ID), - appId = APP_ID, - deviceIdImpl = lazy { DEVICE_ID }, - lazyAppVersionName = lazy { APP_VERSION_NAME }, + deviceId = DEVICE_ID, + appVersionName = APP_VERSION_NAME, + FakeInstrumentedConfig() ) } - @After - fun tearDown() { - unmockkAll() - } - @Test fun testUrls() { assertEquals( "https://a-$APP_ID.config.emb-api.com/v2/config?appId=$APP_ID&osVersion=0.0.0" + "&appVersion=$APP_VERSION_NAME&deviceId=$DEVICE_ID", - apiUrlBuilder.getConfigUrl() + apiUrlBuilder.resolveUrl(Endpoint.CONFIG) ) assertEquals( - "https://a-$APP_ID.data.emb-api.com/v1/log/suffix", - apiUrlBuilder.getEmbraceUrlWithSuffix("v1", "suffix") + "https://a-$APP_ID.data.emb-api.com/api/v2/logs", + apiUrlBuilder.resolveUrl(Endpoint.LOGS) ) assertEquals( - "https://a-$APP_ID.data.emb-api.com/v2/suffix", - apiUrlBuilder.getEmbraceUrlWithSuffix("v2", "suffix") + "https://a-$APP_ID.data.emb-api.com/api/v2/spans", + apiUrlBuilder.resolveUrl(Endpoint.SESSIONS) ) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt index b2c880ad0a..b6a3db910d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.comms.delivery import io.embrace.android.embracesdk.concurrency.BlockingScheduledExecutorService import io.embrace.android.embracesdk.fakes.FakeClock +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.comms.api.ApiRequest import io.embrace.android.embracesdk.internal.comms.api.ApiRequestMapper import io.embrace.android.embracesdk.internal.comms.api.ApiRequestUrl @@ -251,11 +252,9 @@ internal class EmbracePendingApiCallsSenderTest { repeat(15) { val mapper = ApiRequestMapper( EmbraceApiUrlBuilder( - "https://data.emb-api.com/$it", - "https://config.emb-api.com", - "appId", - lazy { "deviceId" }, - lazy { "appVersionName" } + "deviceId", + "appVersionName", + FakeInstrumentedConfig() ), lazy { "deviceId" }, "appId" @@ -271,22 +270,20 @@ internal class EmbracePendingApiCallsSenderTest { // verify logs were added to the queue, and oldest added requests are dropped assertEquals( - "https://data.emb-api.com/5/v2/logs", + "https://a-abcde.data.emb-api.com/api/v2/logs", queue.pollNextPendingApiCall()?.apiRequest?.url?.url ) assertEquals( - "https://data.emb-api.com/6/v2/logs", + "https://a-abcde.data.emb-api.com/api/v2/logs", queue.pollNextPendingApiCall()?.apiRequest?.url?.url ) // now add some sessions for retry and verify they are returned first val mapper = ApiRequestMapper( EmbraceApiUrlBuilder( - "https://data.emb-api.com/session", - "https://config.emb-api.com", - "appId", - lazy { "deviceId" }, - lazy { "appVersionName" } + "deviceId", + "appVersionName", + FakeInstrumentedConfig() ), lazy { "deviceId" }, "appId" diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallsTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallsTest.kt index 86d2068eec..7332f8e719 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallsTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallsTest.kt @@ -181,8 +181,8 @@ internal class PendingApiCallsTest { private fun Endpoint.getMaxPendingApiCalls(): Int { return when (this) { Endpoint.LOGS -> 10 - Endpoint.UNKNOWN -> 50 Endpoint.SESSIONS -> 100 + else -> 50 } } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt index 7ad7704f52..5b2a5ed2f1 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.internal.config import io.embrace.android.embracesdk.fakes.TestPlatformSerializer +import io.embrace.android.embracesdk.fakes.config.FakeBaseUrlConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig @@ -10,7 +12,6 @@ import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.RecordedRequest import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test @@ -39,11 +40,13 @@ class OkHttpRemoteConfigSourceTest { val baseUrl = server.url("api").toString() client = OkHttpClient.Builder().build() urlBuilder = EmbraceApiUrlBuilder( - coreBaseUrl = baseUrl, - configBaseUrl = baseUrl, - appId = "abcde", - deviceIdImpl = lazy { "deviceId" }, - lazyAppVersionName = lazy { "1.0.0" }, + "deviceId", + "1.0.0", + FakeInstrumentedConfig( + baseUrls = FakeBaseUrlConfig( + configImpl = baseUrl, + ) + ) ) } @@ -130,9 +133,9 @@ class OkHttpRemoteConfigSourceTest { } private fun assertConfigRequestReceived(request: RecordedRequest?) { - assertNotNull(request) + checkNotNull(request) assertEmbraceHeadersAdded(request) - val requestUrl = request?.requestUrl?.toUrl() ?: error("Request URL cannot be null") + val requestUrl = request.requestUrl?.toUrl() ?: error("Request URL cannot be null") assertEquals("/api/v2/config", requestUrl.path) assertEquals("appId=abcde&osVersion=21.0.0&appVersion=1.0.0&deviceId=deviceId", requestUrl.query) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImplTest.kt deleted file mode 100644 index 2c042fbe42..0000000000 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImplTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.behavior - -import io.embrace.android.embracesdk.fakes.createSdkEndpointBehavior -import org.junit.Assert.assertEquals -import org.junit.Test - -internal class SdkEndpointBehaviorImplTest { - - @Test - fun testDefaults() { - with(createSdkEndpointBehavior()) { - assertEquals("https://a-12345.config.emb-api.com", getConfig("12345")) - assertEquals("https://a-12345.data.emb-api.com", getData("12345")) - } - } -} diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt index 1b05922c46..51c460d39d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.injection +import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.fakes.FakeOpenTelemetryModule import io.embrace.android.embracesdk.fakes.injection.FakeAndroidServicesModule @@ -15,8 +16,10 @@ internal class ConfigModuleImplTest { @Test fun `test defaults`() { + val initModule = FakeInitModule() val module = ConfigModuleImpl( - initModule = FakeInitModule(), + initModule = initModule, + coreModule = createCoreModule(ApplicationProvider.getApplicationContext(), initModule), openTelemetryModule = FakeOpenTelemetryModule(), workerThreadModule = FakeWorkerThreadModule(), androidServicesModule = FakeAndroidServicesModule(), @@ -24,5 +27,6 @@ internal class ConfigModuleImplTest { foregroundAction = {}, ) assertNotNull(module.configService) + assertNotNull(module.urlBuilder) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt index 27e25c0fa4..376243f3d5 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt @@ -4,7 +4,9 @@ import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeNetworkCaptureDataSource import io.embrace.android.embracesdk.fakes.FakePreferenceService import io.embrace.android.embracesdk.fakes.FakeSessionIdTracker +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.createNetworkBehavior +import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl @@ -65,10 +67,9 @@ internal class EmbraceNetworkCaptureServiceTest { @Test fun `test capture rule doesn't capture Embrace endpoints`() { - configService.appId = "o0o0o" - val rule = getDefaultRule(urlRegex = "https://a-o0o0o.data.emb-api.com") + val rule = getDefaultRule(urlRegex = "https://a-abcde.data.emb-api.com/api/v2") cfg = RemoteConfig(networkCaptureRules = setOf(rule)) - val result = getService().getNetworkCaptureRules("https://a-o0o0o.data.emb-api.com", "GET") + val result = getService().getNetworkCaptureRules("https://a-abcde.data.emb-api.com/api/v2/spans", "GET") assertEquals(0, result.size) } @@ -195,6 +196,11 @@ internal class EmbraceNetworkCaptureServiceTest { preferenceService, { networkCaptureDataSource }, configService, + EmbraceApiUrlBuilder( + "deviceId", + "1.0.0", + FakeInstrumentedConfig() + ), EmbraceSerializer(), EmbLoggerImpl() ) diff --git a/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt b/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt index 7372fb568f..2719d154bf 100644 --- a/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt +++ b/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt @@ -79,7 +79,7 @@ class OkHttpRequestExecutionService( } private fun Endpoint.getApiRequestFromEndpoint(): ApiRequestV2 = ApiRequestV2( - url = "$coreBaseUrl/v2/${this.path}", + url = "$coreBaseUrl${this.path}", appId = appId, deviceId = lazyDeviceId.value, contentEncoding = "gzip", diff --git a/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt b/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt index bf7a9d1f76..f9666ba45d 100644 --- a/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt +++ b/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt @@ -48,7 +48,7 @@ class OkHttpRequestExecutionServiceTest { protocols = listOf(Protocol.HTTP_2, Protocol.HTTP_1_1) start() } - testServerUrl = server.url("").toString().removeSuffix("/") + testServerUrl = server.url("").toString() requestExecutionService = OkHttpRequestExecutionService( coreBaseUrl = testServerUrl, lazyDeviceId = lazy { testDeviceId }, @@ -69,7 +69,7 @@ class OkHttpRequestExecutionServiceTest { fun `return incomplete if the server does not exist`() { // given a request execution service with a non existent url requestExecutionService = OkHttpRequestExecutionService( - coreBaseUrl = "https://nonexistenturl:1565", + coreBaseUrl = "https://nonexistenturl:1565/", lazyDeviceId = lazy { testDeviceId }, appId = testAppId, embraceVersionName = testEmbraceVersionName, diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/Endpoint.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/Endpoint.kt index 30296e0ecc..be2e2c46b4 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/Endpoint.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/Endpoint.kt @@ -9,5 +9,6 @@ enum class Endpoint( ) { LOGS("logs", "v2"), SESSIONS("spans", "v2"), - UNKNOWN("unknown", "v1") + CONFIG("config", "v2"), + UNKNOWN("unknown", "v1"), } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index b43e3ea152..c241db45b5 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -101,9 +101,10 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( deliveryServiceProvider = deliveryServiceProvider ) }, - configModuleSupplier = { initModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, foregroundAction, _ -> + configModuleSupplier = { initModule, coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, foregroundAction, _ -> createConfigModule( initModule, + coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index b2453e13a6..fd3ade781a 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -142,6 +142,7 @@ internal class ModuleInitBootstrapper( configModule = init(ConfigModule::class) { configModuleSupplier( initModule, + coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 13e234ddab..e400e4f227 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -43,7 +43,7 @@ internal fun fakeModuleInitBootstrapper( workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, - configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, + configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _, _ -> FakeConfigModule() }, dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/EssentialServiceModuleImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/EssentialServiceModuleImplTest.kt index 54181c55e2..8563ce8ebe 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/EssentialServiceModuleImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/EssentialServiceModuleImplTest.kt @@ -40,7 +40,6 @@ internal class EssentialServiceModuleImplTest { ) assertNotNull(module.processStateService) - assertNotNull(module.urlBuilder) assertNotNull(module.apiClient) assertNotNull(module.apiService) assertNotNull(module.activityLifecycleTracker) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt index 855214923e..2b07fd0fca 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt @@ -32,7 +32,7 @@ internal class ModuleInitBootstrapperTest { logger = EmbLoggerImpl() coreModule = FakeCoreModule() moduleInitBootstrapper = ModuleInitBootstrapper( - configModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, + configModuleSupplier = { _, _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, coreModuleSupplier = { _, _ -> coreModule }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> FakeNativeFeatureModule() }, logger = logger diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt index 3e8b00dbd1..0c04b4a055 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt @@ -17,8 +17,6 @@ import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl @@ -102,13 +100,6 @@ fun createSdkModeBehavior( remoteCfg: Provider = { null }, ): SdkModeBehavior = SdkModeBehaviorImpl(thresholdCheck, remoteCfg) -/** - * A [SdkModeBehaviorImpl] that returns default values. - */ -fun createSdkEndpointBehavior( - thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, -): SdkEndpointBehavior = SdkEndpointBehaviorImpl(thresholdCheck, InstrumentedConfigImpl) - /** * A [AppExitInfoBehavior] that returns default values. */ diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt index 821eb6676d..75e2607e7e 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt @@ -1,15 +1,12 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder +import io.embrace.android.embracesdk.internal.comms.api.Endpoint class FakeApiUrlBuilder( - private val config: String = "", - private val other: String = "", override val appId: String = "", override val deviceId: String = "", + override val baseDataUrl: String = "", ) : ApiUrlBuilder { - - override fun getConfigUrl(): String = config - - override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String = other + override fun resolveUrl(endpoint: Endpoint): String = "" } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt index 7cd996fe1b..c5ba2ba6cc 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkBehavior +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.RemoteConfigSource import io.embrace.android.embracesdk.internal.injection.ConfigModule @@ -11,5 +12,6 @@ class FakeConfigModule( captureHttpUrlConnectionRequests = false ) ), - override val remoteConfigSource: RemoteConfigSource = FakeRemoteConfigSource() + override val remoteConfigSource: RemoteConfigSource = FakeRemoteConfigSource(), + override val urlBuilder: ApiUrlBuilder = FakeApiUrlBuilder(), ) : ConfigModule diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt index 5b8152eb3d..21323a17cb 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt @@ -10,7 +10,6 @@ import io.embrace.android.embracesdk.internal.config.behavior.DataCaptureEventBe import io.embrace.android.embracesdk.internal.config.behavior.LogMessageBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior import io.embrace.android.embracesdk.internal.config.behavior.SessionBehavior @@ -35,7 +34,6 @@ class FakeConfigService( override var networkBehavior: NetworkBehavior = createNetworkBehavior(), override var dataCaptureEventBehavior: DataCaptureEventBehavior = createDataCaptureEventBehavior(), override var sdkModeBehavior: SdkModeBehavior = createSdkModeBehavior(), - override var sdkEndpointBehavior: SdkEndpointBehavior = createSdkEndpointBehavior(), override var webViewVitalsBehavior: WebViewVitalsBehavior = createWebViewVitalsBehavior(), override var appExitInfoBehavior: AppExitInfoBehavior = createAppExitInfoBehavior(), override var networkSpanForwardingBehavior: NetworkSpanForwardingBehavior = createNetworkSpanForwardingBehavior(), diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkEndpointBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkEndpointBehavior.kt deleted file mode 100644 index 5752a15f88..0000000000 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkEndpointBehavior.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.embrace.android.embracesdk.fakes.behavior - -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior - -class FakeSdkEndpointBehavior( - private val dataEndpoint: String, - private val configEndpoint: String, -) : SdkEndpointBehavior { - override fun getData(appId: String?): String = dataEndpoint - override fun getConfig(appId: String?): String = configEndpoint -} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeEssentialServiceModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeEssentialServiceModule.kt index a93e288bf7..d9aa61f38d 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeEssentialServiceModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeEssentialServiceModule.kt @@ -3,7 +3,6 @@ package io.embrace.android.embracesdk.fakes.injection import io.embrace.android.embracesdk.fakes.FakeActivityTracker import io.embrace.android.embracesdk.fakes.FakeApiClient import io.embrace.android.embracesdk.fakes.FakeApiService -import io.embrace.android.embracesdk.fakes.FakeApiUrlBuilder import io.embrace.android.embracesdk.fakes.FakeLogWriter import io.embrace.android.embracesdk.fakes.FakeNetworkConnectivityService import io.embrace.android.embracesdk.fakes.FakePendingApiCallsSender @@ -16,7 +15,6 @@ import io.embrace.android.embracesdk.internal.capture.connectivity.NetworkConnec import io.embrace.android.embracesdk.internal.capture.user.UserService import io.embrace.android.embracesdk.internal.comms.api.ApiClient import io.embrace.android.embracesdk.internal.comms.api.ApiService -import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.delivery.PendingApiCallsSender import io.embrace.android.embracesdk.internal.injection.EssentialServiceModule import io.embrace.android.embracesdk.internal.session.id.SessionIdTracker @@ -32,7 +30,6 @@ class FakeEssentialServiceModule( override val apiService: ApiService = FakeApiService(), override val networkConnectivityService: NetworkConnectivityService = FakeNetworkConnectivityService(), override val pendingApiCallsSender: PendingApiCallsSender = FakePendingApiCallsSender(), - override val urlBuilder: ApiUrlBuilder = FakeApiUrlBuilder(), override val logWriter: LogWriter = FakeLogWriter(), override val sessionPropertiesService: FakeSessionPropertiesService = FakeSessionPropertiesService(), ) : EssentialServiceModule From 20c02c909a22b3aec280d1d871118436e60bd44e Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Tue, 12 Nov 2024 10:44:35 +0000 Subject: [PATCH 12/28] fix okhttp implementation --- .../config/OkHttpRemoteConfigSource.kt | 9 +++++++-- .../internal/injection/ConfigModule.kt | 2 +- .../internal/injection/ConfigModuleImpl.kt | 3 ++- .../config/OkHttpRemoteConfigSourceTest.kt | 19 ++++++++++++++++--- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt index e6bccc02b4..6bdc5db0f5 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt @@ -11,6 +11,8 @@ import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer import io.embrace.android.embracesdk.network.http.HttpMethod import okhttp3.OkHttpClient import okhttp3.Request +import okio.GzipSource +import okio.buffer import java.io.IOException internal class OkHttpRemoteConfigSource( @@ -41,8 +43,11 @@ internal class OkHttpRemoteConfigSource( if (!response.isSuccessful) { return null } - val config = response.body?.source()?.inputStream()?.buffered()?.use { - serializer.fromJson(it, RemoteConfig::class.java) + val config = response.body?.source()?.use { src -> + val gzipSource = GzipSource(src) + gzipSource.buffer().inputStream().use { + serializer.fromJson(it, RemoteConfig::class.java) + } } return config } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt index 61501e64c9..c8abf38a58 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt @@ -6,6 +6,6 @@ import io.embrace.android.embracesdk.internal.config.RemoteConfigSource interface ConfigModule { val configService: ConfigService - val remoteConfigSource: RemoteConfigSource + val remoteConfigSource: RemoteConfigSource? val urlBuilder: ApiUrlBuilder? } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index a7aca7c04e..8981fd6a12 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -29,12 +29,13 @@ internal class ConfigModuleImpl( preferencesService = androidServicesModule.preferencesService, suppliedFramework = framework, instrumentedConfig = initModule.instrumentedConfig, - configProvider = remoteConfigSource::getConfig, + configProvider = { remoteConfigSource?.getConfig() }, ) } } override val remoteConfigSource by singleton { + urlBuilder ?: return@singleton null remoteConfigSourceProvider() ?: RemoteConfigSourceImpl( clock = initModule.clock, backgroundWorker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt index 5b2a5ed2f1..8d95965b4e 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -11,6 +11,9 @@ import okhttp3.Protocol import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.RecordedRequest +import okio.Buffer +import okio.GzipSink +import okio.buffer import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Before @@ -29,7 +32,8 @@ class OkHttpRemoteConfigSourceTest { private val remoteConfig = RemoteConfig( backgroundActivityConfig = BackgroundActivityRemoteConfig(100f) ) - private val configResponse = TestPlatformSerializer().toJson(remoteConfig) + + private lateinit var configResponseBuffer: Buffer @Before fun setUp() { @@ -48,12 +52,21 @@ class OkHttpRemoteConfigSourceTest { ) ) ) + + // serialize the config response + configResponseBuffer = Buffer() + val gzipSink = GzipSink(configResponseBuffer).buffer() + TestPlatformSerializer().toJson( + remoteConfig, + RemoteConfig::class.java, + gzipSink.outputStream() + ) } @Test fun `test config 2xx`() { val (cfg, request) = executeRequest( - MockResponse().setResponseCode(200).setBody(configResponse) + MockResponse().setResponseCode(200).setBody(configResponseBuffer) ) assertConfigRequestReceived(request) assertConfigResponseDeserialized(cfg) @@ -91,7 +104,7 @@ class OkHttpRemoteConfigSourceTest { val (cfg, request) = executeRequest( MockResponse() .setResponseCode(200) - .setBody(configResponse) + .setBody(configResponseBuffer) .throttleBody(1, 1, TimeUnit.MILLISECONDS) ) assertConfigRequestReceived(request) From 2e639efcc1eaaf5c960cb122361a0e684cff3e78 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Tue, 12 Nov 2024 12:02:49 +0000 Subject: [PATCH 13/28] refactor: implement store for remote config --- .../internal/comms/api/ApiService.kt | 2 +- .../internal/config/RemoteConfigSource.kt | 16 -------- .../{ => source}/CachedRemoteConfigSource.kt | 2 +- .../{ => source}/OkHttpRemoteConfigSource.kt | 2 +- .../config/source/RemoteConfigSource.kt | 11 +++++ .../{ => source}/RemoteConfigSourceImpl.kt | 2 +- .../config/store/RemoteConfigStore.kt | 15 +++++++ .../config/store/RemoteConfigStoreImpl.kt | 36 ++++++++++++++++ .../internal/injection/ConfigModule.kt | 4 +- .../internal/injection/ConfigModuleImpl.kt | 14 ++++++- .../injection/ConfigModuleSupplier.kt | 2 +- .../internal/config/ConfigServiceImplTest.kt | 2 + .../config/OkHttpRemoteConfigSourceTest.kt | 1 + .../config/store/RemoteConfigStoreImplTest.kt | 41 +++++++++++++++++++ .../injection/ModuleInitBootstrapper.kt | 2 +- .../embracesdk/fakes/FakeConfigModule.kt | 3 +- .../fakes/FakeRemoteConfigSource.kt | 2 +- .../embracesdk/fakes/FakeRemoteConfigStore.kt | 11 +++++ 18 files changed, 141 insertions(+), 27 deletions(-) delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSource.kt rename embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/{ => source}/CachedRemoteConfigSource.kt (73%) rename embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/{ => source}/OkHttpRemoteConfigSource.kt (97%) create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt rename embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/{ => source}/RemoteConfigSourceImpl.kt (98%) create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt create mode 100644 embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt index 9c619c2733..e129ad8daa 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.comms.api import io.embrace.android.embracesdk.internal.capture.connectivity.NetworkConnectivityListener -import io.embrace.android.embracesdk.internal.config.CachedRemoteConfigSource +import io.embrace.android.embracesdk.internal.config.source.CachedRemoteConfigSource import io.embrace.android.embracesdk.internal.injection.SerializationAction import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.LogPayload diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSource.kt deleted file mode 100644 index f75ab62132..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSource.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.embrace.android.embracesdk.internal.config - -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig - -interface RemoteConfigSource { - - /** - * Asynchronously gets the app's SDK configuration. - * - * These settings define app-specific settings, such as disabled log patterns, whether - * screenshots are enabled, as well as limits and thresholds. - * - * @return a future containing the configuration. - */ - fun getConfig(): RemoteConfig? -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/CachedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt similarity index 73% rename from embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/CachedRemoteConfigSource.kt rename to embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt index 7e775eb406..c8c681ef65 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/CachedRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt @@ -1,4 +1,4 @@ -package io.embrace.android.embracesdk.internal.config +package io.embrace.android.embracesdk.internal.config.source import io.embrace.android.embracesdk.internal.comms.api.CachedConfig diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt similarity index 97% rename from embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt rename to embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt index 6bdc5db0f5..0a515cdafc 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt @@ -1,4 +1,4 @@ -package io.embrace.android.embracesdk.internal.config +package io.embrace.android.embracesdk.internal.config.source import io.embrace.android.embracesdk.core.BuildConfig import io.embrace.android.embracesdk.internal.comms.api.ApiRequest diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt new file mode 100644 index 0000000000..3ca65d49f2 --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt @@ -0,0 +1,11 @@ +package io.embrace.android.embracesdk.internal.config.source + +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +interface RemoteConfigSource { + + /** + * Gets the remotely delivered configuration that should apply to the app for the lifetime of this process, if any. + */ + fun getConfig(): RemoteConfig? +} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSourceImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt similarity index 98% rename from embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSourceImpl.kt rename to embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt index 973284a799..4fcfe9a0b6 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/RemoteConfigSourceImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt @@ -1,4 +1,4 @@ -package io.embrace.android.embracesdk.internal.config +package io.embrace.android.embracesdk.internal.config.source import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt new file mode 100644 index 0000000000..496ab38778 --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt @@ -0,0 +1,15 @@ +package io.embrace.android.embracesdk.internal.config.store + +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource + +/** + * Interface for storing and loading the most recently received remote configuration. + */ +interface RemoteConfigStore : RemoteConfigSource { + + /** + * Saves a new remote configuration, overwriting whatever was stored before. + */ + fun save(config: RemoteConfig) +} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt new file mode 100644 index 0000000000..ab8572edbd --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt @@ -0,0 +1,36 @@ +package io.embrace.android.embracesdk.internal.config.store + +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer +import java.io.File +import java.util.zip.GZIPInputStream +import java.util.zip.GZIPOutputStream + +internal class RemoteConfigStoreImpl( + private val serializer: PlatformSerializer, + storageDir: File, +) : RemoteConfigStore { + + private val configFile = File(storageDir, "most_recent_response").apply { + createNewFile() + } + + override fun getConfig(): RemoteConfig? { + try { + GZIPInputStream(configFile.inputStream().buffered()).use { + return serializer.fromJson(it, RemoteConfig::class.java) + } + } catch (exc: Exception) { + return null + } + } + + override fun save(config: RemoteConfig) { + try { + GZIPOutputStream(configFile.outputStream().buffered()).use { stream -> + serializer.toJson(config, RemoteConfig::class.java, stream) + } + } catch (ignored: Exception) { + } + } +} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt index c8abf38a58..129ae2ac8e 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt @@ -2,10 +2,12 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService -import io.embrace.android.embracesdk.internal.config.RemoteConfigSource +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource +import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore interface ConfigModule { val configService: ConfigService val remoteConfigSource: RemoteConfigSource? + val remoteConfigStore: RemoteConfigStore val urlBuilder: ApiUrlBuilder? } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index 8981fd6a12..f17881d70e 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -5,11 +5,14 @@ import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.ConfigServiceImpl -import io.embrace.android.embracesdk.internal.config.RemoteConfigSource -import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSourceImpl +import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore +import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStoreImpl import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.internal.worker.Worker +import java.io.File internal class ConfigModuleImpl( initModule: InitModule, @@ -43,6 +46,13 @@ internal class ConfigModuleImpl( ) } + override val remoteConfigStore: RemoteConfigStore by singleton { + RemoteConfigStoreImpl( + serializer = initModule.jsonSerializer, + storageDir = File(coreModule.context.filesDir, "embrace_remote_config"), + ) + } + override val urlBuilder: ApiUrlBuilder? by singleton { configService.appId ?: return@singleton null Systrace.traceSynchronous("url-builder-init") { diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt index 768a8be251..a26bdde08c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt @@ -1,6 +1,6 @@ package io.embrace.android.embracesdk.internal.injection -import io.embrace.android.embracesdk.internal.config.RemoteConfigSource +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.utils.Provider diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt index f8761dc5e6..57142107aa 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt @@ -12,6 +12,8 @@ import io.embrace.android.embracesdk.internal.comms.api.CachedConfig import io.embrace.android.embracesdk.internal.config.behavior.BehaviorThresholdCheck import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.CachedRemoteConfigSource +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSourceImpl import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl import io.embrace.android.embracesdk.internal.logs.LogSinkImpl diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt index 8d95965b4e..fbd75426ff 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -6,6 +6,7 @@ import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.OkHttpRemoteConfigSource import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.mockwebserver.MockResponse diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt new file mode 100644 index 0000000000..07ce08cfa5 --- /dev/null +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt @@ -0,0 +1,41 @@ +package io.embrace.android.embracesdk.internal.config.store + +import io.embrace.android.embracesdk.fakes.TestPlatformSerializer +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import java.io.File +import java.nio.file.Files + +internal class RemoteConfigStoreImplTest { + + private lateinit var store: RemoteConfigStoreImpl + private lateinit var dir: File + + @Before + fun setUp() { + dir = Files.createTempDirectory("test").toFile() + store = RemoteConfigStoreImpl(TestPlatformSerializer(), dir) + } + + @Test + fun `test config store`() { + assertNull(store.getConfig()) + + // store a config + val config = RemoteConfig(50) + store.save(config) + + // load the config + val loaded = checkNotNull(store.getConfig()) + assertEquals(config, loaded) + + val newConfig = RemoteConfig(100) + store.save(newConfig) + + val newLoaded = checkNotNull(store.getConfig()) + assertEquals(newConfig, newLoaded) + } +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index fd3ade781a..7edfd8fc1c 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -4,7 +4,7 @@ import android.content.Context import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.capture.envelope.session.OtelPayloadMapperImpl -import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSourceImpl import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl import io.embrace.android.embracesdk.internal.logging.InternalErrorType diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt index c5ba2ba6cc..9cad37aa53 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt @@ -3,7 +3,7 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkBehavior import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService -import io.embrace.android.embracesdk.internal.config.RemoteConfigSource +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource import io.embrace.android.embracesdk.internal.injection.ConfigModule class FakeConfigModule( @@ -13,5 +13,6 @@ class FakeConfigModule( ) ), override val remoteConfigSource: RemoteConfigSource = FakeRemoteConfigSource(), + override val remoteConfigStore: FakeRemoteConfigStore = FakeRemoteConfigStore(), override val urlBuilder: ApiUrlBuilder = FakeApiUrlBuilder(), ) : ConfigModule diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt index 62b389db31..0fc71c9b7e 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.fakes -import io.embrace.android.embracesdk.internal.config.RemoteConfigSource import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource class FakeRemoteConfigSource( var cfg: RemoteConfig? = null diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt new file mode 100644 index 0000000000..e56feedaac --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt @@ -0,0 +1,11 @@ +package io.embrace.android.embracesdk.fakes + +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore + +class FakeRemoteConfigStore : RemoteConfigStore { + override fun getConfig(): RemoteConfig? = null + + override fun save(config: RemoteConfig) { + } +} From 544b0c6c0591be32fac0a7592cb98e2a49e1807b Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Tue, 12 Nov 2024 12:53:17 +0000 Subject: [PATCH 14/28] refactor: create combined source of remote config --- .../source/CombinedRemoteConfigSource.kt | 32 +++++++++++ .../source/CombinedRemoteConfigSourceTest.kt | 55 +++++++++++++++++++ .../fakes/FakeRemoteConfigSource.kt | 8 ++- .../embracesdk/fakes/FakeRemoteConfigStore.kt | 6 +- 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt create mode 100644 embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt new file mode 100644 index 0000000000..389d508881 --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt @@ -0,0 +1,32 @@ +package io.embrace.android.embracesdk.internal.config.source + +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore +import io.embrace.android.embracesdk.internal.worker.BackgroundWorker +import java.util.concurrent.TimeUnit + +internal class CombinedRemoteConfigSource( + private val store: RemoteConfigStore, + private val httpSource: RemoteConfigSource, + private val worker: BackgroundWorker, + private val intervalMs: Long = 60 * 60 * 1000 +) : RemoteConfigSource { + + // the remote config that is used for the lifetime of the process. + private val cfg = store.getConfig() + + override fun getConfig(): RemoteConfig? = cfg + + fun scheduleConfigRequests() { + worker.scheduleWithFixedDelay( + ::attemptConfigRequest, + 0, + intervalMs, + TimeUnit.MILLISECONDS + ) + } + + private fun attemptConfigRequest() { + httpSource.getConfig()?.let(store::save) + } +} diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt new file mode 100644 index 0000000000..e33208f4c2 --- /dev/null +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt @@ -0,0 +1,55 @@ +package io.embrace.android.embracesdk.internal.config.source + +import io.embrace.android.embracesdk.concurrency.BlockingScheduledExecutorService +import io.embrace.android.embracesdk.fakes.FakeRemoteConfigSource +import io.embrace.android.embracesdk.fakes.FakeRemoteConfigStore +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.worker.BackgroundWorker +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test + +class CombinedRemoteConfigSourceTest { + + private lateinit var source: CombinedRemoteConfigSource + private lateinit var remoteConfig: RemoteConfig + private lateinit var executorService: BlockingScheduledExecutorService + private lateinit var remoteConfigSource: FakeRemoteConfigSource + + @Before + fun setUp() { + remoteConfig = RemoteConfig(92) + executorService = BlockingScheduledExecutorService() + remoteConfigSource = FakeRemoteConfigSource(remoteConfig) + source = CombinedRemoteConfigSource( + FakeRemoteConfigStore(), + remoteConfigSource, + BackgroundWorker(executorService) + ) + } + + @Test + fun `test initial config null`() { + assertNull(source.getConfig()) + } + + @Test + fun `test initial config populated`() { + val cfg = RemoteConfig(100) + source = CombinedRemoteConfigSource( + FakeRemoteConfigStore(cfg), + remoteConfigSource, + BackgroundWorker(executorService) + ) + assertEquals(cfg, source.getConfig()) + } + + @Test + fun `test requests scheduled`() { + assertEquals(0, remoteConfigSource.callCount) + source.scheduleConfigRequests() + executorService.runCurrentlyBlocked() + assertEquals(1, remoteConfigSource.callCount) + } +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt index 0fc71c9b7e..913633925e 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt @@ -6,5 +6,11 @@ import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource class FakeRemoteConfigSource( var cfg: RemoteConfig? = null ) : RemoteConfigSource { - override fun getConfig(): RemoteConfig? = cfg + + var callCount: Int = 0 + + override fun getConfig(): RemoteConfig? { + callCount++ + return cfg + } } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt index e56feedaac..4601f362c0 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt @@ -3,8 +3,10 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore -class FakeRemoteConfigStore : RemoteConfigStore { - override fun getConfig(): RemoteConfig? = null +class FakeRemoteConfigStore( + val impl: RemoteConfig? = null +) : RemoteConfigStore { + override fun getConfig(): RemoteConfig? = impl override fun save(config: RemoteConfig) { } From 64b193d463c4f8158dbce3043d596a1d2b552094 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Tue, 12 Nov 2024 14:02:17 +0000 Subject: [PATCH 15/28] refactor: add etag support to okhttp --- .../config/source/CachedRemoteConfigSource.kt | 8 ++++- .../source/CombinedRemoteConfigSource.kt | 11 ++++--- .../config/source/OkHttpRemoteConfigSource.kt | 31 +++++++++++++++---- .../config/source/RemoteConfigSource.kt | 5 +++ .../config/source/RemoteConfigSourceImpl.kt | 4 +++ .../config/store/RemoteConfigStore.kt | 20 ++++++++++-- .../config/store/RemoteConfigStoreImpl.kt | 25 +++++++++++++-- .../config/OkHttpRemoteConfigSourceTest.kt | 30 ++++++++++++------ .../source/CombinedRemoteConfigSourceTest.kt | 15 ++++++++- .../config/store/RemoteConfigStoreImplTest.kt | 20 +++++++++--- .../fakes/FakeRemoteConfigSource.kt | 5 +++ .../embracesdk/fakes/FakeRemoteConfigStore.kt | 14 +++++++-- 12 files changed, 153 insertions(+), 35 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt index c8c681ef65..ec1d1157b0 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt @@ -1,8 +1,14 @@ package io.embrace.android.embracesdk.internal.config.source import io.embrace.android.embracesdk.internal.comms.api.CachedConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -interface CachedRemoteConfigSource : RemoteConfigSource { +interface CachedRemoteConfigSource { + + /** + * Gets the remotely delivered configuration that should apply to the app for the lifetime of this process, if any. + */ + fun getConfig(): RemoteConfig? fun getCachedConfig(): CachedConfig } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt index 389d508881..f242807324 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt @@ -10,14 +10,17 @@ internal class CombinedRemoteConfigSource( private val httpSource: RemoteConfigSource, private val worker: BackgroundWorker, private val intervalMs: Long = 60 * 60 * 1000 -) : RemoteConfigSource { +) { // the remote config that is used for the lifetime of the process. - private val cfg = store.getConfig() + private val cfg = store.loadConfig() - override fun getConfig(): RemoteConfig? = cfg + fun getConfig(): RemoteConfig? = cfg fun scheduleConfigRequests() { + worker.submit { + store.retrieveEtag()?.let(httpSource::setInitialEtag) + } worker.scheduleWithFixedDelay( ::attemptConfigRequest, 0, @@ -27,6 +30,6 @@ internal class CombinedRemoteConfigSource( } private fun attemptConfigRequest() { - httpSource.getConfig()?.let(store::save) + httpSource.getConfig()?.let(store::saveConfig) } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt index 0a515cdafc..6aa20e48f6 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt @@ -11,6 +11,7 @@ import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer import io.embrace.android.embracesdk.network.http.HttpMethod import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.Response import okio.GzipSource import okio.buffer import java.io.IOException @@ -27,29 +28,47 @@ internal class OkHttpRemoteConfigSource( null } + override fun setInitialEtag(etag: String) { + this.etag = etag + } + + private var etag: String? = null + private fun fetchConfigImpl(): RemoteConfig? { + val request = prepareRequest() + val call = okhttpClient.newCall(request) + val response = call.execute() + return processResponse(response) + } + + private fun prepareRequest(): Request { val url = apiUrlBuilder.resolveUrl(Endpoint.CONFIG) val headers = prepareConfigRequest(url).getHeaders() val builder = Request.Builder().url(url) + etag?.let { + builder.header("If-None-Match", it) + } headers.forEach { entry -> builder.header(entry.key, entry.value) } - val call = okhttpClient.newCall( - builder.build() - ) + val request = builder.build() + return request + } - val response = call.execute() + private fun processResponse(response: Response): RemoteConfig? { + response.header("ETag")?.let { + this.etag = it + } if (!response.isSuccessful) { return null } - val config = response.body?.source()?.use { src -> + return response.body?.source()?.use { src -> val gzipSource = GzipSource(src) gzipSource.buffer().inputStream().use { serializer.fromJson(it, RemoteConfig::class.java) } } - return config } private fun prepareConfigRequest(url: String) = ApiRequest( diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt index 3ca65d49f2..ffe7bda09c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt @@ -8,4 +8,9 @@ interface RemoteConfigSource { * Gets the remotely delivered configuration that should apply to the app for the lifetime of this process, if any. */ fun getConfig(): RemoteConfig? + + /** + * Sets the initial ETag to use for the first request, if any. + */ + fun setInitialEtag(etag: String) } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt index 4fcfe9a0b6..9a0f6c4d10 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt @@ -24,6 +24,10 @@ class RemoteConfigSourceImpl( attemptConfigRefresh() } + override fun setInitialEtag(etag: String) { + // no-op + } + @Volatile private var configProp = RemoteConfig() diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt index 496ab38778..7326140eea 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt @@ -1,15 +1,29 @@ package io.embrace.android.embracesdk.internal.config.store import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource /** * Interface for storing and loading the most recently received remote configuration. */ -interface RemoteConfigStore : RemoteConfigSource { +interface RemoteConfigStore { + + /** + * Loads the most recent remote configuration, if any. + */ + fun loadConfig(): RemoteConfig? /** * Saves a new remote configuration, overwriting whatever was stored before. */ - fun save(config: RemoteConfig) + fun saveConfig(config: RemoteConfig) + + /** + * Retrieves the most recently stored ETag, if any. + */ + fun retrieveEtag(): String? + + /** + * Stores the most recently received ETag + */ + fun storeEtag(etag: String) } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt index ab8572edbd..3eeeef4a15 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt @@ -15,7 +15,11 @@ internal class RemoteConfigStoreImpl( createNewFile() } - override fun getConfig(): RemoteConfig? { + private val etagFile = File(storageDir, "etag").apply { + createNewFile() + } + + override fun loadConfig(): RemoteConfig? { try { GZIPInputStream(configFile.inputStream().buffered()).use { return serializer.fromJson(it, RemoteConfig::class.java) @@ -25,7 +29,7 @@ internal class RemoteConfigStoreImpl( } } - override fun save(config: RemoteConfig) { + override fun saveConfig(config: RemoteConfig) { try { GZIPOutputStream(configFile.outputStream().buffered()).use { stream -> serializer.toJson(config, RemoteConfig::class.java, stream) @@ -33,4 +37,21 @@ internal class RemoteConfigStoreImpl( } catch (ignored: Exception) { } } + + override fun retrieveEtag(): String? { + return try { + etagFile.readText().ifEmpty { + null + } + } catch (exc: Exception) { + null + } + } + + override fun storeEtag(etag: String) { + try { + etagFile.writeText(etag) + } catch (ignored: Exception) { + } + } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt index fbd75426ff..434ef2622f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -29,6 +29,7 @@ class OkHttpRemoteConfigSourceTest { private lateinit var server: MockWebServer private lateinit var client: OkHttpClient private lateinit var urlBuilder: EmbraceApiUrlBuilder + private lateinit var source: OkHttpRemoteConfigSource private val remoteConfig = RemoteConfig( backgroundActivityConfig = BackgroundActivityRemoteConfig(100f) @@ -62,6 +63,7 @@ class OkHttpRemoteConfigSourceTest { RemoteConfig::class.java, gzipSink.outputStream() ) + source = OkHttpRemoteConfigSource(client, urlBuilder, TestPlatformSerializer()) } @Test @@ -100,25 +102,34 @@ class OkHttpRemoteConfigSourceTest { } @Test - fun `test call timeout in middle of server response`() { - client = client.newBuilder().callTimeout(5, TimeUnit.MILLISECONDS).build() + fun `test invalid response from server`() { val (cfg, request) = executeRequest( - MockResponse() - .setResponseCode(200) - .setBody(configResponseBuffer) - .throttleBody(1, 1, TimeUnit.MILLISECONDS) + MockResponse().setResponseCode(200).setBody("{") ) assertConfigRequestReceived(request) assertConfigResponseNotDeserialized(cfg) } @Test - fun `test invalid response from server`() { + fun `test etag header respected`() { + val etagValue = "attempt_1" val (cfg, request) = executeRequest( - MockResponse().setResponseCode(200).setBody("{") + MockResponse().setResponseCode(200) + .setBody(configResponseBuffer) + .setHeader("ETag", etagValue) ) assertConfigRequestReceived(request) - assertConfigResponseNotDeserialized(cfg) + assertNull(request?.getHeader("If-None-Match")) + assertConfigResponseDeserialized(cfg) + + // second request with etag + val (secondCfg, secondRequest) = executeRequest( + MockResponse().setResponseCode(304) + .setHeader("ETag", etagValue) + ) + assertConfigRequestReceived(secondRequest) + assertEquals(etagValue, secondRequest?.getHeader("If-None-Match")) + assertConfigResponseNotDeserialized(secondCfg) } private fun executeRequest(response: MockResponse?): Pair { @@ -126,7 +137,6 @@ class OkHttpRemoteConfigSourceTest { return Pair(null, null) } server.enqueue(response) - val source = OkHttpRemoteConfigSource(client, urlBuilder, TestPlatformSerializer()) val cfg = source.getConfig() val request = pollRequest() CallData(request, cfg) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt index e33208f4c2..daef23297f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt @@ -16,14 +16,16 @@ class CombinedRemoteConfigSourceTest { private lateinit var remoteConfig: RemoteConfig private lateinit var executorService: BlockingScheduledExecutorService private lateinit var remoteConfigSource: FakeRemoteConfigSource + private lateinit var remoteConfigStore: FakeRemoteConfigStore @Before fun setUp() { remoteConfig = RemoteConfig(92) executorService = BlockingScheduledExecutorService() remoteConfigSource = FakeRemoteConfigSource(remoteConfig) + remoteConfigStore = FakeRemoteConfigStore() source = CombinedRemoteConfigSource( - FakeRemoteConfigStore(), + remoteConfigStore, remoteConfigSource, BackgroundWorker(executorService) ) @@ -51,5 +53,16 @@ class CombinedRemoteConfigSourceTest { source.scheduleConfigRequests() executorService.runCurrentlyBlocked() assertEquals(1, remoteConfigSource.callCount) + assertNull(remoteConfigSource.etag) + } + + @Test + fun `test persisted etag value populated`() { + remoteConfigStore.etag = "etag" + assertEquals(0, remoteConfigSource.callCount) + source.scheduleConfigRequests() + executorService.runCurrentlyBlocked() + assertEquals(1, remoteConfigSource.callCount) + assertEquals("etag", remoteConfigSource.etag) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt index 07ce08cfa5..f29ebb3392 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt @@ -22,20 +22,30 @@ internal class RemoteConfigStoreImplTest { @Test fun `test config store`() { - assertNull(store.getConfig()) + assertNull(store.loadConfig()) // store a config val config = RemoteConfig(50) - store.save(config) + store.saveConfig(config) // load the config - val loaded = checkNotNull(store.getConfig()) + val loaded = checkNotNull(store.loadConfig()) assertEquals(config, loaded) val newConfig = RemoteConfig(100) - store.save(newConfig) + store.saveConfig(newConfig) - val newLoaded = checkNotNull(store.getConfig()) + val newLoaded = checkNotNull(store.loadConfig()) assertEquals(newConfig, newLoaded) } + + @Test + fun `test etag store`() { + assertNull(store.retrieveEtag()) + store.storeEtag("etag") + assertEquals("etag", store.retrieveEtag()) + + store.storeEtag("another") + assertEquals("another", store.retrieveEtag()) + } } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt index 913633925e..a22940471c 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt @@ -8,9 +8,14 @@ class FakeRemoteConfigSource( ) : RemoteConfigSource { var callCount: Int = 0 + var etag: String? = null override fun getConfig(): RemoteConfig? { callCount++ return cfg } + + override fun setInitialEtag(etag: String) { + this.etag = etag + } } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt index 4601f362c0..597acd0033 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt @@ -4,10 +4,18 @@ import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore class FakeRemoteConfigStore( - val impl: RemoteConfig? = null + val impl: RemoteConfig? = null, + var etag: String? = null, ) : RemoteConfigStore { - override fun getConfig(): RemoteConfig? = impl - override fun save(config: RemoteConfig) { + override fun loadConfig(): RemoteConfig? = impl + + override fun saveConfig(config: RemoteConfig) { + } + + override fun retrieveEtag(): String? = etag + + override fun storeEtag(etag: String) { + this.etag = etag } } From dddb137173c646addb773c2588ba38e225eaf81e Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Tue, 12 Nov 2024 15:41:02 +0000 Subject: [PATCH 16/28] refactor: alter config implementation to use okhttp --- .../internal/comms/api/ApiResponseCache.kt | 95 ------------ .../internal/comms/api/ApiService.kt | 3 +- .../internal/comms/api/EmbraceApiService.kt | 63 -------- .../config/source/CachedRemoteConfigSource.kt | 14 -- .../source/CombinedRemoteConfigSource.kt | 12 +- .../config/source/ConfigHttpResponse.kt | 8 + .../config/source/OkHttpRemoteConfigSource.kt | 11 +- .../config/source/RemoteConfigSource.kt | 4 +- .../config/source/RemoteConfigSourceImpl.kt | 146 ------------------ .../config/store/RemoteConfigStore.kt | 16 +- .../config/store/RemoteConfigStoreImpl.kt | 39 ++--- .../internal/injection/ConfigModule.kt | 4 + .../internal/injection/ConfigModuleImpl.kt | 47 ++++-- .../injection/ConfigModuleSupplier.kt | 11 +- .../internal/injection/DeliveryModuleImpl.kt | 1 + .../injection/EssentialServiceModuleImpl.kt | 6 - .../internal/injection/StorageModule.kt | 2 - .../internal/injection/StorageModuleImpl.kt | 8 - .../internal/comms/api/CachedConfigTest.kt | 17 -- .../comms/api/EmbraceApiServiceTest.kt | 83 ---------- .../internal/config/ConfigServiceImplTest.kt | 61 +------- .../config/OkHttpRemoteConfigSourceTest.kt | 6 +- .../source/CombinedRemoteConfigSourceTest.kt | 8 +- .../config/store/RemoteConfigStoreImplTest.kt | 27 ++-- .../injection/ConfigModuleImplTest.kt | 1 - .../resources/remote_config_response.json | 15 -- .../OkHttpRequestExecutionService.kt | 14 +- .../OkHttpRequestExecutionServiceTest.kt | 13 +- .../injection/StorageModuleImplTest.kt | 1 - .../internal/comms/api/CachedConfig.kt | 10 -- .../testframework/IntegrationTestRule.kt | 21 ++- .../actions/EmbraceSetupInterface.kt | 7 +- .../injection/ModuleInitBootstrapper.kt | 23 ++- .../fakes/FakeModuleInitBootstrapper.kt | 2 +- .../injection/ModuleInitBootstrapperTest.kt | 2 +- embrace-test-fakes/build.gradle.kts | 1 + .../embracesdk/fakes/FakeApiService.kt | 10 -- .../embracesdk/fakes/FakeConfigModule.kt | 5 + .../fakes/FakeRemoteConfigSource.kt | 6 +- .../embracesdk/fakes/FakeRemoteConfigStore.kt | 16 +- .../fakes/injection/FakeStorageModule.kt | 7 +- 41 files changed, 160 insertions(+), 686 deletions(-) delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiResponseCache.kt delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/ConfigHttpResponse.kt delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt delete mode 100644 embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/CachedConfigTest.kt delete mode 100644 embrace-android-core/src/test/resources/remote_config_response.json delete mode 100644 embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/CachedConfig.kt diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiResponseCache.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiResponseCache.kt deleted file mode 100644 index 1a57e311b3..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiResponseCache.kt +++ /dev/null @@ -1,95 +0,0 @@ -package io.embrace.android.embracesdk.internal.comms.api - -import android.net.http.HttpResponseCache -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer -import io.embrace.android.embracesdk.internal.storage.StorageService -import java.io.Closeable -import java.io.IOException -import java.net.CacheResponse -import java.net.URI -import java.util.zip.GZIPInputStream - -/** - * Caches HTTP requests made via HttpUrlConnection using [HttpResponseCache]. This is - * currently only used to cache responses from the config endpoint, which contain etags in the - * response headers. - * - * This class therefore provides functions to retrieve the etag for any cached responses. This - * means the eTag can be set in the request header & we can avoid unnecessary work on the client - * & on the server. - */ -class ApiResponseCache( - private val serializer: PlatformSerializer, - private val storageService: StorageService, -) : Closeable { - - private companion object { - private const val MAX_CACHE_SIZE_BYTES: Long = 2 * 1024 * 1024 // 2 MiB - private const val ETAG_HEADER = "ETag" - } - - @Volatile - private var cache: HttpResponseCache? = null - private val lock = Object() - - private fun initializeIfNeeded() { - if (cache == null) { - synchronized(lock) { - if (cache == null) { - cache = runCatching { - HttpResponseCache.install( - storageService.getConfigCacheDir(), - MAX_CACHE_SIZE_BYTES - ) - }.getOrNull() - } - } - } - } - - override fun close() { - cache?.flush() - } - - fun retrieveCachedConfig(url: String, request: ApiRequest): CachedConfig { - val cachedResponse = retrieveCacheResponse(url, request) - val obj = cachedResponse?.runCatching { - GZIPInputStream(body).use { - serializer.fromJson(it, RemoteConfig::class.java) - } - }?.getOrNull() - val eTag = cachedResponse?.let { retrieveETag(cachedResponse) } - return CachedConfig(obj, eTag) - } - - /** - * Retrieves the cache response for the given request, if any exists. - */ - private fun retrieveCacheResponse(url: String, request: ApiRequest): CacheResponse? { - initializeIfNeeded() - val obj = cache ?: return null - - return try { - val uri = URI.create(url) - val requestMethod = request.httpMethod.toString() - val headerFields = request.getHeaders().mapValues { listOf(it.value) } - obj.get(uri, requestMethod, headerFields) - } catch (exc: IOException) { - null - } - } - - /** - * Searches the cache to see whether a request has a cached response, and if so returns its etag. - */ - private fun retrieveETag(cacheResponse: CacheResponse): String? { - runCatching { - val eTag = cacheResponse.headers[ETAG_HEADER] - if (!eTag.isNullOrEmpty()) { - return eTag[0] - } - } - return null - } -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt index e129ad8daa..b84009f5cf 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiService.kt @@ -1,13 +1,12 @@ package io.embrace.android.embracesdk.internal.comms.api import io.embrace.android.embracesdk.internal.capture.connectivity.NetworkConnectivityListener -import io.embrace.android.embracesdk.internal.config.source.CachedRemoteConfigSource import io.embrace.android.embracesdk.internal.injection.SerializationAction import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.LogPayload import java.util.concurrent.Future -interface ApiService : CachedRemoteConfigSource, NetworkConnectivityListener { +interface ApiService : NetworkConnectivityListener { /** * Sends a list of OTel Logs to the API. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt index d4098107d9..7ab5df10b7 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt @@ -1,25 +1,21 @@ package io.embrace.android.embracesdk.internal.comms.api -import io.embrace.android.embracesdk.core.BuildConfig import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.TypeUtils import io.embrace.android.embracesdk.internal.comms.delivery.NetworkStatus import io.embrace.android.embracesdk.internal.comms.delivery.PendingApiCallsSender import io.embrace.android.embracesdk.internal.compression.ConditionalGzipOutputStream -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.injection.SerializationAction import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.LogPayload import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer import io.embrace.android.embracesdk.internal.worker.PriorityWorker -import io.embrace.android.embracesdk.network.http.HttpMethod import java.lang.reflect.ParameterizedType import java.util.concurrent.Future internal class EmbraceApiService( private val apiClient: ApiClient, private val serializer: PlatformSerializer, - private val cachedConfigProvider: (url: String, request: ApiRequest) -> CachedConfig, private val priorityWorker: PriorityWorker, private val pendingApiCallsSender: PendingApiCallsSender, lazyDeviceId: Lazy, @@ -32,11 +28,6 @@ internal class EmbraceApiService( ApiRequestMapper(urlBuilder, lazyDeviceId, appId) } } - private val configUrl by lazy { - Systrace.traceSynchronous("config-url-init") { - urlBuilder.resolveUrl(Endpoint.CONFIG) - } - } private var lastNetworkStatus: NetworkStatus = NetworkStatus.UNKNOWN init { @@ -45,60 +36,6 @@ internal class EmbraceApiService( } } - @Throws(IllegalStateException::class) - @Suppress("UseCheckOrError") - override fun getConfig(): RemoteConfig? { - var request = prepareConfigRequest(configUrl) - val cachedResponse = cachedConfigProvider(configUrl, request) - if (cachedResponse.isValid()) { // only bother if we have a useful response. - request = request.copy(eTag = cachedResponse.eTag) - } - - return when (val response = apiClient.executeGet(request)) { - is ApiResponse.Success -> { - response.body?.let { - serializer.fromJson(it, RemoteConfig::class.java) - } - } - - is ApiResponse.NotModified -> { - cachedResponse.remoteConfig - } - - is ApiResponse.TooManyRequests -> { - // TODO: We should retry after the retryAfter time or 3 seconds and apply exponential backoff. - null - } - - is ApiResponse.Failure, ApiResponse.None, ApiResponse.NoPayload -> { - null - } - - is ApiResponse.Incomplete -> { - throw response.exception - } - - ApiResponse.PayloadTooLarge -> { - // Not expected to receive a 413 response for a GET request. - null - } - } - } - - override fun getCachedConfig(): CachedConfig { - val request = prepareConfigRequest(configUrl) - return cachedConfigProvider(configUrl, request) - } - - private fun prepareConfigRequest(url: String) = ApiRequest( - contentType = "application/json", - userAgent = "Embrace/a/" + BuildConfig.VERSION_NAME, - accept = "application/json", - url = ApiRequestUrl(url), - httpMethod = HttpMethod.GET, - acceptEncoding = "gzip", - ) - override fun onNetworkConnectivityStatusChanged(status: NetworkStatus) { lastNetworkStatus = status } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt deleted file mode 100644 index ec1d1157b0..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CachedRemoteConfigSource.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.source - -import io.embrace.android.embracesdk.internal.comms.api.CachedConfig -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig - -interface CachedRemoteConfigSource { - - /** - * Gets the remotely delivered configuration that should apply to the app for the lifetime of this process, if any. - */ - fun getConfig(): RemoteConfig? - - fun getCachedConfig(): CachedConfig -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt index f242807324..ded006a02b 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt @@ -5,7 +5,7 @@ import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore import io.embrace.android.embracesdk.internal.worker.BackgroundWorker import java.util.concurrent.TimeUnit -internal class CombinedRemoteConfigSource( +class CombinedRemoteConfigSource( private val store: RemoteConfigStore, private val httpSource: RemoteConfigSource, private val worker: BackgroundWorker, @@ -13,14 +13,12 @@ internal class CombinedRemoteConfigSource( ) { // the remote config that is used for the lifetime of the process. - private val cfg = store.loadConfig() + private val response by lazy { store.loadResponse() } - fun getConfig(): RemoteConfig? = cfg + fun getConfig(): RemoteConfig? = response?.cfg fun scheduleConfigRequests() { - worker.submit { - store.retrieveEtag()?.let(httpSource::setInitialEtag) - } + response?.etag?.let(httpSource::setInitialEtag) worker.scheduleWithFixedDelay( ::attemptConfigRequest, 0, @@ -30,6 +28,6 @@ internal class CombinedRemoteConfigSource( } private fun attemptConfigRequest() { - httpSource.getConfig()?.let(store::saveConfig) + httpSource.getConfig()?.let(store::saveResponse) } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/ConfigHttpResponse.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/ConfigHttpResponse.kt new file mode 100644 index 0000000000..a542c5774e --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/ConfigHttpResponse.kt @@ -0,0 +1,8 @@ +package io.embrace.android.embracesdk.internal.config.source + +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +data class ConfigHttpResponse( + val cfg: RemoteConfig?, + val etag: String?, +) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt index 6aa20e48f6..763fb68b53 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/OkHttpRemoteConfigSource.kt @@ -22,7 +22,7 @@ internal class OkHttpRemoteConfigSource( private val serializer: PlatformSerializer, ) : RemoteConfigSource { - override fun getConfig(): RemoteConfig? = try { + override fun getConfig(): ConfigHttpResponse? = try { fetchConfigImpl() } catch (exc: IOException) { null @@ -34,7 +34,7 @@ internal class OkHttpRemoteConfigSource( private var etag: String? = null - private fun fetchConfigImpl(): RemoteConfig? { + private fun fetchConfigImpl(): ConfigHttpResponse? { val request = prepareRequest() val call = okhttpClient.newCall(request) val response = call.execute() @@ -56,19 +56,20 @@ internal class OkHttpRemoteConfigSource( return request } - private fun processResponse(response: Response): RemoteConfig? { - response.header("ETag")?.let { + private fun processResponse(response: Response): ConfigHttpResponse? { + response.header("etag")?.let { this.etag = it } if (!response.isSuccessful) { return null } - return response.body?.source()?.use { src -> + val cfg = response.body?.source()?.use { src -> val gzipSource = GzipSource(src) gzipSource.buffer().inputStream().use { serializer.fromJson(it, RemoteConfig::class.java) } } + return ConfigHttpResponse(cfg, etag) } private fun prepareConfigRequest(url: String) = ApiRequest( diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt index ffe7bda09c..7655fff992 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSource.kt @@ -1,13 +1,11 @@ package io.embrace.android.embracesdk.internal.config.source -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig - interface RemoteConfigSource { /** * Gets the remotely delivered configuration that should apply to the app for the lifetime of this process, if any. */ - fun getConfig(): RemoteConfig? + fun getConfig(): ConfigHttpResponse? /** * Sets the initial ETag to use for the first request, if any. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt deleted file mode 100644 index 9a0f6c4d10..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/RemoteConfigSourceImpl.kt +++ /dev/null @@ -1,146 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.source - -import io.embrace.android.embracesdk.internal.clock.Clock -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessStateListener -import io.embrace.android.embracesdk.internal.worker.BackgroundWorker -import kotlin.math.min - -/** - * Loads configuration for the app from the Embrace API. - */ -class RemoteConfigSourceImpl( - private val clock: Clock, - private val backgroundWorker: BackgroundWorker, - private val foregroundAction: () -> Unit, -) : RemoteConfigSource, ProcessStateListener { - - private val lock = Any() - - var remoteConfigSource: CachedRemoteConfigSource? = null - set(value) { - field = value - loadConfigFromCache() - attemptConfigRefresh() - } - - override fun setInitialEtag(etag: String) { - // no-op - } - - @Volatile - private var configProp = RemoteConfig() - - @Volatile - var lastUpdated: Long = 0 - - @Volatile - private var lastRefreshConfigAttempt: Long = 0 - - @Volatile - private var configRetrySafeWindow = DEFAULT_RETRY_WAIT_TIME.toDouble() - - override fun getConfig(): RemoteConfig { - attemptConfigRefresh() - return configProp - } - - /** - * Load Config from cache if present. - */ - internal fun loadConfigFromCache() { - val cachedConfig = remoteConfigSource?.getCachedConfig() - val obj = cachedConfig?.remoteConfig - - if (obj != null) { - val oldConfig = configProp - updateConfig(oldConfig, obj) - } - } - - private fun attemptConfigRefresh() { - if (configRequiresRefresh() && configRetryIsSafe()) { - synchronized(lock) { - if (configRequiresRefresh() && configRetryIsSafe()) { - lastRefreshConfigAttempt = clock.now() - // Attempt to asynchronously update the config if it is out of date - refreshConfig() - } - } - } - } - - private fun refreshConfig() { - val previousConfig = configProp - backgroundWorker.submit { - // Ensure that another thread didn't refresh it already in the meantime - if (configRequiresRefresh()) { - try { - lastRefreshConfigAttempt = clock.now() - val newConfig = remoteConfigSource?.getConfig() - if (newConfig != null) { - updateConfig(previousConfig, newConfig) - lastUpdated = clock.now() - } - configRetrySafeWindow = DEFAULT_RETRY_WAIT_TIME.toDouble() - } catch (ex: Exception) { - configRetrySafeWindow = - min( - MAX_ALLOWED_RETRY_WAIT_TIME.toDouble(), - configRetrySafeWindow * 2 - ) - } - } - } - } - - private fun updateConfig(previousConfig: RemoteConfig, newConfig: RemoteConfig) { - val b = newConfig != previousConfig - if (b) { - configProp = newConfig - } - } - - override fun onForeground(coldStart: Boolean, timestamp: Long) { - // Refresh the config on resume if it has expired - getConfig() - foregroundAction() - } - - /** - * Checks if the time diff since the last fetch exceeds the - * [RemoteConfigSourceImpl.CONFIG_TTL] millis. - * - * @return if the config requires to be fetched from the remote server again or not. - */ - private fun configRequiresRefresh(): Boolean { - return clock.now() - lastUpdated > CONFIG_TTL - } - - /** - * Checks if the time diff since the last attempt is enough to try again. - * - * @return if the config can be fetched from the remote server again or not. - */ - private fun configRetryIsSafe(): Boolean { - return clock.now() > lastRefreshConfigAttempt + configRetrySafeWindow * 1000 - } - - private companion object { - - /** - * Config lives for 1 hour before attempting to retrieve again. - */ - private const val CONFIG_TTL = 60 * 60 * 1000L - - /** - * Config refresh default retry period. - */ - private const val DEFAULT_RETRY_WAIT_TIME: Long = 20 // 20 seconds - - /** - * Config max allowed refresh retry period. - */ - private const val MAX_ALLOWED_RETRY_WAIT_TIME: Long = 300 // 5 minutes - } -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt index 7326140eea..ebefffa727 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStore.kt @@ -1,6 +1,6 @@ package io.embrace.android.embracesdk.internal.config.store -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse /** * Interface for storing and loading the most recently received remote configuration. @@ -10,20 +10,10 @@ interface RemoteConfigStore { /** * Loads the most recent remote configuration, if any. */ - fun loadConfig(): RemoteConfig? + fun loadResponse(): ConfigHttpResponse? /** * Saves a new remote configuration, overwriting whatever was stored before. */ - fun saveConfig(config: RemoteConfig) - - /** - * Retrieves the most recently stored ETag, if any. - */ - fun retrieveEtag(): String? - - /** - * Stores the most recently received ETag - */ - fun storeEtag(etag: String) + fun saveResponse(response: ConfigHttpResponse) } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt index 3eeeef4a15..ee237f9b44 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.internal.config.store import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer import java.io.File import java.util.zip.GZIPInputStream @@ -11,6 +12,10 @@ internal class RemoteConfigStoreImpl( storageDir: File, ) : RemoteConfigStore { + init { + storageDir.mkdirs() + } + private val configFile = File(storageDir, "most_recent_response").apply { createNewFile() } @@ -19,38 +24,28 @@ internal class RemoteConfigStoreImpl( createNewFile() } - override fun loadConfig(): RemoteConfig? { + override fun loadResponse(): ConfigHttpResponse? { try { - GZIPInputStream(configFile.inputStream().buffered()).use { - return serializer.fromJson(it, RemoteConfig::class.java) + val cfg = GZIPInputStream(configFile.inputStream().buffered()).use { + serializer.fromJson(it, RemoteConfig::class.java) } + return ConfigHttpResponse( + cfg, + etagFile.readText().ifEmpty { + null + } + ) } catch (exc: Exception) { return null } } - override fun saveConfig(config: RemoteConfig) { + override fun saveResponse(response: ConfigHttpResponse) { try { GZIPOutputStream(configFile.outputStream().buffered()).use { stream -> - serializer.toJson(config, RemoteConfig::class.java, stream) - } - } catch (ignored: Exception) { - } - } - - override fun retrieveEtag(): String? { - return try { - etagFile.readText().ifEmpty { - null + serializer.toJson(response.cfg, RemoteConfig::class.java, stream) } - } catch (exc: Exception) { - null - } - } - - override fun storeEtag(etag: String) { - try { - etagFile.writeText(etag) + response.etag?.let(etagFile::writeText) } catch (ignored: Exception) { } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt index 129ae2ac8e..8e07e16e1c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt @@ -2,12 +2,16 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService +import io.embrace.android.embracesdk.internal.config.source.CombinedRemoteConfigSource import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore +import okhttp3.OkHttpClient interface ConfigModule { val configService: ConfigService + val combinedRemoteConfigSource: CombinedRemoteConfigSource? val remoteConfigSource: RemoteConfigSource? val remoteConfigStore: RemoteConfigStore val urlBuilder: ApiUrlBuilder? + val okHttpClient: OkHttpClient } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index f17881d70e..5d465b0343 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -5,14 +5,17 @@ import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.ConfigServiceImpl -import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource -import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSourceImpl +import io.embrace.android.embracesdk.internal.config.source.CombinedRemoteConfigSource +import io.embrace.android.embracesdk.internal.config.source.OkHttpRemoteConfigSource import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStoreImpl import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.internal.worker.Worker +import okhttp3.OkHttpClient +import okhttp3.Protocol import java.io.File +import java.util.concurrent.TimeUnit internal class ConfigModuleImpl( initModule: InitModule, @@ -21,10 +24,32 @@ internal class ConfigModuleImpl( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - foregroundAction: () -> Unit, - remoteConfigSourceProvider: Provider = { null }, + remoteConfigStoreProvider: Provider = { null }, ) : ConfigModule { + companion object { + private const val DEFAULT_CONNECTION_TIMEOUT_SECONDS = 10L + private const val DEFAULT_READ_TIMEOUT_SECONDS = 60L + } + + override val okHttpClient by singleton { + OkHttpClient() + .newBuilder() + .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)) + .connectTimeout(DEFAULT_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .readTimeout(DEFAULT_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .build() + } + + override val combinedRemoteConfigSource: CombinedRemoteConfigSource? by singleton { + configService.appId ?: return@singleton null + CombinedRemoteConfigSource( + store = remoteConfigStore, + httpSource = remoteConfigSource ?: return@singleton null, + worker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), + ) + } + override val configService: ConfigService by singleton { Systrace.traceSynchronous("config-service-init") { ConfigServiceImpl( @@ -32,22 +57,22 @@ internal class ConfigModuleImpl( preferencesService = androidServicesModule.preferencesService, suppliedFramework = framework, instrumentedConfig = initModule.instrumentedConfig, - configProvider = { remoteConfigSource?.getConfig() }, + configProvider = { combinedRemoteConfigSource?.getConfig() }, ) } } override val remoteConfigSource by singleton { - urlBuilder ?: return@singleton null - remoteConfigSourceProvider() ?: RemoteConfigSourceImpl( - clock = initModule.clock, - backgroundWorker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), - foregroundAction = foregroundAction, + val builder = urlBuilder ?: return@singleton null + OkHttpRemoteConfigSource( + okhttpClient = okHttpClient, + apiUrlBuilder = builder, + serializer = initModule.jsonSerializer, ) } override val remoteConfigStore: RemoteConfigStore by singleton { - RemoteConfigStoreImpl( + remoteConfigStoreProvider() ?: RemoteConfigStoreImpl( serializer = initModule.jsonSerializer, storageDir = File(coreModule.context.filesDir, "embrace_remote_config"), ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt index a26bdde08c..cec8ca8fb3 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt @@ -1,6 +1,6 @@ package io.embrace.android.embracesdk.internal.injection -import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource +import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.utils.Provider @@ -14,8 +14,7 @@ typealias ConfigModuleSupplier = ( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - foregroundAction: () -> Unit, - remoteConfigSourceProvider: Provider, + remoteConfigStoreProvider: Provider, ) -> ConfigModule fun createConfigModule( @@ -25,8 +24,7 @@ fun createConfigModule( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - foregroundAction: () -> Unit, - remoteConfigSourceProvider: Provider, + remoteConfigStoreProvider: Provider, ): ConfigModule = ConfigModuleImpl( initModule, coreModule, @@ -34,6 +32,5 @@ fun createConfigModule( workerThreadModule, androidServicesModule, framework, - foregroundAction, - remoteConfigSourceProvider + remoteConfigStoreProvider ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt index 5bbd842c96..e72a0691b1 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt @@ -148,6 +148,7 @@ internal class DeliveryModuleImpl( val lazyDeviceId = lazy(androidServicesModule.preferencesService::deviceIdentifier) if (configModule.configService.autoDataCaptureBehavior.shouldUseOkHttp()) { OkHttpRequestExecutionService( + configModule.okHttpClient, coreBaseUrl, lazyDeviceId, appId, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt index 7f6b819776..6f50cbc6b0 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt @@ -13,7 +13,6 @@ import io.embrace.android.embracesdk.internal.capture.user.EmbraceUserService import io.embrace.android.embracesdk.internal.capture.user.UserService import io.embrace.android.embracesdk.internal.comms.api.ApiClient import io.embrace.android.embracesdk.internal.comms.api.ApiClientImpl -import io.embrace.android.embracesdk.internal.comms.api.ApiRequest import io.embrace.android.embracesdk.internal.comms.api.ApiService import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiService import io.embrace.android.embracesdk.internal.comms.delivery.EmbracePendingApiCallsSender @@ -90,11 +89,6 @@ class EssentialServiceModuleImpl( EmbraceApiService( apiClient = apiClient, serializer = initModule.jsonSerializer, - cachedConfigProvider = { url: String, request: ApiRequest -> - Systrace.traceSynchronous("provide-cache-config") { - storageModule.cache.retrieveCachedConfig(url, request) - } - }, priorityWorker = workerThreadModule.priorityWorker(Worker.Priority.NetworkRequestWorker), pendingApiCallsSender = pendingApiCallsSender, lazyDeviceId = lazyDeviceId, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/StorageModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/StorageModule.kt index 6855f156ed..22232871e8 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/StorageModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/StorageModule.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.internal.injection -import io.embrace.android.embracesdk.internal.comms.api.ApiResponseCache import io.embrace.android.embracesdk.internal.comms.delivery.CacheService import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryCacheManager import io.embrace.android.embracesdk.internal.storage.StorageService @@ -10,7 +9,6 @@ import io.embrace.android.embracesdk.internal.storage.StorageService */ interface StorageModule { val storageService: StorageService - val cache: ApiResponseCache val cacheService: CacheService val deliveryCacheManager: DeliveryCacheManager } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/StorageModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/StorageModuleImpl.kt index 724007130e..9c6a336db8 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/StorageModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/StorageModuleImpl.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.internal.injection -import io.embrace.android.embracesdk.internal.comms.api.ApiResponseCache import io.embrace.android.embracesdk.internal.comms.delivery.CacheService import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryCacheManager import io.embrace.android.embracesdk.internal.comms.delivery.EmbraceCacheService @@ -25,13 +24,6 @@ internal class StorageModuleImpl( ) } - override val cache by singleton { - ApiResponseCache( - initModule.jsonSerializer, - storageService - ) - } - override val cacheService: CacheService by singleton { EmbraceCacheService( storageService, diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/CachedConfigTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/CachedConfigTest.kt deleted file mode 100644 index e7d6810714..0000000000 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/CachedConfigTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.embrace.android.embracesdk.internal.comms.api - -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test - -internal class CachedConfigTest { - - @Test - fun isValid() { - assertFalse(CachedConfig(null, null).isValid()) - assertFalse(CachedConfig(RemoteConfig(), null).isValid()) - assertFalse(CachedConfig(null, "ba09cc").isValid()) - assertTrue(CachedConfig(RemoteConfig(), "ba09cc").isValid()) - } -} diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt index 1333294c72..a8ecd510fa 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt @@ -1,17 +1,14 @@ package io.embrace.android.embracesdk.internal.comms.api -import io.embrace.android.embracesdk.ResourceReader import io.embrace.android.embracesdk.concurrency.BlockingScheduledExecutorService import io.embrace.android.embracesdk.fakes.FakeApiClient import io.embrace.android.embracesdk.fakes.FakeDeliveryCacheManager import io.embrace.android.embracesdk.fakes.FakePendingApiCallsSender import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.TypeUtils -import io.embrace.android.embracesdk.internal.comms.api.ApiClient.Companion.NO_HTTP_RESPONSE import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryCacheManager import io.embrace.android.embracesdk.internal.comms.delivery.NetworkStatus import io.embrace.android.embracesdk.internal.compression.ConditionalGzipOutputStream -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.payload.Attribute import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.Log @@ -25,7 +22,6 @@ import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNull -import org.junit.Assert.assertSame import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -43,7 +39,6 @@ internal class EmbraceApiServiceTest { private lateinit var fakeApiClient: FakeApiClient private lateinit var fakeCacheManager: DeliveryCacheManager private lateinit var testScheduledExecutor: BlockingScheduledExecutorService - private lateinit var cachedConfig: CachedConfig private lateinit var apiService: EmbraceApiService private lateinit var fakePendingApiCallsSender: FakePendingApiCallsSender @@ -55,9 +50,6 @@ internal class EmbraceApiServiceTest { instrumentedConfig = FakeInstrumentedConfig() ) fakeApiClient = FakeApiClient() - cachedConfig = CachedConfig( - remoteConfig = RemoteConfig() - ) testScheduledExecutor = BlockingScheduledExecutorService(blockingMode = false) fakeCacheManager = FakeDeliveryCacheManager() fakePendingApiCallsSender = FakePendingApiCallsSender() @@ -71,78 +63,6 @@ internal class EmbraceApiServiceTest { } } - @Test - fun `test getConfig returns correct values in Response`() { - fakeApiClient.queueResponse( - ApiResponse.Success( - headers = emptyMap(), - body = defaultConfigResponseBody - ) - ) - - val remoteConfig = apiService.getConfig() - - // verify a few fields were serialized correctly. - checkNotNull(remoteConfig) - assertTrue(checkNotNull(remoteConfig.sessionConfig?.isEnabled)) - assertEquals(100, remoteConfig.threshold) - } - - @Test(expected = IllegalStateException::class) - fun `getConfig throws an exception when receiving ApiResponse_Incomplete`() { - val incompleteResponse: ApiResponse.Incomplete = ApiResponse.Incomplete( - IllegalStateException("Connection failed") - ) - fakeApiClient.queueResponse(incompleteResponse) - apiService.getConfig() - } - - @Test - fun `cached remote config returned when 304 received`() { - fakeApiClient.queueResponse( - ApiResponse.NotModified - ) - assertEquals(cachedConfig.remoteConfig, apiService.getConfig()) - } - - @Test - fun `getConfig did not complete returns a null config`() { - fakeApiClient.queueResponse( - ApiResponse.Failure( - code = NO_HTTP_RESPONSE, - headers = emptyMap() - ) - ) - assertNull(apiService.getConfig()) - } - - @Test - fun `getConfig results in unexpected response code returns a null config`() { - fakeApiClient.queueResponse( - ApiResponse.Failure( - code = 400, - headers = emptyMap() - ) - ) - assertNull(apiService.getConfig()) - } - - @Test - fun testGetConfigWithMatchingEtag() { - val cfg = RemoteConfig() - cachedConfig = CachedConfig(cfg, "my_etag") - fakeApiClient.queueResponse( - ApiResponse.NotModified - ) - val remoteConfig = apiService.getConfig() - assertSame(cfg, remoteConfig) - } - - @Test - fun `getCacheConfig returns what the provider provides`() { - assertEquals(apiService.getCachedConfig(), cachedConfig) - } - @Test fun `send v2 session`() { fakeApiClient.queueResponse(successfulPostResponse) @@ -412,7 +332,6 @@ internal class EmbraceApiServiceTest { apiService = EmbraceApiService( apiClient = fakeApiClient, serializer = serializer, - cachedConfigProvider = { _, _ -> cachedConfig }, priorityWorker = PriorityWorker(testScheduledExecutor), pendingApiCallsSender = fakePendingApiCallsSender, lazyDeviceId = lazy { fakeDeviceId }, @@ -426,8 +345,6 @@ internal class EmbraceApiServiceTest { private const val fakeAppId = "abcde" private const val fakeDeviceId = "ajflkadsflkadslkfjds" private const val fakeAppVersionName = "6.1.0" - private val defaultConfigResponseBody = - ResourceReader.readResourceAsText("remote_config_response.json") private val successfulPostResponse = ApiResponse.Success( headers = emptyMap(), body = "" diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt index 57142107aa..1c74ed9d8f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt @@ -8,12 +8,8 @@ import io.embrace.android.embracesdk.fakes.FakeProcessStateService import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.config.FakeProjectConfig import io.embrace.android.embracesdk.internal.SystemInfo -import io.embrace.android.embracesdk.internal.comms.api.CachedConfig import io.embrace.android.embracesdk.internal.config.behavior.BehaviorThresholdCheck -import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.config.source.CachedRemoteConfigSource -import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSourceImpl import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl import io.embrace.android.embracesdk.internal.logs.LogSinkImpl @@ -43,14 +39,12 @@ internal class ConfigServiceImplTest { private lateinit var worker: BackgroundWorker private lateinit var executor: BlockingScheduledExecutorService private lateinit var thresholdCheck: BehaviorThresholdCheck - private lateinit var remoteConfigSource: RemoteConfigSourceImpl companion object { private lateinit var remoteConfig: RemoteConfig private lateinit var processStateService: ProcessStateService private lateinit var logger: EmbLogger private lateinit var fakeClock: FakeClock - private lateinit var fakeCachedConfig: RemoteConfig /** * Setup before all tests get executed. Create mocks here. @@ -63,9 +57,6 @@ internal class ConfigServiceImplTest { processStateService = FakeProcessStateService() fakeClock = FakeClock() logger = EmbLoggerImpl() - fakeCachedConfig = RemoteConfig( // alter config to trigger listener - anrConfig = AnrRemoteConfig() - ) } /** @@ -87,7 +78,7 @@ internal class ConfigServiceImplTest { fakePreferenceService = FakePreferenceService(deviceIdentifier = "07D85B44E4E245F4A30E559BFC0D07FF") executor = BlockingScheduledExecutorService(blockingMode = false) worker = BackgroundWorker(executor) - service = createService(worker = worker, action = {}) + service = createService() assertFalse(service.isOnlyUsingOtelExporters()) } @@ -160,41 +151,6 @@ internal class ConfigServiceImplTest { assertFalse(thresholdCheck.isBehaviorEnabled(-1000f)) } - @Test - fun `test config does not exist in cache, so it's not loaded`() { - assertTrue(service.anrBehavior.isAnrCaptureEnabled()) - remoteConfigSource.loadConfigFromCache() - - // config was not updated - assertTrue(service.anrBehavior.isAnrCaptureEnabled()) - } - - /** - * Test that calling getConfig() refreshes the config - * As we are using a DirectExecutor this method will run synchronously and - * return the updated config. - * In a real situation, the async refresh would be triggered and the config returned would be the previous one. - */ - @Test - fun `test onForeground() refreshes the config`() { - // advance the clock so it's safe to retry config refresh - fakeClock.tick(1000000000000) - val newConfig = RemoteConfig(anrConfig = AnrRemoteConfig()) - var count = 0 - - remoteConfigSource.remoteConfigSource = object : CachedRemoteConfigSource { - override fun getCachedConfig(): CachedConfig { - return CachedConfig(null, null) - } - override fun getConfig(): RemoteConfig { - count++ - return newConfig - } - } - remoteConfigSource.onForeground(true, 1100L) - assertEquals(1, count) - } - @Test fun `test app framework`() { assertEquals(AppFramework.NATIVE, service.appFramework) @@ -202,12 +158,12 @@ internal class ConfigServiceImplTest { @Test(expected = IllegalArgumentException::class) fun testEmptyAppId() { - createService(worker = worker, appId = null) + createService(appId = null) } @Test(expected = IllegalArgumentException::class) fun testNullAppId() { - createService(worker = worker, appId = null) + createService(appId = null) } @Test @@ -218,7 +174,7 @@ internal class ConfigServiceImplTest { SystemInfo() ) cfg.addLogExporter(FakeLogRecordExporter()) - val service = createService(worker = worker, config = cfg, appId = null) + val service = createService(config = cfg, appId = null) assertNotNull(service) assertTrue(service.isOnlyUsingOtelExporters()) } @@ -228,8 +184,6 @@ internal class ConfigServiceImplTest { * tasks for its internal [BackgroundWorker] */ private fun createService( - worker: BackgroundWorker, - action: () -> Unit = {}, config: OpenTelemetryConfiguration = OpenTelemetryConfiguration( SpanSinkImpl(), LogSinkImpl(), @@ -238,18 +192,13 @@ internal class ConfigServiceImplTest { appId: String? = "AbCdE", ): ConfigServiceImpl { thresholdCheck = BehaviorThresholdCheck { fakePreferenceService.deviceIdentifier } - remoteConfigSource = RemoteConfigSourceImpl( - clock = fakeClock, - backgroundWorker = worker, - foregroundAction = action, - ) return ConfigServiceImpl( openTelemetryCfg = config, preferencesService = fakePreferenceService, suppliedFramework = AppFramework.NATIVE, instrumentedConfig = FakeInstrumentedConfig(project = FakeProjectConfig(appId = appId)), thresholdCheck = thresholdCheck, - configProvider = remoteConfigSource::getConfig, + configProvider = ::remoteConfig ) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt index 434ef2622f..751f6ec9d2 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -116,7 +116,7 @@ class OkHttpRemoteConfigSourceTest { val (cfg, request) = executeRequest( MockResponse().setResponseCode(200) .setBody(configResponseBuffer) - .setHeader("ETag", etagValue) + .setHeader("etag", etagValue) ) assertConfigRequestReceived(request) assertNull(request?.getHeader("If-None-Match")) @@ -125,7 +125,7 @@ class OkHttpRemoteConfigSourceTest { // second request with etag val (secondCfg, secondRequest) = executeRequest( MockResponse().setResponseCode(304) - .setHeader("ETag", etagValue) + .setHeader("etag", etagValue) ) assertConfigRequestReceived(secondRequest) assertEquals(etagValue, secondRequest?.getHeader("If-None-Match")) @@ -137,7 +137,7 @@ class OkHttpRemoteConfigSourceTest { return Pair(null, null) } server.enqueue(response) - val cfg = source.getConfig() + val cfg = source.getConfig()?.cfg val request = pollRequest() CallData(request, cfg) return Pair(cfg, request) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt index daef23297f..5d055b94cf 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt @@ -22,7 +22,7 @@ class CombinedRemoteConfigSourceTest { fun setUp() { remoteConfig = RemoteConfig(92) executorService = BlockingScheduledExecutorService() - remoteConfigSource = FakeRemoteConfigSource(remoteConfig) + remoteConfigSource = FakeRemoteConfigSource(ConfigHttpResponse(remoteConfig, "another")) remoteConfigStore = FakeRemoteConfigStore() source = CombinedRemoteConfigSource( remoteConfigStore, @@ -40,7 +40,7 @@ class CombinedRemoteConfigSourceTest { fun `test initial config populated`() { val cfg = RemoteConfig(100) source = CombinedRemoteConfigSource( - FakeRemoteConfigStore(cfg), + FakeRemoteConfigStore(ConfigHttpResponse(cfg, null)), remoteConfigSource, BackgroundWorker(executorService) ) @@ -58,11 +58,11 @@ class CombinedRemoteConfigSourceTest { @Test fun `test persisted etag value populated`() { - remoteConfigStore.etag = "etag" + remoteConfigStore.impl = ConfigHttpResponse(RemoteConfig(), "etag") assertEquals(0, remoteConfigSource.callCount) source.scheduleConfigRequests() + assertEquals("etag", remoteConfigSource.etag) executorService.runCurrentlyBlocked() assertEquals(1, remoteConfigSource.callCount) - assertEquals("etag", remoteConfigSource.etag) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt index f29ebb3392..a99a263e06 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.config.store import io.embrace.android.embracesdk.fakes.TestPlatformSerializer import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Before @@ -22,30 +23,22 @@ internal class RemoteConfigStoreImplTest { @Test fun `test config store`() { - assertNull(store.loadConfig()) + assertNull(store.loadResponse()) // store a config val config = RemoteConfig(50) - store.saveConfig(config) + store.saveResponse(ConfigHttpResponse(config, "etag")) // load the config - val loaded = checkNotNull(store.loadConfig()) - assertEquals(config, loaded) + val loaded = checkNotNull(store.loadResponse()) + assertEquals(config, loaded.cfg) + assertEquals("etag", loaded.etag) val newConfig = RemoteConfig(100) - store.saveConfig(newConfig) + store.saveResponse(ConfigHttpResponse(newConfig, "another")) - val newLoaded = checkNotNull(store.loadConfig()) - assertEquals(newConfig, newLoaded) - } - - @Test - fun `test etag store`() { - assertNull(store.retrieveEtag()) - store.storeEtag("etag") - assertEquals("etag", store.retrieveEtag()) - - store.storeEtag("another") - assertEquals("another", store.retrieveEtag()) + val newLoaded = checkNotNull(store.loadResponse()) + assertEquals(newConfig, newLoaded.cfg) + assertEquals("another", newLoaded.etag) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt index 51c460d39d..241b342706 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt @@ -24,7 +24,6 @@ internal class ConfigModuleImplTest { workerThreadModule = FakeWorkerThreadModule(), androidServicesModule = FakeAndroidServicesModule(), framework = AppFramework.NATIVE, - foregroundAction = {}, ) assertNotNull(module.configService) assertNotNull(module.urlBuilder) diff --git a/embrace-android-core/src/test/resources/remote_config_response.json b/embrace-android-core/src/test/resources/remote_config_response.json deleted file mode 100644 index 38f601e5d4..0000000000 --- a/embrace-android-core/src/test/resources/remote_config_response.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "ls": 100, - "event_limits": {}, - "offset": 0, - "personas": [], - "session_control": { - "enable": true, - "async_end": false - }, - "threshold": 100, - "ui": { - "views": 100 - }, - "urlconnection_request_enabled": true -} \ No newline at end of file diff --git a/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt b/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt index 2719d154bf..019a7bc68b 100644 --- a/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt +++ b/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt @@ -9,7 +9,6 @@ import io.embrace.android.embracesdk.internal.logging.InternalErrorType import okhttp3.Headers.Companion.toHeaders import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient -import okhttp3.Protocol import okhttp3.Request import okhttp3.RequestBody import okio.BufferedSink @@ -17,25 +16,16 @@ import okio.buffer import okio.source import java.io.IOException import java.io.InputStream -import java.util.concurrent.TimeUnit class OkHttpRequestExecutionService( + private val okHttpClient: OkHttpClient, private val coreBaseUrl: String, private val lazyDeviceId: Lazy, private val appId: String, private val embraceVersionName: String, private val logger: EmbLogger, - connectionTimeoutSeconds: Long = DEFAULT_CONNECTION_TIMEOUT_SECONDS, - readTimeoutSeconds: Long = DEFAULT_READ_TIMEOUT_SECONDS, ) : RequestExecutionService { - private val okHttpClient = OkHttpClient() - .newBuilder() - .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)) - .connectTimeout(connectionTimeoutSeconds, TimeUnit.SECONDS) - .readTimeout(readTimeoutSeconds, TimeUnit.SECONDS) - .build() - override fun attemptHttpRequest( payloadStream: () -> InputStream, envelopeType: SupportedEnvelopeType, @@ -87,8 +77,6 @@ class OkHttpRequestExecutionService( ) private companion object { - const val DEFAULT_CONNECTION_TIMEOUT_SECONDS = 10L - const val DEFAULT_READ_TIMEOUT_SECONDS = 60L private val mediaType = "application/json".toMediaType() class ApiRequestBody( diff --git a/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt b/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt index f9666ba45d..9d48c50730 100644 --- a/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt +++ b/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt @@ -4,6 +4,7 @@ import io.embrace.android.embracesdk.fakes.FakeEmbLogger import io.embrace.android.embracesdk.internal.delivery.PayloadType import io.embrace.android.embracesdk.internal.delivery.SupportedEnvelopeType import okhttp3.Headers.Companion.toHeaders +import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer @@ -15,12 +16,14 @@ import org.junit.Before import org.junit.Test import java.net.SocketTimeoutException import java.net.UnknownHostException +import java.util.concurrent.TimeUnit class OkHttpRequestExecutionServiceTest { private lateinit var requestExecutionService: OkHttpRequestExecutionService private lateinit var server: MockWebServer private lateinit var testServerUrl: String private lateinit var logger: FakeEmbLogger + private lateinit var client: OkHttpClient private val testAppId = "test_app_id" private val testDeviceId = "test_device_id" @@ -49,14 +52,19 @@ class OkHttpRequestExecutionServiceTest { start() } testServerUrl = server.url("").toString() + client = OkHttpClient() + .newBuilder() + .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)) + .connectTimeout(1, TimeUnit.SECONDS) + .readTimeout(1, TimeUnit.SECONDS) + .build() requestExecutionService = OkHttpRequestExecutionService( + okHttpClient = client, coreBaseUrl = testServerUrl, lazyDeviceId = lazy { testDeviceId }, appId = testAppId, embraceVersionName = testEmbraceVersionName, logger = logger, - connectionTimeoutSeconds = 1L, - readTimeoutSeconds = 1L, ) } @@ -69,6 +77,7 @@ class OkHttpRequestExecutionServiceTest { fun `return incomplete if the server does not exist`() { // given a request execution service with a non existent url requestExecutionService = OkHttpRequestExecutionService( + okHttpClient = client, coreBaseUrl = "https://nonexistenturl:1565/", lazyDeviceId = lazy { testDeviceId }, appId = testAppId, diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/StorageModuleImplTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/StorageModuleImplTest.kt index 0c73fd81e3..dbb6bcd8c9 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/StorageModuleImplTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/StorageModuleImplTest.kt @@ -22,7 +22,6 @@ internal class StorageModuleImplTest { ) assertNotNull(module.storageService) - assertNotNull(module.cache) assertNotNull(module.cacheService) assertNotNull(module.deliveryCacheManager) assertNotNull(module.storageService) diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/CachedConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/CachedConfig.kt deleted file mode 100644 index bb838e1c6f..0000000000 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/CachedConfig.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.embrace.android.embracesdk.internal.comms.api - -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig - -class CachedConfig( - val remoteConfig: RemoteConfig? = null, - val eTag: String? = null, -) { - fun isValid(): Boolean = remoteConfig != null && eTag != null -} diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt index e75cc7d11f..87a5cef028 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt @@ -144,7 +144,11 @@ internal class IntegrationTestRule( if (startSdk) { embraceImpl.start(overriddenCoreModule.context) - assertEquals(expectSdkToStart, bootstrapper.essentialServiceModule.processStateService.isInitialized()) + assertEquals( + "SDK did not start in integration test.", + expectSdkToStart, + bootstrapper.essentialServiceModule.processStateService.isInitialized() + ) } } testCaseAction(action) @@ -153,17 +157,12 @@ internal class IntegrationTestRule( otelExportAssertion(otelAssertion) } - fun prepareConfig(instrumentedConfig: FakeInstrumentedConfig) = + private fun prepareConfig(instrumentedConfig: FakeInstrumentedConfig) = when { - setup.useMockWebServer -> { - instrumentedConfig.copy( - baseUrls = FakeBaseUrlConfig(configImpl = baseUrl, dataImpl = baseUrl) - ) - } - - else -> { - instrumentedConfig - } + setup.useMockWebServer -> instrumentedConfig.copy( + baseUrls = FakeBaseUrlConfig(configImpl = baseUrl, dataImpl = baseUrl) + ) + else -> instrumentedConfig } /** diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index c241db45b5..7c5666eb2b 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -8,6 +8,7 @@ import io.embrace.android.embracesdk.fakes.FakeEmbLogger import io.embrace.android.embracesdk.fakes.FakeNativeFeatureModule import io.embrace.android.embracesdk.fakes.FakeNetworkConnectivityService import io.embrace.android.embracesdk.fakes.FakeRemoteConfigSource +import io.embrace.android.embracesdk.fakes.FakeRemoteConfigStore import io.embrace.android.embracesdk.fakes.FakeRequestExecutionService import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.injection.FakeAnrModule @@ -16,6 +17,7 @@ import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeNativeCoreModule import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryService import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse import io.embrace.android.embracesdk.internal.delivery.execution.RequestExecutionService import io.embrace.android.embracesdk.internal.delivery.storage.PayloadStorageService import io.embrace.android.embracesdk.internal.injection.AndroidServicesModule @@ -101,7 +103,7 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( deliveryServiceProvider = deliveryServiceProvider ) }, - configModuleSupplier = { initModule, coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, foregroundAction, _ -> + configModuleSupplier = { initModule, coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, _ -> createConfigModule( initModule, coreModule, @@ -109,9 +111,8 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( workerThreadModule, androidServicesModule, appFramework, - foregroundAction ) { - FakeRemoteConfigSource(remoteConfig) + FakeRemoteConfigStore(ConfigHttpResponse(remoteConfig, null)) } }, anrModuleSupplier = { _, _, _ -> fakeAnrModule }, diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index 7edfd8fc1c..1768f6b03c 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -1,10 +1,8 @@ package io.embrace.android.embracesdk.internal.injection import android.content.Context -import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.capture.envelope.session.OtelPayloadMapperImpl -import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSourceImpl import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl import io.embrace.android.embracesdk.internal.logging.InternalErrorType @@ -146,17 +144,18 @@ internal class ModuleInitBootstrapper( openTelemetryModule, workerThreadModule, androidServicesModule, - appFramework, - { - if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { - EmbraceInternalApi.getInstance().internalInterface.stopSdk() - } - }, - { null } - ) + appFramework + ) { null } + // FIXME: need to disable SDK properly. +// { +// if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { +// EmbraceInternalApi.getInstance().internalInterface.stopSdk() +// } +// }, } val serviceRegistry = coreModule.serviceRegistry postInit(ConfigModule::class) { + configModule.combinedRemoteConfigSource?.scheduleConfigRequests() serviceRegistry.registerService(lazy { configModule.configService }) serviceRegistry.registerService(lazy { configModule.remoteConfigSource }) openTelemetryModule.setupSensitiveKeysBehavior(configModule.configService.sensitiveKeysBehavior) @@ -185,11 +184,7 @@ internal class ModuleInitBootstrapper( ) } postInit(EssentialServiceModule::class) { - // Allow config service to start making HTTP requests with(essentialServiceModule) { - (configModule.remoteConfigSource as? RemoteConfigSourceImpl)?.remoteConfigSource = - apiService - serviceRegistry.registerServices( lazy { essentialServiceModule.processStateService }, lazy { activityLifecycleTracker }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index e400e4f227..13e234ddab 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -43,7 +43,7 @@ internal fun fakeModuleInitBootstrapper( workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, - configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _, _ -> FakeConfigModule() }, + configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt index 2b07fd0fca..855214923e 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt @@ -32,7 +32,7 @@ internal class ModuleInitBootstrapperTest { logger = EmbLoggerImpl() coreModule = FakeCoreModule() moduleInitBootstrapper = ModuleInitBootstrapper( - configModuleSupplier = { _, _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, + configModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, coreModuleSupplier = { _, _ -> coreModule }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> FakeNativeFeatureModule() }, logger = logger diff --git a/embrace-test-fakes/build.gradle.kts b/embrace-test-fakes/build.gradle.kts index 592b9536f3..a154a20657 100644 --- a/embrace-test-fakes/build.gradle.kts +++ b/embrace-test-fakes/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { compileOnly(libs.opentelemetry.semconv.incubating) implementation(libs.junit) + implementation(libs.okhttp) implementation(libs.robolectric) implementation(libs.lifecycle.runtime) } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiService.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiService.kt index f6b8f60bc7..586eb34134 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiService.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiService.kt @@ -2,9 +2,7 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.internal.comms.api.ApiResponse import io.embrace.android.embracesdk.internal.comms.api.ApiService -import io.embrace.android.embracesdk.internal.comms.api.CachedConfig import io.embrace.android.embracesdk.internal.comms.delivery.NetworkStatus -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.injection.SerializationAction import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.LogPayload @@ -27,14 +25,6 @@ class FakeApiService : ApiService { val sessionRequests: MutableList> = mutableListOf() var futureGetCount: Int = 0 - override fun getConfig(): RemoteConfig? { - TODO("Not yet implemented") - } - - override fun getCachedConfig(): CachedConfig { - TODO("Not yet implemented") - } - override fun sendLogEnvelope(logEnvelope: Envelope) { sentLogPayloads.add(logEnvelope.data) } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt index 9cad37aa53..6b5a0795b3 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt @@ -3,8 +3,10 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkBehavior import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService +import io.embrace.android.embracesdk.internal.config.source.CombinedRemoteConfigSource import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource import io.embrace.android.embracesdk.internal.injection.ConfigModule +import okhttp3.OkHttpClient class FakeConfigModule( override val configService: ConfigService = FakeConfigService( @@ -12,7 +14,10 @@ class FakeConfigModule( captureHttpUrlConnectionRequests = false ) ), + override val combinedRemoteConfigSource: CombinedRemoteConfigSource? = null, + override val remoteConfigSource: RemoteConfigSource = FakeRemoteConfigSource(), override val remoteConfigStore: FakeRemoteConfigStore = FakeRemoteConfigStore(), override val urlBuilder: ApiUrlBuilder = FakeApiUrlBuilder(), + override val okHttpClient: OkHttpClient = OkHttpClient(), ) : ConfigModule diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt index a22940471c..ce31a67b61 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigSource.kt @@ -1,16 +1,16 @@ package io.embrace.android.embracesdk.fakes -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse import io.embrace.android.embracesdk.internal.config.source.RemoteConfigSource class FakeRemoteConfigSource( - var cfg: RemoteConfig? = null + var cfg: ConfigHttpResponse? = null ) : RemoteConfigSource { var callCount: Int = 0 var etag: String? = null - override fun getConfig(): RemoteConfig? { + override fun getConfig(): ConfigHttpResponse? { callCount++ return cfg } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt index 597acd0033..250dbbee3d 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt @@ -1,21 +1,15 @@ package io.embrace.android.embracesdk.fakes -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore class FakeRemoteConfigStore( - val impl: RemoteConfig? = null, - var etag: String? = null, + var impl: ConfigHttpResponse? = null, ) : RemoteConfigStore { - override fun loadConfig(): RemoteConfig? = impl + override fun loadResponse(): ConfigHttpResponse? = impl - override fun saveConfig(config: RemoteConfig) { - } - - override fun retrieveEtag(): String? = etag - - override fun storeEtag(etag: String) { - this.etag = etag + override fun saveResponse(response: ConfigHttpResponse) { + impl = response } } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeStorageModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeStorageModule.kt index b2434af92b..1880c133b8 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeStorageModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeStorageModule.kt @@ -3,7 +3,6 @@ package io.embrace.android.embracesdk.fakes.injection import io.embrace.android.embracesdk.fakes.FakeCacheService import io.embrace.android.embracesdk.fakes.FakeDeliveryCacheManager import io.embrace.android.embracesdk.fakes.FakeStorageService -import io.embrace.android.embracesdk.internal.comms.api.ApiResponseCache import io.embrace.android.embracesdk.internal.comms.delivery.CacheService import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryCacheManager import io.embrace.android.embracesdk.internal.injection.StorageModule @@ -13,8 +12,4 @@ class FakeStorageModule( override val cacheService: CacheService = FakeCacheService(), override val deliveryCacheManager: DeliveryCacheManager = FakeDeliveryCacheManager(), override val storageService: StorageService = FakeStorageService(), -) : StorageModule { - - override val cache: ApiResponseCache - get() = throw UnsupportedOperationException() -} +) : StorageModule From 0aaaa0c265a1a828fd036b3540bbbcd18bb82c58 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Tue, 12 Nov 2024 17:05:45 +0000 Subject: [PATCH 17/28] poc: disable sdk earlier in lifecycle --- .../testframework/IntegrationTestRule.kt | 2 +- .../embrace/android/embracesdk/EmbraceImpl.kt | 11 ++++++----- .../injection/ModuleInitBootstrapper.kt | 18 ++++++++++-------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt index 87a5cef028..9f55b8ae71 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt @@ -147,7 +147,7 @@ internal class IntegrationTestRule( assertEquals( "SDK did not start in integration test.", expectSdkToStart, - bootstrapper.essentialServiceModule.processStateService.isInitialized() + embraceImpl.isStarted ) } } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt index ab35d7d16c..b903312570 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt @@ -146,17 +146,18 @@ internal class EmbraceImpl @JvmOverloads constructor( val startTimeMs = sdkClock.now() val appFramework = fromFramework(framework) - bootstrapper.init(context, appFramework, startTimeMs) + if (!bootstrapper.init(context, appFramework, startTimeMs)) { + if (bootstrapper.configModule.configService.sdkModeBehavior.isSdkDisabled()) { + stop() + } + return + } startSynchronous("post-services-setup") val coreModule = bootstrapper.coreModule application = coreModule.application val configModule = bootstrapper.configModule - if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { - stop() - return - } if (configModule.configService.autoDataCaptureBehavior.isComposeClickCaptureEnabled()) { registerComposeActivityListener(coreModule.application) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index 1768f6b03c..a471968a8b 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -146,16 +146,19 @@ internal class ModuleInitBootstrapper( androidServicesModule, appFramework ) { null } - // FIXME: need to disable SDK properly. -// { -// if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { -// EmbraceInternalApi.getInstance().internalInterface.stopSdk() -// } -// }, } + + Systrace.traceSynchronous("sdk-disable-check") { + // kick off config HTTP request first so the SDK can't get in a permanently disabled state + configModule.combinedRemoteConfigSource?.scheduleConfigRequests() + + if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { + return false + } + } + val serviceRegistry = coreModule.serviceRegistry postInit(ConfigModule::class) { - configModule.combinedRemoteConfigSource?.scheduleConfigRequests() serviceRegistry.registerService(lazy { configModule.configService }) serviceRegistry.registerService(lazy { configModule.remoteConfigSource }) openTelemetryModule.setupSensitiveKeysBehavior(configModule.configService.sensitiveKeysBehavior) @@ -453,7 +456,6 @@ internal class ModuleInitBootstrapper( if (isInitialized()) { coreModule.serviceRegistry.close() workerThreadModule.close() - essentialServiceModule.processStateService.close() initialized.set(false) } } From 82a73da786077e03b88cee882d42265197e1ae48 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Fri, 8 Nov 2024 16:57:01 +0000 Subject: [PATCH 18/28] wip: okhttp implementation --- embrace-android-core/build.gradle.kts | 1 + .../internal/comms/api/ApiUrlBuilder.kt | 10 ++ .../comms/api/EmbraceApiUrlBuilder.kt | 9 +- .../config/OkHttpRemoteConfigSource.kt | 57 +++++++ .../injection/EssentialServiceModuleImpl.kt | 2 +- .../comms/api/EmbraceApiServiceTest.kt | 2 +- .../comms/api/EmbraceApiUrlBuilderTest.kt | 2 +- .../config/OkHttpRemoteConfigSourceTest.kt | 156 ++++++++++++++++++ .../embracesdk/fakes/FakeApiUrlBuilder.kt | 18 +- 9 files changed, 243 insertions(+), 14 deletions(-) create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt create mode 100644 embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt diff --git a/embrace-android-core/build.gradle.kts b/embrace-android-core/build.gradle.kts index 42b5da4d67..80a89bba67 100644 --- a/embrace-android-core/build.gradle.kts +++ b/embrace-android-core/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { compileOnly(libs.opentelemetry.semconv.incubating) implementation(libs.lifecycle.runtime) implementation(libs.lifecycle.process) + implementation(libs.okhttp) testImplementation(platform(libs.opentelemetry.bom)) testImplementation(libs.opentelemetry.api) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt index 3425794a53..cf55926384 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt @@ -5,6 +5,16 @@ package io.embrace.android.embracesdk.internal.comms.api */ interface ApiUrlBuilder { + /** + * The App ID that will be used in API requests. + */ + val appId: String + + /** + * The Device ID that will be used in API requests. + */ + val deviceId: String + /** * Returns the url used to fetch the config. */ diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt index 1fded563a5..2e6666f84a 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt @@ -5,21 +5,24 @@ import android.os.Build internal class EmbraceApiUrlBuilder( private val coreBaseUrl: String, private val configBaseUrl: String, - private val appId: String, - private val lazyDeviceId: Lazy, + override val appId: String, + deviceIdImpl: Lazy, private val lazyAppVersionName: Lazy, ) : ApiUrlBuilder { + companion object { private const val CONFIG_API_VERSION = 2 } + override val deviceId: String by deviceIdImpl + private fun getConfigBaseUrl() = "$configBaseUrl/v$CONFIG_API_VERSION/${"config"}" private fun getOperatingSystemCode() = Build.VERSION.SDK_INT.toString() + ".0.0" override fun getConfigUrl(): String { return "${getConfigBaseUrl()}?appId=$appId&osVersion=${getOperatingSystemCode()}" + - "&appVersion=${lazyAppVersionName.value}&deviceId=${lazyDeviceId.value}" + "&appVersion=${lazyAppVersionName.value}&deviceId=$deviceId" } override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String { diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt new file mode 100644 index 0000000000..580045de67 --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt @@ -0,0 +1,57 @@ +package io.embrace.android.embracesdk.internal.config + +import io.embrace.android.embracesdk.core.BuildConfig +import io.embrace.android.embracesdk.internal.comms.api.ApiRequest +import io.embrace.android.embracesdk.internal.comms.api.ApiRequestUrl +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder +import io.embrace.android.embracesdk.internal.comms.api.getHeaders +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer +import io.embrace.android.embracesdk.network.http.HttpMethod +import okhttp3.OkHttpClient +import okhttp3.Request +import java.io.IOException + +internal class OkHttpRemoteConfigSource( + private val okhttpClient: OkHttpClient, + private val apiUrlBuilder: ApiUrlBuilder, + private val serializer: PlatformSerializer, +) : RemoteConfigSource { + + override fun getConfig(): RemoteConfig? = try { + fetchConfigImpl() + } catch (exc: IOException) { + null + } + + private fun fetchConfigImpl(): RemoteConfig? { + val url = apiUrlBuilder.getConfigUrl() + val headers = prepareConfigRequest(url).getHeaders() + val builder = Request.Builder().url(url) + + headers.forEach { entry -> + builder.header(entry.key, entry.value) + } + val call = okhttpClient.newCall( + builder.build() + ) + + val response = call.execute() + if (!response.isSuccessful) { + return null + } + val config = response.body?.source()?.inputStream()?.buffered()?.use { + serializer.fromJson(it, RemoteConfig::class.java) + } + return config + } + + private fun prepareConfigRequest(url: String) = ApiRequest( + userAgent = "Embrace/a/" + BuildConfig.VERSION_NAME, + url = ApiRequestUrl(url), + httpMethod = HttpMethod.GET, + acceptEncoding = "gzip", + appId = apiUrlBuilder.appId, + deviceId = apiUrlBuilder.deviceId, + ) +} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt index f3aa4aec4a..230a46af9d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt @@ -69,7 +69,7 @@ class EssentialServiceModuleImpl( coreBaseUrl = coreBaseUrl, configBaseUrl = configBaseUrl, appId = appId, - lazyDeviceId = lazyDeviceId, + deviceIdImpl = lazyDeviceId, lazyAppVersionName = lazy { coreModule.packageVersionInfo.versionName } ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt index db8ca53ac9..5af2be9c7f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt @@ -52,7 +52,7 @@ internal class EmbraceApiServiceTest { coreBaseUrl = "https://a-$fakeAppId.data.emb-api.com", configBaseUrl = "https://a-$fakeAppId.config.emb-api.com", appId = fakeAppId, - lazyDeviceId = lazy { fakeDeviceId }, + deviceIdImpl = lazy { fakeDeviceId }, lazyAppVersionName = lazy { fakeAppVersionName } ) fakeApiClient = FakeApiClient() diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt index 5c33f75f9e..77470af413 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt @@ -22,7 +22,7 @@ internal class EmbraceApiUrlBuilderTest { coreBaseUrl = baseUrlLocalConfig.getData(APP_ID), configBaseUrl = baseUrlLocalConfig.getConfig(APP_ID), appId = APP_ID, - lazyDeviceId = lazy { DEVICE_ID }, + deviceIdImpl = lazy { DEVICE_ID }, lazyAppVersionName = lazy { APP_VERSION_NAME }, ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt new file mode 100644 index 0000000000..863b3a77a8 --- /dev/null +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -0,0 +1,156 @@ +package io.embrace.android.embracesdk.internal.config + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.fakes.TestPlatformSerializer +import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import okhttp3.OkHttpClient +import okhttp3.Protocol +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import okhttp3.mockwebserver.RecordedRequest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import java.util.concurrent.TimeUnit + +@RunWith(AndroidJUnit4::class) +class OkHttpRemoteConfigSourceTest { + + private lateinit var server: MockWebServer + private lateinit var client: OkHttpClient + private lateinit var urlBuilder: EmbraceApiUrlBuilder + + private val remoteConfig = RemoteConfig( + backgroundActivityConfig = BackgroundActivityRemoteConfig(100f) + ) + private val configResponse = TestPlatformSerializer().toJson(remoteConfig) + + @Before + fun setUp() { + server = MockWebServer().apply { + protocols = listOf(Protocol.HTTP_1_1, Protocol.HTTP_2) + start() + } + val baseUrl = server.url("api").toString() + client = OkHttpClient.Builder().build() + urlBuilder = EmbraceApiUrlBuilder( + coreBaseUrl = baseUrl, + configBaseUrl = baseUrl, + appId = "abcde", + deviceIdImpl = lazy { "deviceId" }, + lazyAppVersionName = lazy { "1.0.0" }, + ) + } + + @Test + fun `test config 2xx`() { + val (cfg, request) = executeRequest( + MockResponse().setResponseCode(200).setBody(configResponse) + ) + assertConfigRequestReceived(request) + assertConfigResponseDeserialized(cfg) + } + + @Test + fun `test config 4xx`() { + val (cfg, request) = executeRequest( + MockResponse().setResponseCode(400) + ) + assertConfigRequestReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + @Test + fun `test config 5xx`() { + val (cfg, request) = executeRequest( + MockResponse().setResponseCode(500) + ) + assertConfigRequestReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + @Test + fun `test no response from server`() { + client = client.newBuilder().callTimeout(1, TimeUnit.MILLISECONDS).build() + val (cfg, request) = executeRequest(null) + assertConfigRequestNotReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + @Test + fun `test call timeout in middle of server response`() { + client = client.newBuilder().callTimeout(5, TimeUnit.MILLISECONDS).build() + val (cfg, request) = executeRequest( + MockResponse() + .setResponseCode(200) + .setBody(configResponse) + .throttleBody(1, 1, TimeUnit.MILLISECONDS) + ) + assertConfigRequestReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + @Test + fun `test invalid response from server`() { + val (cfg, request) = executeRequest( + MockResponse().setResponseCode(200).setBody("{") + ) + assertConfigRequestReceived(request) + assertConfigResponseNotDeserialized(cfg) + } + + private fun executeRequest(response: MockResponse?): Pair { + if (response == null) { + return Pair(null, null) + } + server.enqueue(response) + val source = OkHttpRemoteConfigSource(client, urlBuilder, TestPlatformSerializer()) + val cfg = source.getConfig() + val request = pollRequest() + CallData(request, cfg) + return Pair(cfg, request) + } + + private fun assertEmbraceHeadersAdded(request: RecordedRequest?) { + val headers = request?.headers?.toMap() ?: error("Request headers cannot be null") + assertEquals("application/json", headers["Accept"]) + assertEquals("application/json", headers["Content-Type"]) + assertEquals("gzip", headers["Accept-Encoding"]) + assertEquals("abcde", headers["X-EM-AID"]) + assertEquals("deviceId", headers["X-EM-DID"]) + } + + private fun assertConfigRequestNotReceived(request: RecordedRequest?) { + assertNull(request) + } + + private fun assertConfigRequestReceived(request: RecordedRequest?) { + assertNotNull(request) + assertEmbraceHeadersAdded(request) + val requestUrl = request?.requestUrl?.toUrl() ?: error("Request URL cannot be null") + assertEquals("/api/v2/config", requestUrl.path) + assertEquals("appId=abcde&osVersion=21.0.0&appVersion=1.0.0&deviceId=deviceId", requestUrl.query) + } + + private fun assertConfigResponseDeserialized(cfg: RemoteConfig?) { + checkNotNull(cfg) + assertEquals(remoteConfig.backgroundActivityConfig, cfg.backgroundActivityConfig) + } + + private fun assertConfigResponseNotDeserialized(cfg: RemoteConfig?) { + assertNull(cfg) + } + + private data class CallData( + val request: RecordedRequest?, + val deserializedConfig: RemoteConfig?, + ) + + private fun pollRequest(): RecordedRequest? = server.takeRequest(1, TimeUnit.SECONDS) +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt index 9de47a61ce..821eb6676d 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt @@ -2,12 +2,14 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder -class FakeApiUrlBuilder : ApiUrlBuilder { - override fun getConfigUrl(): String { - return "" - } - - override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String { - return "" - } +class FakeApiUrlBuilder( + private val config: String = "", + private val other: String = "", + override val appId: String = "", + override val deviceId: String = "", +) : ApiUrlBuilder { + + override fun getConfigUrl(): String = config + + override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String = other } From bc28460533fa6a2cbc1622c97e8a0f2f5e52ceda Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Mon, 11 Nov 2024 14:43:02 +0000 Subject: [PATCH 19/28] refactor: reorganise initial bootstrapping --- .../config/detekt/baseline.xml | 2 +- .../injection/AndroidServicesModuleImpl.kt | 10 +-- .../AndroidServicesModuleSupplier.kt | 4 +- .../internal/injection/DeliveryModuleImpl.kt | 62 +++++++++++------ .../injection/DeliveryModuleSupplier.kt | 19 +++--- .../prefs/EmbracePreferencesService.kt | 34 +--------- .../session/EmbraceSessionPropertiesTest.kt | 6 +- .../AndroidServicesModuleImplTest.kt | 6 +- .../injection/DeliveryModuleImplTest.kt | 6 +- .../prefs/EmbracePreferencesServiceTest.kt | 8 +-- .../actions/EmbraceSetupInterface.kt | 37 ++++++----- .../injection/ModuleInitBootstrapper.kt | 66 ++++--------------- .../fakes/FakeModuleInitBootstrapper.kt | 4 +- 13 files changed, 106 insertions(+), 158 deletions(-) diff --git a/embrace-android-core/config/detekt/baseline.xml b/embrace-android-core/config/detekt/baseline.xml index f7162bde92..5b94df8f86 100644 --- a/embrace-android-core/config/detekt/baseline.xml +++ b/embrace-android-core/config/detekt/baseline.xml @@ -2,6 +2,6 @@ - LongParameterList:DeliveryModuleSupplier.kt$( configModule: ConfigModule, initModule: InitModule, otelModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, payloadStorageServiceProvider: Provider<PayloadStorageService?>, cacheStorageServiceProvider: Provider<PayloadStorageService?>, requestExecutionServiceProvider: Provider<RequestExecutionService?>, deliveryServiceProvider: Provider<DeliveryService?>, ) + LongParameterList:DeliveryModuleSupplier.kt$( configModule: ConfigModule, initModule: InitModule, otelModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, androidServicesModule: AndroidServicesModule, payloadStorageServiceProvider: Provider<PayloadStorageService>?, cacheStorageServiceProvider: Provider<PayloadStorageService>?, requestExecutionServiceProvider: Provider<RequestExecutionService>?, deliveryServiceProvider: Provider<DeliveryService>?, ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImpl.kt index 807aabf0a6..ca361b6d8c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImpl.kt @@ -5,23 +5,17 @@ package io.embrace.android.embracesdk.internal.injection import android.preference.PreferenceManager import io.embrace.android.embracesdk.internal.prefs.EmbracePreferencesService import io.embrace.android.embracesdk.internal.prefs.PreferencesService -import io.embrace.android.embracesdk.internal.worker.Worker internal class AndroidServicesModuleImpl( initModule: InitModule, coreModule: CoreModule, - workerThreadModule: WorkerThreadModule, ) : AndroidServicesModule { override val preferencesService: PreferencesService by singleton { - val lazyPrefs = lazy { + EmbracePreferencesService( PreferenceManager.getDefaultSharedPreferences( coreModule.context - ) - } - EmbracePreferencesService( - workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), - lazyPrefs, + ), initModule.clock, initModule.jsonSerializer ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleSupplier.kt index 2ff61d7093..183dcca485 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleSupplier.kt @@ -6,11 +6,9 @@ package io.embrace.android.embracesdk.internal.injection typealias AndroidServicesModuleSupplier = ( initModule: InitModule, coreModule: CoreModule, - workerThreadModule: WorkerThreadModule, ) -> AndroidServicesModule fun createAndroidServicesModule( initModule: InitModule, coreModule: CoreModule, - workerThreadModule: WorkerThreadModule, -): AndroidServicesModule = AndroidServicesModuleImpl(initModule, coreModule, workerThreadModule) +): AndroidServicesModule = AndroidServicesModuleImpl(initModule, coreModule) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt index 98938667ad..5bc5064b00 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt @@ -1,10 +1,13 @@ package io.embrace.android.embracesdk.internal.injection +import io.embrace.android.embracesdk.core.BuildConfig import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryService import io.embrace.android.embracesdk.internal.comms.delivery.EmbraceDeliveryService import io.embrace.android.embracesdk.internal.delivery.StoredTelemetryMetadata import io.embrace.android.embracesdk.internal.delivery.caching.PayloadCachingService import io.embrace.android.embracesdk.internal.delivery.caching.PayloadCachingServiceImpl +import io.embrace.android.embracesdk.internal.delivery.execution.HttpUrlConnectionRequestExecutionService +import io.embrace.android.embracesdk.internal.delivery.execution.OkHttpRequestExecutionService import io.embrace.android.embracesdk.internal.delivery.execution.RequestExecutionService import io.embrace.android.embracesdk.internal.delivery.intake.IntakeService import io.embrace.android.embracesdk.internal.delivery.intake.IntakeServiceImpl @@ -28,21 +31,11 @@ internal class DeliveryModuleImpl( coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, - requestExecutionServiceProvider: Provider, - payloadStorageServiceProvider: Provider, - cacheStorageServiceProvider: Provider, - deliveryServiceProvider: () -> DeliveryService? = { - val apiService = essentialServiceModule.apiService - if (configModule.configService.isOnlyUsingOtelExporters() || apiService == null) { - null - } else { - EmbraceDeliveryService( - storageModule.deliveryCacheManager, - apiService, - initModule.jsonSerializer - ) - } - }, + androidServicesModule: AndroidServicesModule, + requestExecutionServiceProvider: Provider?, + payloadStorageServiceProvider: Provider?, + cacheStorageServiceProvider: Provider?, + deliveryServiceProvider: Provider? ) : DeliveryModule { private val processIdProvider = { otelModule.openTelemetryConfiguration.processIdentifier } @@ -64,7 +57,15 @@ internal class DeliveryModuleImpl( } override val deliveryService: DeliveryService? by singleton { - deliveryServiceProvider() + deliveryServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { + null + } else { + EmbraceDeliveryService( + storageModule.deliveryCacheManager, + essentialServiceModule.apiService ?: return@singleton null, + initModule.jsonSerializer + ) + } } private val dataPersistenceWorker: PriorityWorker by singleton { @@ -111,7 +112,7 @@ internal class DeliveryModuleImpl( } override val payloadStorageService: PayloadStorageService? by singleton { - payloadStorageServiceProvider() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { + payloadStorageServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { null } else { PayloadStorageServiceImpl( @@ -125,7 +126,7 @@ internal class DeliveryModuleImpl( } override val cacheStorageService: PayloadStorageService? by singleton { - cacheStorageServiceProvider() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { + cacheStorageServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { null } else { PayloadStorageServiceImpl( @@ -139,7 +140,30 @@ internal class DeliveryModuleImpl( } override val requestExecutionService: RequestExecutionService? by singleton { - requestExecutionServiceProvider() + requestExecutionServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { + null + } else { + val appId = checkNotNull(configModule.configService.appId) + val coreBaseUrl = configModule.configService.sdkEndpointBehavior.getData(appId) + val lazyDeviceId = lazy(androidServicesModule.preferencesService::deviceIdentifier) + if (configModule.configService.autoDataCaptureBehavior.shouldUseOkHttp()) { + OkHttpRequestExecutionService( + coreBaseUrl, + lazyDeviceId, + appId, + BuildConfig.VERSION_NAME, + initModule.logger, + ) + } else { + HttpUrlConnectionRequestExecutionService( + coreBaseUrl, + lazyDeviceId, + appId, + BuildConfig.VERSION_NAME, + initModule.logger, + ) + } + } } override val schedulingService: SchedulingService? by singleton { diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleSupplier.kt index c8dff6de20..6b98dbef5d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleSupplier.kt @@ -16,10 +16,11 @@ typealias DeliveryModuleSupplier = ( coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, - payloadStorageServiceProvider: Provider, - cacheStorageServiceProvider: Provider, - requestExecutionServiceProvider: Provider, - deliveryServiceProvider: Provider, + androidServicesModule: AndroidServicesModule, + payloadStorageServiceProvider: Provider?, + cacheStorageServiceProvider: Provider?, + requestExecutionServiceProvider: Provider?, + deliveryServiceProvider: Provider?, ) -> DeliveryModule fun createDeliveryModule( @@ -30,10 +31,11 @@ fun createDeliveryModule( coreModule: CoreModule, storageModule: StorageModule, essentialServiceModule: EssentialServiceModule, - payloadStorageServiceProvider: Provider, - cacheStorageServiceProvider: Provider, - requestExecutionServiceProvider: Provider, - deliveryServiceProvider: Provider, + androidServicesModule: AndroidServicesModule, + payloadStorageServiceProvider: Provider?, + cacheStorageServiceProvider: Provider?, + requestExecutionServiceProvider: Provider?, + deliveryServiceProvider: Provider?, ): DeliveryModule = DeliveryModuleImpl( configModule, initModule, @@ -42,6 +44,7 @@ fun createDeliveryModule( coreModule, storageModule, essentialServiceModule, + androidServicesModule, requestExecutionServiceProvider, payloadStorageServiceProvider, cacheStorageServiceProvider, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt index 388f34de76..f9c7b7574d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesService.kt @@ -1,48 +1,16 @@ package io.embrace.android.embracesdk.internal.prefs import android.content.SharedPreferences -import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer import io.embrace.android.embracesdk.internal.utils.Uuid.getEmbUuid -import io.embrace.android.embracesdk.internal.worker.BackgroundWorker -import java.util.concurrent.Callable -import java.util.concurrent.Future -import java.util.concurrent.TimeUnit internal class EmbracePreferencesService( - private val backgroundWorker: BackgroundWorker, - private val lazyPrefs: Lazy, + private val prefs: SharedPreferences, private val clock: Clock, private val serializer: PlatformSerializer, ) : PreferencesService { - // We get SharedPreferences on a background thread because it loads data from disk - // and can block. When client code needs to set/get a preference, getSharedPrefs() will - // block if necessary with Future.get(). Eagerly offloading buys us more time - // for SharedPreferences to load the File and reduces the likelihood of blocking - // when invoked by client code. - private val preferences: Future = Systrace.traceSynchronous("trigger-load-prefs") { - backgroundWorker.submit( - callable = Callable { - Systrace.traceSynchronous("load-prefs") { - lazyPrefs.value - } - } - ) - } - - // fallback from this very unlikely case by just loading on the main thread - private val prefs: SharedPreferences - get() = try { - Systrace.traceSynchronous("get-prefs") { - preferences.get(2, TimeUnit.SECONDS) - } - } catch (exc: Throwable) { - // fallback from this very unlikely case by just loading on the main thread - lazyPrefs.value - } - private fun SharedPreferences.getStringPreference(key: String): String? { return getString(key, null) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt index a4a80a65e1..8aa13c942b 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt @@ -10,7 +10,6 @@ import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan import io.embrace.android.embracesdk.fakes.behavior.FakeSessionBehavior -import io.embrace.android.embracesdk.fakes.fakeBackgroundWorker import io.embrace.android.embracesdk.internal.prefs.EmbracePreferencesService import io.embrace.android.embracesdk.internal.prefs.PreferencesService import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer @@ -43,11 +42,10 @@ internal class EmbraceSessionPropertiesTest { @Before fun setUp() { - val worker = fakeBackgroundWorker() context = ApplicationProvider.getApplicationContext() - val prefs = lazy { PreferenceManager.getDefaultSharedPreferences(context) } + val prefs = PreferenceManager.getDefaultSharedPreferences(context) preferencesService = - EmbracePreferencesService(worker, prefs, fakeClock, EmbraceSerializer()) + EmbracePreferencesService(prefs, fakeClock, EmbraceSerializer()) configService = FakeConfigService( sessionBehavior = FakeSessionBehavior(MAX_SESSION_PROPERTIES_DEFAULT) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt index 9317efd933..9024062d3d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/AndroidServicesModuleImplTest.kt @@ -1,10 +1,13 @@ package io.embrace.android.embracesdk.internal.injection +import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.internal.prefs.EmbracePreferencesService import io.mockk.mockk import org.junit.Assert.assertTrue import org.junit.Test +import org.junit.runner.RunWith +@RunWith(AndroidJUnit4::class) internal class AndroidServicesModuleImplTest { @Test @@ -13,8 +16,7 @@ internal class AndroidServicesModuleImplTest { val coreModule = createCoreModule(mockk(relaxed = true), initModule) val module = AndroidServicesModuleImpl( initModule = initModule, - coreModule = coreModule, - workerThreadModule = WorkerThreadModuleImpl() + coreModule = coreModule ) assertTrue(module.preferencesService is EmbracePreferencesService) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt index c1a4695a0f..2f69cb6e37 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImplTest.kt @@ -8,6 +8,7 @@ import io.embrace.android.embracesdk.fakes.FakeDeliveryService import io.embrace.android.embracesdk.fakes.FakeOpenTelemetryModule import io.embrace.android.embracesdk.fakes.FakeRequestExecutionService import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior +import io.embrace.android.embracesdk.fakes.injection.FakeAndroidServicesModule import io.embrace.android.embracesdk.fakes.injection.FakeEssentialServiceModule import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeStorageModule @@ -38,9 +39,10 @@ class DeliveryModuleImplTest { CoreModuleImpl(ApplicationProvider.getApplicationContext(), initModule), FakeStorageModule(), FakeEssentialServiceModule(), + FakeAndroidServicesModule(), ::FakeRequestExecutionService, - { null }, - { null }, + null, + null, ::FakeDeliveryService ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt index 1d53f3a400..0bdef72e48 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/prefs/EmbracePreferencesServiceTest.kt @@ -9,7 +9,6 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeSharedPreferences -import io.embrace.android.embracesdk.fakes.fakeBackgroundWorker import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -27,7 +26,6 @@ internal class EmbracePreferencesServiceTest { private lateinit var service: EmbracePreferencesService private lateinit var fakeClock: FakeClock - private val executorService = fakeBackgroundWorker() private val context = ApplicationProvider.getApplicationContext() @Before @@ -35,8 +33,7 @@ internal class EmbracePreferencesServiceTest { prefs = PreferenceManager.getDefaultSharedPreferences(context) fakeClock = FakeClock() service = EmbracePreferencesService( - executorService, - lazy { prefs }, + prefs, fakeClock, EmbraceSerializer() ) @@ -190,8 +187,7 @@ internal class EmbracePreferencesServiceTest { @Test fun `test incrementAndGet returns -1 on an exception`() { service = EmbracePreferencesService( - executorService, - lazy { FakeSharedPreferences(throwExceptionOnGet = true) }, + FakeSharedPreferences(throwExceptionOnGet = true), fakeClock, EmbraceSerializer() ) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index 58821db99d..b43e3ea152 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -14,7 +14,9 @@ import io.embrace.android.embracesdk.fakes.injection.FakeAnrModule import io.embrace.android.embracesdk.fakes.injection.FakeCoreModule import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeNativeCoreModule +import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryService import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.delivery.execution.RequestExecutionService import io.embrace.android.embracesdk.internal.delivery.storage.PayloadStorageService import io.embrace.android.embracesdk.internal.injection.AndroidServicesModule import io.embrace.android.embracesdk.internal.injection.AnrModule @@ -42,13 +44,12 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( val overriddenWorkerThreadModule: WorkerThreadModule = createWorkerThreadModule(), val overriddenAndroidServicesModule: AndroidServicesModule = createAndroidServicesModule( initModule = overriddenInitModule, - coreModule = overriddenCoreModule, - workerThreadModule = overriddenWorkerThreadModule + coreModule = overriddenCoreModule ), val fakeAnrModule: AnrModule = FakeAnrModule(), val fakeNativeFeatureModule: FakeNativeFeatureModule = FakeNativeFeatureModule(), - var cacheStorageServiceProvider: Provider = { null }, - var payloadStorageServiceProvider: Provider = { null }, + var cacheStorageServiceProvider: Provider? = null, + var payloadStorageServiceProvider: Provider? = null, val networkConnectivityService: FakeNetworkConnectivityService = FakeNetworkConnectivityService(), val lifecycleOwner: TestLifecycleOwner = TestLifecycleOwner(initialState = Lifecycle.State.INITIALIZED), ) { @@ -62,7 +63,7 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( openTelemetryModule = overriddenInitModule.openTelemetryModule, coreModuleSupplier = { _, _ -> overriddenCoreModule }, workerThreadModuleSupplier = { overriddenWorkerThreadModule }, - androidServicesModuleSupplier = { _, _, _ -> overriddenAndroidServicesModule }, + androidServicesModuleSupplier = { _, _ -> overriddenAndroidServicesModule }, essentialServiceModuleSupplier = { initModule, configModule, openTelemetryModule, coreModule, workerThreadModule, systemServiceModule, androidServicesModule, storageModule, _, _ -> createEssentialServiceModule( initModule, @@ -76,7 +77,15 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( { lifecycleOwner } ) { networkConnectivityService } }, - deliveryModuleSupplier = { configModule, otelModule, initModule, workerThreadModule, coreModule, storageModule, essentialServiceModule, _, _, requestExecutionServiceProvider, deliveryServiceProvider -> + deliveryModuleSupplier = { configModule, otelModule, initModule, workerThreadModule, coreModule, storageModule, essentialServiceModule, androidServicesModule, _, _, _, _ -> + val requestExecutionServiceProvider: Provider? = when { + useMockWebServer -> null + else -> ::FakeRequestExecutionService + } + val deliveryServiceProvider: Provider? = when { + useMockWebServer -> null + else -> ::FakeDeliveryService + } createDeliveryModule( configModule, otelModule, @@ -85,20 +94,12 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( coreModule, storageModule, essentialServiceModule, + androidServicesModule, payloadStorageServiceProvider = payloadStorageServiceProvider, cacheStorageServiceProvider = cacheStorageServiceProvider, - requestExecutionServiceProvider = { - when { - useMockWebServer -> requestExecutionServiceProvider() - else -> FakeRequestExecutionService() - } - }, - deliveryServiceProvider = { - when { - useMockWebServer -> deliveryServiceProvider() - else -> FakeDeliveryService() - } - }) + requestExecutionServiceProvider = requestExecutionServiceProvider, + deliveryServiceProvider = deliveryServiceProvider + ) }, configModuleSupplier = { initModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, foregroundAction, _ -> createConfigModule( diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index f2a29a03ac..b2453e13a6 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -1,14 +1,10 @@ package io.embrace.android.embracesdk.internal.injection import android.content.Context -import io.embrace.android.embracesdk.core.BuildConfig import io.embrace.android.embracesdk.internal.EmbraceInternalApi import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.capture.envelope.session.OtelPayloadMapperImpl -import io.embrace.android.embracesdk.internal.comms.delivery.EmbraceDeliveryService import io.embrace.android.embracesdk.internal.config.RemoteConfigSourceImpl -import io.embrace.android.embracesdk.internal.delivery.execution.HttpUrlConnectionRequestExecutionService -import io.embrace.android.embracesdk.internal.delivery.execution.OkHttpRequestExecutionService import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl import io.embrace.android.embracesdk.internal.logging.InternalErrorType @@ -129,7 +125,6 @@ internal class ModuleInitBootstrapper( val result = if (!isInitialized()) { coreModule = init(CoreModule::class) { coreModuleSupplier(context, initModule) } - val serviceRegistry = coreModule.serviceRegistry workerThreadModule = init(WorkerThreadModule::class) { workerThreadModuleSupplier() } @@ -140,12 +135,8 @@ internal class ModuleInitBootstrapper( } } - systemServiceModule = init(SystemServiceModule::class) { - systemServiceModuleSupplier(coreModule, versionChecker) - } - androidServicesModule = init(AndroidServicesModule::class) { - androidServicesModuleSupplier(initModule, coreModule, workerThreadModule) + androidServicesModuleSupplier(initModule, coreModule) } configModule = init(ConfigModule::class) { @@ -163,12 +154,17 @@ internal class ModuleInitBootstrapper( { null } ) } + val serviceRegistry = coreModule.serviceRegistry postInit(ConfigModule::class) { serviceRegistry.registerService(lazy { configModule.configService }) serviceRegistry.registerService(lazy { configModule.remoteConfigSource }) openTelemetryModule.setupSensitiveKeysBehavior(configModule.configService.sensitiveKeysBehavior) } + systemServiceModule = init(SystemServiceModule::class) { + systemServiceModuleSupplier(coreModule, versionChecker) + } + storageModule = init(StorageModule::class) { storageModuleSupplier(initModule, coreModule, workerThreadModule) } @@ -286,49 +282,15 @@ internal class ModuleInitBootstrapper( coreModule, storageModule, essentialServiceModule, - { null }, - { null }, - { - if (configModule.configService.isOnlyUsingOtelExporters()) { - null - } else { - val appId = checkNotNull(configModule.configService.appId) - val coreBaseUrl = configModule.configService.sdkEndpointBehavior.getData(appId) - val lazyDeviceId = lazy(androidServicesModule.preferencesService::deviceIdentifier) - if (configModule.configService.autoDataCaptureBehavior.shouldUseOkHttp()) { - OkHttpRequestExecutionService( - coreBaseUrl, - lazyDeviceId, - appId, - BuildConfig.VERSION_NAME, - logger, - ) - } else { - HttpUrlConnectionRequestExecutionService( - coreBaseUrl, - lazyDeviceId, - appId, - BuildConfig.VERSION_NAME, - logger, - ) - } - } - }, - { - val apiService = essentialServiceModule.apiService - if (configModule.configService.isOnlyUsingOtelExporters() || apiService == null) { - null - } else { - EmbraceDeliveryService( - storageModule.deliveryCacheManager, - apiService, - initModule.jsonSerializer - ) - } - } + androidServicesModule, + null, + null, + null, + null ) - }.apply { - payloadCachingService?.run { + } + postInit(DeliveryModule::class) { + deliveryModule.payloadCachingService?.run { openTelemetryModule.spanRepository.setSpanUpdateNotifier { reportBackgroundActivityStateChange() } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 35f458da7d..13e234ddab 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -39,14 +39,14 @@ internal fun fakeModuleInitBootstrapper( fakeOpenTelemetryModule: FakeOpenTelemetryModule = FakeOpenTelemetryModule(), coreModuleSupplier: CoreModuleSupplier = { _, _ -> FakeCoreModule() }, systemServiceModuleSupplier: SystemServiceModuleSupplier = { _, _ -> FakeSystemServiceModule() }, - androidServicesModuleSupplier: AndroidServicesModuleSupplier = { _, _, _ -> FakeAndroidServicesModule() }, + androidServicesModuleSupplier: AndroidServicesModuleSupplier = { _, _ -> FakeAndroidServicesModule() }, workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, - deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, + deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, anrModuleSupplier: AnrModuleSupplier = { _, _, _ -> FakeAnrModule() }, logModuleSupplier: LogModuleSupplier = { _, _, _, _, _, _, _, _ -> FakeLogModule() }, nativeCoreModuleSupplier: NativeCoreModuleSupplier = { _ -> FakeNativeCoreModule() }, From c737405a8f61a1fcf3936912d0259156c4e25a6a Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Mon, 11 Nov 2024 17:22:07 +0000 Subject: [PATCH 20/28] refactor: extract url construction --- .../internal/comms/api/ApiRequestMapper.kt | 2 +- .../internal/comms/api/ApiUrlBuilder.kt | 6 +-- .../internal/comms/api/EmbraceApiService.kt | 2 +- .../comms/api/EmbraceApiUrlBuilder.kt | 44 ++++++++++-------- .../internal/comms/api/EmbraceUrl.kt | 1 + .../comms/delivery/PendingApiCallQueue.kt | 2 +- .../internal/config/ConfigService.kt | 6 --- .../internal/config/ConfigServiceImpl.kt | 7 --- .../config/OkHttpRemoteConfigSource.kt | 3 +- .../config/behavior/SdkEndpointBehavior.kt | 14 ------ .../behavior/SdkEndpointBehaviorImpl.kt | 34 -------------- .../internal/injection/ConfigModule.kt | 2 + .../internal/injection/ConfigModuleImpl.kt | 14 ++++++ .../injection/ConfigModuleSupplier.kt | 3 ++ .../internal/injection/DeliveryModuleImpl.kt | 4 +- .../injection/EssentialServiceModule.kt | 2 - .../injection/EssentialServiceModuleImpl.kt | 23 +--------- .../internal/injection/LogModuleImpl.kt | 1 + .../logging/EmbraceNetworkCaptureService.kt | 9 ++-- .../comms/api/ApiRequestMapperTest.kt | 15 ++++-- .../comms/api/EmbraceApiServiceTest.kt | 46 ++++++------------- .../comms/api/EmbraceApiUrlBuilderTest.kt | 31 ++++--------- .../EmbracePendingApiCallsSenderTest.kt | 21 ++++----- .../comms/delivery/PendingApiCallsTest.kt | 2 +- .../config/OkHttpRemoteConfigSourceTest.kt | 19 ++++---- .../behavior/SdkEndpointBehaviorImplTest.kt | 16 ------- .../injection/ConfigModuleImplTest.kt | 6 ++- .../EmbraceNetworkCaptureServiceTest.kt | 12 +++-- .../OkHttpRequestExecutionService.kt | 2 +- .../OkHttpRequestExecutionServiceTest.kt | 4 +- .../embracesdk/internal/comms/api/Endpoint.kt | 3 +- .../actions/EmbraceSetupInterface.kt | 3 +- .../injection/ModuleInitBootstrapper.kt | 1 + .../fakes/FakeModuleInitBootstrapper.kt | 2 +- .../EssentialServiceModuleImplTest.kt | 1 - .../injection/ModuleInitBootstrapperTest.kt | 2 +- .../android/embracesdk/fakes/Behavior.kt | 9 ---- .../embracesdk/fakes/FakeApiUrlBuilder.kt | 9 ++-- .../embracesdk/fakes/FakeConfigModule.kt | 4 +- .../embracesdk/fakes/FakeConfigService.kt | 2 - .../fakes/behavior/FakeSdkEndpointBehavior.kt | 11 ----- .../injection/FakeEssentialServiceModule.kt | 3 -- 42 files changed, 147 insertions(+), 256 deletions(-) delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehavior.kt delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt delete mode 100644 embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImplTest.kt delete mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkEndpointBehavior.kt diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapper.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapper.kt index 8f6b40db0b..bccd822c6c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapper.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapper.kt @@ -13,7 +13,7 @@ internal class ApiRequestMapper( ) { private val apiUrlBuilders = Endpoint.values().associateWith { - urlBuilder.getEmbraceUrlWithSuffix(it.version, it.path) + urlBuilder.resolveUrl(it) } private fun Endpoint.asEmbraceUrl(): String = checkNotNull(apiUrlBuilders[this]) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt index cf55926384..85f7b47b90 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/ApiUrlBuilder.kt @@ -16,12 +16,12 @@ interface ApiUrlBuilder { val deviceId: String /** - * Returns the url used to fetch the config. + * Base URL for the data endpoint */ - fun getConfigUrl(): String + val baseDataUrl: String /** * Returns the url used to send data to Embrace. */ - fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String + fun resolveUrl(endpoint: Endpoint): String } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt index 1c7ba875f2..d4098107d9 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiService.kt @@ -34,7 +34,7 @@ internal class EmbraceApiService( } private val configUrl by lazy { Systrace.traceSynchronous("config-url-init") { - urlBuilder.getConfigUrl() + urlBuilder.resolveUrl(Endpoint.CONFIG) } } private var lastNetworkStatus: NetworkStatus = NetworkStatus.UNKNOWN diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt index 2e6666f84a..02ce4ae2a8 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt @@ -1,32 +1,36 @@ package io.embrace.android.embracesdk.internal.comms.api import android.os.Build +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig internal class EmbraceApiUrlBuilder( - private val coreBaseUrl: String, - private val configBaseUrl: String, - override val appId: String, - deviceIdImpl: Lazy, - private val lazyAppVersionName: Lazy, + override val deviceId: String, + private val appVersionName: String, + instrumentedConfig: InstrumentedConfig, ) : ApiUrlBuilder { companion object { - private const val CONFIG_API_VERSION = 2 + private const val CONFIG_DEFAULT: String = "config.emb-api.com" + private const val DATA_DEFAULT: String = "data.emb-api.com" } - override val deviceId: String by deviceIdImpl - - private fun getConfigBaseUrl() = "$configBaseUrl/v$CONFIG_API_VERSION/${"config"}" - - private fun getOperatingSystemCode() = Build.VERSION.SDK_INT.toString() + ".0.0" - - override fun getConfigUrl(): String { - return "${getConfigBaseUrl()}?appId=$appId&osVersion=${getOperatingSystemCode()}" + - "&appVersion=${lazyAppVersionName.value}&deviceId=$deviceId" - } - - override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String { - val fullSuffix = if (apiVersion == "v1") "log/$suffix" else suffix - return "$coreBaseUrl/$apiVersion/$fullSuffix" + override val appId: String = checkNotNull(instrumentedConfig.project.getAppId()) + private val coreBaseUrl = instrumentedConfig.baseUrls.getData() ?: "https://a-$appId.$DATA_DEFAULT/api" + private val configBaseUrl = instrumentedConfig.baseUrls.getConfig() ?: "https://a-$appId.$CONFIG_DEFAULT" + private val operatingSystemCode = Build.VERSION.SDK_INT.toString() + ".0.0" + override val baseDataUrl: String = resolveUrl(Endpoint.SESSIONS).split(Endpoint.SESSIONS.path).first() + + override fun resolveUrl(endpoint: Endpoint): String { + val baseUrl = when (endpoint) { + Endpoint.CONFIG -> configBaseUrl + else -> coreBaseUrl + } + val queryParams = when (endpoint) { + Endpoint.CONFIG -> + "?appId=$appId&osVersion=$operatingSystemCode" + + "&appVersion=$appVersionName&deviceId=$deviceId" + else -> "" + } + return "$baseUrl/${endpoint.version}/${endpoint.path}$queryParams" } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceUrl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceUrl.kt index 45ec98f1a9..db25dbe087 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceUrl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceUrl.kt @@ -18,6 +18,7 @@ internal class EmbraceUrl(val url: URL) { return when (url.path.substringAfterLast("/")) { Endpoint.LOGS.path -> Endpoint.LOGS Endpoint.SESSIONS.path -> Endpoint.SESSIONS + Endpoint.CONFIG.path -> Endpoint.CONFIG else -> Endpoint.UNKNOWN } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallQueue.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallQueue.kt index f68f776c7e..2e506c2876 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallQueue.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallQueue.kt @@ -88,7 +88,7 @@ class PendingApiCallQueue( return when (this) { Endpoint.LOGS -> 10 Endpoint.SESSIONS -> 100 - Endpoint.UNKNOWN -> 50 + else -> 50 } } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt index 486e237c57..0995e588bc 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigService.kt @@ -9,7 +9,6 @@ import io.embrace.android.embracesdk.internal.config.behavior.DataCaptureEventBe import io.embrace.android.embracesdk.internal.config.behavior.LogMessageBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior import io.embrace.android.embracesdk.internal.config.behavior.SessionBehavior @@ -68,11 +67,6 @@ interface ConfigService { */ val sdkModeBehavior: SdkModeBehavior - /** - * Provides base endpoints the SDK should send data to - */ - val sdkEndpointBehavior: SdkEndpointBehavior - /** * Provides whether the SDK should enable certain 'behavior' of web vitals */ diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt index 3758da0864..5c7cb65e98 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt @@ -19,8 +19,6 @@ import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior @@ -108,11 +106,6 @@ internal class ConfigServiceImpl( remoteSupplier = configProvider ) - override val sdkEndpointBehavior: SdkEndpointBehavior = SdkEndpointBehaviorImpl( - thresholdCheck = thresholdCheck, - instrumentedConfig = instrumentedConfig - ) - override val appExitInfoBehavior: AppExitInfoBehavior = AppExitInfoBehaviorImpl( thresholdCheck = thresholdCheck, remoteSupplier = configProvider, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt index 580045de67..e6bccc02b4 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSource.kt @@ -4,6 +4,7 @@ import io.embrace.android.embracesdk.core.BuildConfig import io.embrace.android.embracesdk.internal.comms.api.ApiRequest import io.embrace.android.embracesdk.internal.comms.api.ApiRequestUrl import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder +import io.embrace.android.embracesdk.internal.comms.api.Endpoint import io.embrace.android.embracesdk.internal.comms.api.getHeaders import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer @@ -25,7 +26,7 @@ internal class OkHttpRemoteConfigSource( } private fun fetchConfigImpl(): RemoteConfig? { - val url = apiUrlBuilder.getConfigUrl() + val url = apiUrlBuilder.resolveUrl(Endpoint.CONFIG) val headers = prepareConfigRequest(url).getHeaders() val builder = Request.Builder().url(url) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehavior.kt deleted file mode 100644 index cf00721f0b..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehavior.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.behavior - -interface SdkEndpointBehavior { - - /** - * Data base URL. - */ - fun getData(appId: String?): String - - /** - * Config base URL. - */ - fun getConfig(appId: String?): String -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt deleted file mode 100644 index 7e05fec69f..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImpl.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.behavior - -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig -import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig - -/** - * Provides the behavior that the Background Activity feature should follow. - */ -class SdkEndpointBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - private val instrumentedConfig: InstrumentedConfig, -) : SdkEndpointBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck -) { - - companion object { - const val CONFIG_DEFAULT: String = "config.emb-api.com" - const val DATA_DEFAULT: String = "data.emb-api.com" - } - - override fun getData(appId: String?): String { - if (appId == null) { - return "" - } - return instrumentedConfig.baseUrls.getData() ?: "https://a-$appId.$DATA_DEFAULT" - } - - override fun getConfig(appId: String?): String { - if (appId == null) { - return "" - } - return instrumentedConfig.baseUrls.getConfig() ?: "https://a-$appId.$CONFIG_DEFAULT" - } -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt index b051b60039..61501e64c9 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModule.kt @@ -1,9 +1,11 @@ package io.embrace.android.embracesdk.internal.injection +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.RemoteConfigSource interface ConfigModule { val configService: ConfigService val remoteConfigSource: RemoteConfigSource + val urlBuilder: ApiUrlBuilder? } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index dbc8d3539e..a7aca7c04e 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.internal.Systrace +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder +import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.ConfigServiceImpl import io.embrace.android.embracesdk.internal.config.RemoteConfigSource @@ -11,6 +13,7 @@ import io.embrace.android.embracesdk.internal.worker.Worker internal class ConfigModuleImpl( initModule: InitModule, + coreModule: CoreModule, openTelemetryModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, @@ -38,4 +41,15 @@ internal class ConfigModuleImpl( foregroundAction = foregroundAction, ) } + + override val urlBuilder: ApiUrlBuilder? by singleton { + configService.appId ?: return@singleton null + Systrace.traceSynchronous("url-builder-init") { + EmbraceApiUrlBuilder( + deviceId = androidServicesModule.preferencesService.deviceIdentifier, + appVersionName = coreModule.packageVersionInfo.versionName, + instrumentedConfig = initModule.instrumentedConfig, + ) + } + } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt index 1d725d7b59..768a8be251 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt @@ -9,6 +9,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider */ typealias ConfigModuleSupplier = ( initModule: InitModule, + coreModule: CoreModule, openTelemetryModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, @@ -19,6 +20,7 @@ typealias ConfigModuleSupplier = ( fun createConfigModule( initModule: InitModule, + coreModule: CoreModule, openTelemetryModule: OpenTelemetryModule, workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, @@ -27,6 +29,7 @@ fun createConfigModule( remoteConfigSourceProvider: Provider, ): ConfigModule = ConfigModuleImpl( initModule, + coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt index 5bc5064b00..5bbd842c96 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/DeliveryModuleImpl.kt @@ -143,8 +143,8 @@ internal class DeliveryModuleImpl( requestExecutionServiceProvider?.invoke() ?: if (configModule.configService.isOnlyUsingOtelExporters()) { null } else { - val appId = checkNotNull(configModule.configService.appId) - val coreBaseUrl = configModule.configService.sdkEndpointBehavior.getData(appId) + val appId = configModule.configService.appId ?: return@singleton null + val coreBaseUrl = configModule.urlBuilder?.baseDataUrl ?: return@singleton null val lazyDeviceId = lazy(androidServicesModule.preferencesService::deviceIdentifier) if (configModule.configService.autoDataCaptureBehavior.shouldUseOkHttp()) { OkHttpRequestExecutionService( diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModule.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModule.kt index d742ac7bf0..39eaed4023 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModule.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModule.kt @@ -6,7 +6,6 @@ import io.embrace.android.embracesdk.internal.capture.session.SessionPropertiesS import io.embrace.android.embracesdk.internal.capture.user.UserService import io.embrace.android.embracesdk.internal.comms.api.ApiClient import io.embrace.android.embracesdk.internal.comms.api.ApiService -import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.delivery.PendingApiCallsSender import io.embrace.android.embracesdk.internal.session.id.SessionIdTracker import io.embrace.android.embracesdk.internal.session.lifecycle.ActivityTracker @@ -20,7 +19,6 @@ interface EssentialServiceModule { val processStateService: ProcessStateService val activityLifecycleTracker: ActivityTracker val userService: UserService - val urlBuilder: ApiUrlBuilder val apiClient: ApiClient val apiService: ApiService? val networkConnectivityService: NetworkConnectivityService diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt index 230a46af9d..7f6b819776 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/EssentialServiceModuleImpl.kt @@ -15,9 +15,7 @@ import io.embrace.android.embracesdk.internal.comms.api.ApiClient import io.embrace.android.embracesdk.internal.comms.api.ApiClientImpl import io.embrace.android.embracesdk.internal.comms.api.ApiRequest import io.embrace.android.embracesdk.internal.comms.api.ApiService -import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiService -import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.delivery.EmbracePendingApiCallsSender import io.embrace.android.embracesdk.internal.comms.delivery.PendingApiCallsSender import io.embrace.android.embracesdk.internal.session.id.SessionIdTracker @@ -56,25 +54,6 @@ class EssentialServiceModuleImpl( ActivityLifecycleTracker(coreModule.application, initModule.logger) } - override val urlBuilder: ApiUrlBuilder by singleton { - Systrace.traceSynchronous("url-builder-init") { - // We use SdkEndpointBehavior and localConfig directly to avoid a circular dependency - // but we want to access behaviors from ConfigService when possible. - val sdkEndpointBehavior = configService.sdkEndpointBehavior - val appId = checkNotNull(configService.appId) - val coreBaseUrl = sdkEndpointBehavior.getData(appId) - val configBaseUrl = sdkEndpointBehavior.getConfig(appId) - - EmbraceApiUrlBuilder( - coreBaseUrl = coreBaseUrl, - configBaseUrl = configBaseUrl, - appId = appId, - deviceIdImpl = lazyDeviceId, - lazyAppVersionName = lazy { coreModule.packageVersionInfo.versionName } - ) - } - } - override val userService: UserService by singleton { Systrace.traceSynchronous("user-service-init") { EmbraceUserService( @@ -120,7 +99,7 @@ class EssentialServiceModuleImpl( pendingApiCallsSender = pendingApiCallsSender, lazyDeviceId = lazyDeviceId, appId = appId, - urlBuilder = urlBuilder + urlBuilder = checkNotNull(configModule.urlBuilder) ) } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/LogModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/LogModuleImpl.kt index 2e02aca1a4..21fb7d63c9 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/LogModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/LogModuleImpl.kt @@ -30,6 +30,7 @@ internal class LogModuleImpl( androidServicesModule.preferencesService, { networkCaptureDataSource }, configModule.configService, + configModule.urlBuilder, initModule.jsonSerializer, initModule.logger ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureService.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureService.kt index d52bafad29..bed4991c1b 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureService.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureService.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.network.logging +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig import io.embrace.android.embracesdk.internal.logging.EmbLogger @@ -19,6 +20,7 @@ internal class EmbraceNetworkCaptureService( private val preferencesService: PreferencesService, private val networkCaptureDataSource: Provider, private val configService: ConfigService, + private val urlBuilder: ApiUrlBuilder?, private val serializer: PlatformSerializer, private val logger: EmbLogger, ) : NetworkCaptureService { @@ -41,9 +43,10 @@ internal class EmbraceNetworkCaptureService( } // Embrace data endpoint cannot be captured, even if there is a rule for that. - val appId = configService.appId - if (url.contentEquals(configService.sdkEndpointBehavior.getData(appId))) { - return emptySet() + urlBuilder?.baseDataUrl?.let { + if (url.startsWith(it)) { + return emptySet() + } } val applicableRules = networkCaptureRules.filter { rule -> diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapperTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapperTest.kt index 27065ba416..de8105a82a 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapperTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/ApiRequestMapperTest.kt @@ -1,5 +1,7 @@ package io.embrace.android.embracesdk.internal.comms.api +import io.embrace.android.embracesdk.fakes.config.FakeBaseUrlConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.payload.Attribute import io.embrace.android.embracesdk.internal.payload.Envelope import io.embrace.android.embracesdk.internal.payload.Log @@ -19,11 +21,14 @@ internal class ApiRequestMapperTest { private val deviceId = lazy { "deviceId" } private val mapper = ApiRequestMapper( urlBuilder = EmbraceApiUrlBuilder( - BASE_URL, - CONFIG_URL, - "appId", - deviceId, - lazy { "appVersionName" } + deviceId.value, + "1.0", + FakeInstrumentedConfig( + baseUrls = FakeBaseUrlConfig( + configImpl = CONFIG_URL, + dataImpl = BASE_URL + ) + ) ), lazyDeviceId = deviceId, appId = "appId" diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt index 5af2be9c7f..1333294c72 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt @@ -5,6 +5,7 @@ import io.embrace.android.embracesdk.concurrency.BlockingScheduledExecutorServic import io.embrace.android.embracesdk.fakes.FakeApiClient import io.embrace.android.embracesdk.fakes.FakeDeliveryCacheManager import io.embrace.android.embracesdk.fakes.FakePendingApiCallsSender +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.TypeUtils import io.embrace.android.embracesdk.internal.comms.api.ApiClient.Companion.NO_HTTP_RESPONSE import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryCacheManager @@ -49,11 +50,9 @@ internal class EmbraceApiServiceTest { @Before fun setUp() { apiUrlBuilder = EmbraceApiUrlBuilder( - coreBaseUrl = "https://a-$fakeAppId.data.emb-api.com", - configBaseUrl = "https://a-$fakeAppId.config.emb-api.com", - appId = fakeAppId, - deviceIdImpl = lazy { fakeDeviceId }, - lazyAppVersionName = lazy { fakeAppVersionName } + deviceId = fakeDeviceId, + appVersionName = fakeAppVersionName, + instrumentedConfig = FakeInstrumentedConfig() ) fakeApiClient = FakeApiClient() cachedConfig = CachedConfig( @@ -151,7 +150,7 @@ internal class EmbraceApiServiceTest { var finished = false apiService.sendSession({ it.write(payload) }) { finished = true } verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/spans", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/spans", expectedPayload = payload ) assertTrue(finished) @@ -181,7 +180,7 @@ internal class EmbraceApiServiceTest { val type: ParameterizedType = TypeUtils.parameterizedType(Envelope::class, LogPayload::class) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getGenericsExpectedPayloadSerialized(logsEnvelope, type) ) } @@ -211,28 +210,11 @@ internal class EmbraceApiServiceTest { val request = fakePendingApiCallsSender.retryQueue.single().first val payload = fakePendingApiCallsSender.retryQueue.single().second - assertEquals("https://a-$fakeAppId.data.emb-api.com/v2/logs", request.url.url) + assertEquals("https://a-$fakeAppId.data.emb-api.com/api/v2/logs", request.url.url) val type: ParameterizedType = TypeUtils.parameterizedType(Envelope::class, LogPayload::class) assertArrayEquals(getGenericsExpectedPayloadSerialized(logsEnvelope, type), payload) } - @Test - fun `validate all API endpoint URLs`() { - Endpoint.values().forEach { - if (it.version == "v1") { - assertEquals( - "https://a-$fakeAppId.data.emb-api.com/v1/log/${it.path}", - apiUrlBuilder.getEmbraceUrlWithSuffix("v1", it.path) - ) - } else { - assertEquals( - "https://a-$fakeAppId.data.emb-api.com/v2/${it.path}", - apiUrlBuilder.getEmbraceUrlWithSuffix("v2", it.path) - ) - } - } - } - @Test fun `network request runnable is used`() { initApiService() @@ -247,7 +229,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) assertEquals(0, fakeApiClient.sentRequests.size) val request = fakePendingApiCallsSender.retryQueue.single().first - assertEquals("https://a-$fakeAppId.data.emb-api.com/v2/logs", request.url.url) + assertEquals("https://a-$fakeAppId.data.emb-api.com/api/v2/logs", request.url.url) } @Test @@ -263,7 +245,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(1, fakePendingApiCallsSender.retryQueue.size) @@ -281,7 +263,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(1, fakePendingApiCallsSender.retryQueue.size) @@ -302,7 +284,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) @@ -320,7 +302,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) @@ -341,7 +323,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) @@ -441,7 +423,7 @@ internal class EmbraceApiServiceTest { } companion object { - private const val fakeAppId = "A1B2C" + private const val fakeAppId = "abcde" private const val fakeDeviceId = "ajflkadsflkadslkfjds" private const val fakeAppVersionName = "6.1.0" private val defaultConfigResponseBody = diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt index 77470af413..ae2942194c 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt @@ -1,13 +1,11 @@ package io.embrace.android.embracesdk.internal.comms.api -import io.embrace.android.embracesdk.fakes.createSdkEndpointBehavior -import io.mockk.unmockkAll -import org.junit.After +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test -private const val APP_ID = "o0o0o" +private const val APP_ID = "abcde" private const val APP_VERSION_NAME = "1.0.0" private const val DEVICE_ID = "07D85B44E4E245F4A30E559BFC0D07FF" @@ -16,36 +14,27 @@ internal class EmbraceApiUrlBuilderTest { @Before fun setup() { - val baseUrlLocalConfig = createSdkEndpointBehavior() - apiUrlBuilder = EmbraceApiUrlBuilder( - coreBaseUrl = baseUrlLocalConfig.getData(APP_ID), - configBaseUrl = baseUrlLocalConfig.getConfig(APP_ID), - appId = APP_ID, - deviceIdImpl = lazy { DEVICE_ID }, - lazyAppVersionName = lazy { APP_VERSION_NAME }, + deviceId = DEVICE_ID, + appVersionName = APP_VERSION_NAME, + FakeInstrumentedConfig() ) } - @After - fun tearDown() { - unmockkAll() - } - @Test fun testUrls() { assertEquals( "https://a-$APP_ID.config.emb-api.com/v2/config?appId=$APP_ID&osVersion=0.0.0" + "&appVersion=$APP_VERSION_NAME&deviceId=$DEVICE_ID", - apiUrlBuilder.getConfigUrl() + apiUrlBuilder.resolveUrl(Endpoint.CONFIG) ) assertEquals( - "https://a-$APP_ID.data.emb-api.com/v1/log/suffix", - apiUrlBuilder.getEmbraceUrlWithSuffix("v1", "suffix") + "https://a-$APP_ID.data.emb-api.com/api/v2/logs", + apiUrlBuilder.resolveUrl(Endpoint.LOGS) ) assertEquals( - "https://a-$APP_ID.data.emb-api.com/v2/suffix", - apiUrlBuilder.getEmbraceUrlWithSuffix("v2", "suffix") + "https://a-$APP_ID.data.emb-api.com/api/v2/spans", + apiUrlBuilder.resolveUrl(Endpoint.SESSIONS) ) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt index b2c880ad0a..b6a3db910d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.comms.delivery import io.embrace.android.embracesdk.concurrency.BlockingScheduledExecutorService import io.embrace.android.embracesdk.fakes.FakeClock +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.comms.api.ApiRequest import io.embrace.android.embracesdk.internal.comms.api.ApiRequestMapper import io.embrace.android.embracesdk.internal.comms.api.ApiRequestUrl @@ -251,11 +252,9 @@ internal class EmbracePendingApiCallsSenderTest { repeat(15) { val mapper = ApiRequestMapper( EmbraceApiUrlBuilder( - "https://data.emb-api.com/$it", - "https://config.emb-api.com", - "appId", - lazy { "deviceId" }, - lazy { "appVersionName" } + "deviceId", + "appVersionName", + FakeInstrumentedConfig() ), lazy { "deviceId" }, "appId" @@ -271,22 +270,20 @@ internal class EmbracePendingApiCallsSenderTest { // verify logs were added to the queue, and oldest added requests are dropped assertEquals( - "https://data.emb-api.com/5/v2/logs", + "https://a-abcde.data.emb-api.com/api/v2/logs", queue.pollNextPendingApiCall()?.apiRequest?.url?.url ) assertEquals( - "https://data.emb-api.com/6/v2/logs", + "https://a-abcde.data.emb-api.com/api/v2/logs", queue.pollNextPendingApiCall()?.apiRequest?.url?.url ) // now add some sessions for retry and verify they are returned first val mapper = ApiRequestMapper( EmbraceApiUrlBuilder( - "https://data.emb-api.com/session", - "https://config.emb-api.com", - "appId", - lazy { "deviceId" }, - lazy { "appVersionName" } + "deviceId", + "appVersionName", + FakeInstrumentedConfig() ), lazy { "deviceId" }, "appId" diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallsTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallsTest.kt index 86d2068eec..7332f8e719 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallsTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/PendingApiCallsTest.kt @@ -181,8 +181,8 @@ internal class PendingApiCallsTest { private fun Endpoint.getMaxPendingApiCalls(): Int { return when (this) { Endpoint.LOGS -> 10 - Endpoint.UNKNOWN -> 50 Endpoint.SESSIONS -> 100 + else -> 50 } } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt index 0dedc37ca0..8416c83958 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -2,6 +2,8 @@ package io.embrace.android.embracesdk.internal.config import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.fakes.TestPlatformSerializer +import io.embrace.android.embracesdk.fakes.config.FakeBaseUrlConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig @@ -11,7 +13,6 @@ import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.RecordedRequest import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test @@ -39,11 +40,13 @@ class OkHttpRemoteConfigSourceTest { val baseUrl = server.url("api").toString() client = OkHttpClient.Builder().build() urlBuilder = EmbraceApiUrlBuilder( - coreBaseUrl = baseUrl, - configBaseUrl = baseUrl, - appId = "abcde", - deviceIdImpl = lazy { "deviceId" }, - lazyAppVersionName = lazy { "1.0.0" }, + "deviceId", + "1.0.0", + FakeInstrumentedConfig( + baseUrls = FakeBaseUrlConfig( + configImpl = baseUrl, + ) + ) ) } @@ -130,9 +133,9 @@ class OkHttpRemoteConfigSourceTest { } private fun assertConfigRequestReceived(request: RecordedRequest?) { - assertNotNull(request) + checkNotNull(request) assertEmbraceHeadersAdded(request) - val requestUrl = request?.requestUrl?.toUrl() ?: error("Request URL cannot be null") + val requestUrl = request.requestUrl?.toUrl() ?: error("Request URL cannot be null") assertEquals("/api/v2/config", requestUrl.path) assertEquals("appId=abcde&osVersion=21.0.0&appVersion=1.0.0&deviceId=deviceId", requestUrl.query) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImplTest.kt deleted file mode 100644 index 2c042fbe42..0000000000 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkEndpointBehaviorImplTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.behavior - -import io.embrace.android.embracesdk.fakes.createSdkEndpointBehavior -import org.junit.Assert.assertEquals -import org.junit.Test - -internal class SdkEndpointBehaviorImplTest { - - @Test - fun testDefaults() { - with(createSdkEndpointBehavior()) { - assertEquals("https://a-12345.config.emb-api.com", getConfig("12345")) - assertEquals("https://a-12345.data.emb-api.com", getData("12345")) - } - } -} diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt index 1b05922c46..51c460d39d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/injection/ConfigModuleImplTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.injection +import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.fakes.FakeOpenTelemetryModule import io.embrace.android.embracesdk.fakes.injection.FakeAndroidServicesModule @@ -15,8 +16,10 @@ internal class ConfigModuleImplTest { @Test fun `test defaults`() { + val initModule = FakeInitModule() val module = ConfigModuleImpl( - initModule = FakeInitModule(), + initModule = initModule, + coreModule = createCoreModule(ApplicationProvider.getApplicationContext(), initModule), openTelemetryModule = FakeOpenTelemetryModule(), workerThreadModule = FakeWorkerThreadModule(), androidServicesModule = FakeAndroidServicesModule(), @@ -24,5 +27,6 @@ internal class ConfigModuleImplTest { foregroundAction = {}, ) assertNotNull(module.configService) + assertNotNull(module.urlBuilder) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt index 27e25c0fa4..376243f3d5 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt @@ -4,7 +4,9 @@ import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeNetworkCaptureDataSource import io.embrace.android.embracesdk.fakes.FakePreferenceService import io.embrace.android.embracesdk.fakes.FakeSessionIdTracker +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.createNetworkBehavior +import io.embrace.android.embracesdk.internal.comms.api.EmbraceApiUrlBuilder import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl @@ -65,10 +67,9 @@ internal class EmbraceNetworkCaptureServiceTest { @Test fun `test capture rule doesn't capture Embrace endpoints`() { - configService.appId = "o0o0o" - val rule = getDefaultRule(urlRegex = "https://a-o0o0o.data.emb-api.com") + val rule = getDefaultRule(urlRegex = "https://a-abcde.data.emb-api.com/api/v2") cfg = RemoteConfig(networkCaptureRules = setOf(rule)) - val result = getService().getNetworkCaptureRules("https://a-o0o0o.data.emb-api.com", "GET") + val result = getService().getNetworkCaptureRules("https://a-abcde.data.emb-api.com/api/v2/spans", "GET") assertEquals(0, result.size) } @@ -195,6 +196,11 @@ internal class EmbraceNetworkCaptureServiceTest { preferenceService, { networkCaptureDataSource }, configService, + EmbraceApiUrlBuilder( + "deviceId", + "1.0.0", + FakeInstrumentedConfig() + ), EmbraceSerializer(), EmbLoggerImpl() ) diff --git a/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt b/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt index 7372fb568f..2719d154bf 100644 --- a/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt +++ b/embrace-android-delivery/src/main/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionService.kt @@ -79,7 +79,7 @@ class OkHttpRequestExecutionService( } private fun Endpoint.getApiRequestFromEndpoint(): ApiRequestV2 = ApiRequestV2( - url = "$coreBaseUrl/v2/${this.path}", + url = "$coreBaseUrl${this.path}", appId = appId, deviceId = lazyDeviceId.value, contentEncoding = "gzip", diff --git a/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt b/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt index bf7a9d1f76..f9666ba45d 100644 --- a/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt +++ b/embrace-android-delivery/src/test/kotlin/io/embrace/android/embracesdk/internal/delivery/execution/OkHttpRequestExecutionServiceTest.kt @@ -48,7 +48,7 @@ class OkHttpRequestExecutionServiceTest { protocols = listOf(Protocol.HTTP_2, Protocol.HTTP_1_1) start() } - testServerUrl = server.url("").toString().removeSuffix("/") + testServerUrl = server.url("").toString() requestExecutionService = OkHttpRequestExecutionService( coreBaseUrl = testServerUrl, lazyDeviceId = lazy { testDeviceId }, @@ -69,7 +69,7 @@ class OkHttpRequestExecutionServiceTest { fun `return incomplete if the server does not exist`() { // given a request execution service with a non existent url requestExecutionService = OkHttpRequestExecutionService( - coreBaseUrl = "https://nonexistenturl:1565", + coreBaseUrl = "https://nonexistenturl:1565/", lazyDeviceId = lazy { testDeviceId }, appId = testAppId, embraceVersionName = testEmbraceVersionName, diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/Endpoint.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/Endpoint.kt index 30296e0ecc..be2e2c46b4 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/Endpoint.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/Endpoint.kt @@ -9,5 +9,6 @@ enum class Endpoint( ) { LOGS("logs", "v2"), SESSIONS("spans", "v2"), - UNKNOWN("unknown", "v1") + CONFIG("config", "v2"), + UNKNOWN("unknown", "v1"), } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index b43e3ea152..c241db45b5 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -101,9 +101,10 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( deliveryServiceProvider = deliveryServiceProvider ) }, - configModuleSupplier = { initModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, foregroundAction, _ -> + configModuleSupplier = { initModule, coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, foregroundAction, _ -> createConfigModule( initModule, + coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index b2453e13a6..fd3ade781a 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -142,6 +142,7 @@ internal class ModuleInitBootstrapper( configModule = init(ConfigModule::class) { configModuleSupplier( initModule, + coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 13e234ddab..e400e4f227 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -43,7 +43,7 @@ internal fun fakeModuleInitBootstrapper( workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, - configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, + configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _, _ -> FakeConfigModule() }, dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/EssentialServiceModuleImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/EssentialServiceModuleImplTest.kt index 54181c55e2..8563ce8ebe 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/EssentialServiceModuleImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/EssentialServiceModuleImplTest.kt @@ -40,7 +40,6 @@ internal class EssentialServiceModuleImplTest { ) assertNotNull(module.processStateService) - assertNotNull(module.urlBuilder) assertNotNull(module.apiClient) assertNotNull(module.apiService) assertNotNull(module.activityLifecycleTracker) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt index 855214923e..2b07fd0fca 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt @@ -32,7 +32,7 @@ internal class ModuleInitBootstrapperTest { logger = EmbLoggerImpl() coreModule = FakeCoreModule() moduleInitBootstrapper = ModuleInitBootstrapper( - configModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, + configModuleSupplier = { _, _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, coreModuleSupplier = { _, _ -> coreModule }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> FakeNativeFeatureModule() }, logger = logger diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt index 3e8b00dbd1..0c04b4a055 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt @@ -17,8 +17,6 @@ import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl @@ -102,13 +100,6 @@ fun createSdkModeBehavior( remoteCfg: Provider = { null }, ): SdkModeBehavior = SdkModeBehaviorImpl(thresholdCheck, remoteCfg) -/** - * A [SdkModeBehaviorImpl] that returns default values. - */ -fun createSdkEndpointBehavior( - thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, -): SdkEndpointBehavior = SdkEndpointBehaviorImpl(thresholdCheck, InstrumentedConfigImpl) - /** * A [AppExitInfoBehavior] that returns default values. */ diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt index 821eb6676d..75e2607e7e 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeApiUrlBuilder.kt @@ -1,15 +1,12 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder +import io.embrace.android.embracesdk.internal.comms.api.Endpoint class FakeApiUrlBuilder( - private val config: String = "", - private val other: String = "", override val appId: String = "", override val deviceId: String = "", + override val baseDataUrl: String = "", ) : ApiUrlBuilder { - - override fun getConfigUrl(): String = config - - override fun getEmbraceUrlWithSuffix(apiVersion: String, suffix: String): String = other + override fun resolveUrl(endpoint: Endpoint): String = "" } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt index 7cd996fe1b..c5ba2ba6cc 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigModule.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.fakes.behavior.FakeNetworkBehavior +import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.RemoteConfigSource import io.embrace.android.embracesdk.internal.injection.ConfigModule @@ -11,5 +12,6 @@ class FakeConfigModule( captureHttpUrlConnectionRequests = false ) ), - override val remoteConfigSource: RemoteConfigSource = FakeRemoteConfigSource() + override val remoteConfigSource: RemoteConfigSource = FakeRemoteConfigSource(), + override val urlBuilder: ApiUrlBuilder = FakeApiUrlBuilder(), ) : ConfigModule diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt index 5b8152eb3d..21323a17cb 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt @@ -10,7 +10,6 @@ import io.embrace.android.embracesdk.internal.config.behavior.DataCaptureEventBe import io.embrace.android.embracesdk.internal.config.behavior.LogMessageBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior import io.embrace.android.embracesdk.internal.config.behavior.SessionBehavior @@ -35,7 +34,6 @@ class FakeConfigService( override var networkBehavior: NetworkBehavior = createNetworkBehavior(), override var dataCaptureEventBehavior: DataCaptureEventBehavior = createDataCaptureEventBehavior(), override var sdkModeBehavior: SdkModeBehavior = createSdkModeBehavior(), - override var sdkEndpointBehavior: SdkEndpointBehavior = createSdkEndpointBehavior(), override var webViewVitalsBehavior: WebViewVitalsBehavior = createWebViewVitalsBehavior(), override var appExitInfoBehavior: AppExitInfoBehavior = createAppExitInfoBehavior(), override var networkSpanForwardingBehavior: NetworkSpanForwardingBehavior = createNetworkSpanForwardingBehavior(), diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkEndpointBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkEndpointBehavior.kt deleted file mode 100644 index 5752a15f88..0000000000 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkEndpointBehavior.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.embrace.android.embracesdk.fakes.behavior - -import io.embrace.android.embracesdk.internal.config.behavior.SdkEndpointBehavior - -class FakeSdkEndpointBehavior( - private val dataEndpoint: String, - private val configEndpoint: String, -) : SdkEndpointBehavior { - override fun getData(appId: String?): String = dataEndpoint - override fun getConfig(appId: String?): String = configEndpoint -} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeEssentialServiceModule.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeEssentialServiceModule.kt index a93e288bf7..d9aa61f38d 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeEssentialServiceModule.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/injection/FakeEssentialServiceModule.kt @@ -3,7 +3,6 @@ package io.embrace.android.embracesdk.fakes.injection import io.embrace.android.embracesdk.fakes.FakeActivityTracker import io.embrace.android.embracesdk.fakes.FakeApiClient import io.embrace.android.embracesdk.fakes.FakeApiService -import io.embrace.android.embracesdk.fakes.FakeApiUrlBuilder import io.embrace.android.embracesdk.fakes.FakeLogWriter import io.embrace.android.embracesdk.fakes.FakeNetworkConnectivityService import io.embrace.android.embracesdk.fakes.FakePendingApiCallsSender @@ -16,7 +15,6 @@ import io.embrace.android.embracesdk.internal.capture.connectivity.NetworkConnec import io.embrace.android.embracesdk.internal.capture.user.UserService import io.embrace.android.embracesdk.internal.comms.api.ApiClient import io.embrace.android.embracesdk.internal.comms.api.ApiService -import io.embrace.android.embracesdk.internal.comms.api.ApiUrlBuilder import io.embrace.android.embracesdk.internal.comms.delivery.PendingApiCallsSender import io.embrace.android.embracesdk.internal.injection.EssentialServiceModule import io.embrace.android.embracesdk.internal.session.id.SessionIdTracker @@ -32,7 +30,6 @@ class FakeEssentialServiceModule( override val apiService: ApiService = FakeApiService(), override val networkConnectivityService: NetworkConnectivityService = FakeNetworkConnectivityService(), override val pendingApiCallsSender: PendingApiCallsSender = FakePendingApiCallsSender(), - override val urlBuilder: ApiUrlBuilder = FakeApiUrlBuilder(), override val logWriter: LogWriter = FakeLogWriter(), override val sessionPropertiesService: FakeSessionPropertiesService = FakeSessionPropertiesService(), ) : EssentialServiceModule From d5f637a1c43155e72a18a8e34d0eac89870f5e21 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Wed, 13 Nov 2024 11:49:05 +0000 Subject: [PATCH 21/28] fix build --- .../embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt index 2c90799d0d..a3f2868d2f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/OkHttpRemoteConfigSourceTest.kt @@ -34,8 +34,6 @@ class OkHttpRemoteConfigSourceTest { private val remoteConfig = RemoteConfig( backgroundActivityConfig = BackgroundActivityRemoteConfig(100f) ) - private val configResponse = TestPlatformSerializer().toJson(remoteConfig) - private lateinit var configResponseBuffer: Buffer @Before From 50e5a39742cb4f8a1493da22176896483492c736 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Wed, 13 Nov 2024 16:42:16 +0000 Subject: [PATCH 22/28] test: add integration tests for config disabling sdk --- .../internal/injection/ConfigModuleImpl.kt | 4 +- .../injection/ConfigModuleSupplier.kt | 5 -- .../testcases/EmbraceInternalInterfaceTest.kt | 4 +- .../testcases/NetworkRequestApiTest.kt | 2 +- .../embracesdk/testcases/PublicApiTest.kt | 2 +- .../features/BreadcrumbFeatureTest.kt | 2 +- .../testcases/features/JvmCrashFeatureTest.kt | 2 +- .../testcases/features/RemoteConfigTest.kt | 31 +++++++++++- .../features/ResurrectionFeatureTest.kt | 4 +- .../features/V1DeliveryFeatureTest.kt | 10 ++-- .../session/BackgroundActivityTest.kt | 2 +- .../testcases/session/ManualSessionTest.kt | 4 +- .../session/OtelSessionGatingTest.kt | 4 +- .../testframework/IntegrationTestRule.kt | 30 ++++++++++-- .../EmbracePayloadAssertionInterface.kt | 48 +++++++++++++++++++ .../actions/EmbracePreSdkStartInterface.kt | 16 ++++++- .../actions/EmbraceSetupInterface.kt | 18 ------- .../testframework/server/FakeApiServer.kt | 20 ++++++-- .../injection/ModuleInitBootstrapper.kt | 2 +- .../fakes/FakeModuleInitBootstrapper.kt | 2 +- .../injection/ModuleInitBootstrapperTest.kt | 2 +- 21 files changed, 155 insertions(+), 59 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index 5d465b0343..ccf63b526c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -10,7 +10,6 @@ import io.embrace.android.embracesdk.internal.config.source.OkHttpRemoteConfigSo import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStoreImpl import io.embrace.android.embracesdk.internal.payload.AppFramework -import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.internal.worker.Worker import okhttp3.OkHttpClient import okhttp3.Protocol @@ -24,7 +23,6 @@ internal class ConfigModuleImpl( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - remoteConfigStoreProvider: Provider = { null }, ) : ConfigModule { companion object { @@ -72,7 +70,7 @@ internal class ConfigModuleImpl( } override val remoteConfigStore: RemoteConfigStore by singleton { - remoteConfigStoreProvider() ?: RemoteConfigStoreImpl( + RemoteConfigStoreImpl( serializer = initModule.jsonSerializer, storageDir = File(coreModule.context.filesDir, "embrace_remote_config"), ) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt index cec8ca8fb3..3b4037730c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleSupplier.kt @@ -1,8 +1,6 @@ package io.embrace.android.embracesdk.internal.injection -import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore import io.embrace.android.embracesdk.internal.payload.AppFramework -import io.embrace.android.embracesdk.internal.utils.Provider /** * Function that returns an instance of [ConfigModule]. Matches the signature of the constructor for [ConfigModuleImpl] @@ -14,7 +12,6 @@ typealias ConfigModuleSupplier = ( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - remoteConfigStoreProvider: Provider, ) -> ConfigModule fun createConfigModule( @@ -24,7 +21,6 @@ fun createConfigModule( workerThreadModule: WorkerThreadModule, androidServicesModule: AndroidServicesModule, framework: AppFramework, - remoteConfigStoreProvider: Provider, ): ConfigModule = ConfigModuleImpl( initModule, coreModule, @@ -32,5 +28,4 @@ fun createConfigModule( workerThreadModule, androidServicesModule, framework, - remoteConfigStoreProvider ) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt index ec6c2d23d9..aa95aa90a3 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/EmbraceInternalInterfaceTest.kt @@ -203,7 +203,7 @@ internal class EmbraceInternalInterfaceTest { @Test fun `access check methods work as expected`() { testRule.runTest( - remoteConfig = RemoteConfig( + persistedRemoteConfig = RemoteConfig( disabledUrlPatterns = setOf("dontlogmebro.pizza"), networkCaptureRules = setOf( NetworkCaptureRuleRemoteConfig( @@ -305,7 +305,7 @@ internal class EmbraceInternalInterfaceTest { @Test fun `SDK will not start if feature flag has it being disabled`() { testRule.runTest( - remoteConfig = RemoteConfig(threshold = 0), + persistedRemoteConfig = RemoteConfig(threshold = 0), expectSdkToStart = false, testCaseAction = { assertFalse(embrace.isStarted) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt index 043225757c..59a3087d59 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/NetworkRequestApiTest.kt @@ -179,7 +179,7 @@ internal class NetworkRequestApiTest { @Test fun `disabled URLs not recorded`() { testRule.runTest( - remoteConfig = RemoteConfig( + persistedRemoteConfig = RemoteConfig( disabledUrlPatterns = setOf("dontlogmebro.pizza"), networkCaptureRules = setOf( NetworkCaptureRuleRemoteConfig( diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt index c4c8eb7c8a..0470220368 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/PublicApiTest.kt @@ -74,7 +74,7 @@ internal class PublicApiTest { var foregroundSessionId: String? = null var backgroundSessionId: String? = null testRule.runTest( - remoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), + persistedRemoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), instrumentedConfig = instrumentedConfig, testCaseAction = { recordSession { diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/BreadcrumbFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/BreadcrumbFeatureTest.kt index f8794e8f08..6eb6bcdca1 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/BreadcrumbFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/BreadcrumbFeatureTest.kt @@ -25,7 +25,7 @@ internal class BreadcrumbFeatureTest { @Test fun `custom breadcrumb feature`() { testRule.runTest( - remoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), + persistedRemoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), testCaseAction = { recordSession { embrace.addBreadcrumb("Hello, world!") diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/JvmCrashFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/JvmCrashFeatureTest.kt index 37c5dc5937..9c0d21806d 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/JvmCrashFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/JvmCrashFeatureTest.kt @@ -62,7 +62,7 @@ internal class JvmCrashFeatureTest { @Test fun `app crash in the background generates a crash log`() { testRule.runTest( - remoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), + persistedRemoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), testCaseAction = { simulateJvmUncaughtException(testException) }, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/RemoteConfigTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/RemoteConfigTest.kt index 2a5e20ee5d..da0f5e808b 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/RemoteConfigTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/RemoteConfigTest.kt @@ -1,8 +1,10 @@ package io.embrace.android.embracesdk.testcases.features import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.assertions.returnIfConditionMet import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.testframework.IntegrationTestRule +import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Rule @@ -16,6 +18,9 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) internal class RemoteConfigTest { + private val sdkDisabledConfig = RemoteConfig(0) + private val sdkEnabledConfig = RemoteConfig(100) + @Rule @JvmField val testRule: IntegrationTestRule = IntegrationTestRule() @@ -23,12 +28,19 @@ internal class RemoteConfigTest { @Test fun `SDK can start`() { testRule.runTest( - remoteConfig = RemoteConfig(100), + persistedRemoteConfig = sdkEnabledConfig, preSdkStartAction = { + assertNoConfigPersisted() assertFalse(embrace.isStarted) }, testCaseAction = { assertTrue(embrace.isStarted) + }, + assertAction = { + assertConfigRequested(1) + val response = readPersistedConfigResponse() + assertEquals(100, response.cfg?.threshold) + assertEquals("server_etag_value", response.etag) } ) } @@ -36,10 +48,25 @@ internal class RemoteConfigTest { @Test fun `SDK disabled via config cannot start`() { testRule.runTest( - remoteConfig = RemoteConfig(0), + persistedRemoteConfig = sdkDisabledConfig, + serverResponseConfig = sdkEnabledConfig, expectSdkToStart = false, + preSdkStartAction = { + assertNoConfigPersisted() + }, testCaseAction = { assertFalse(embrace.isStarted) + }, + assertAction = { + assertConfigRequested(1) + returnIfConditionMet( + dataProvider = ::readPersistedConfigResponse, + condition = { + val response = readPersistedConfigResponse() + response.cfg?.threshold == 100 && response.etag == "server_etag_value" + }, + desiredValueSupplier = {} + ) } ) } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ResurrectionFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ResurrectionFeatureTest.kt index 939b0b1770..d567b7911b 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ResurrectionFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ResurrectionFeatureTest.kt @@ -4,8 +4,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.getSessionId import io.embrace.android.embracesdk.fakes.FakeNativeCrashService import io.embrace.android.embracesdk.fakes.FakePayloadStorageService -import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior -import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.fakeIncompleteSessionEnvelope import io.embrace.android.embracesdk.fixtures.fakeCachedSessionStoredTelemetryMetadata import io.embrace.android.embracesdk.internal.clock.nanosToMillis @@ -84,7 +82,7 @@ internal class ResurrectionFeatureTest { @Test fun `resurrection attempt with v2 delivery layer off does not crash the SDK`() { testRule.runTest( - remoteConfig = RemoteConfig(killSwitchConfig = KillSwitchRemoteConfig(v2StoragePct = 0f)), + persistedRemoteConfig = RemoteConfig(killSwitchConfig = KillSwitchRemoteConfig(v2StoragePct = 0f)), setupAction = { useMockWebServer = false }, diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/V1DeliveryFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/V1DeliveryFeatureTest.kt index 2e18f3444c..90465f3265 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/V1DeliveryFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/V1DeliveryFeatureTest.kt @@ -7,8 +7,6 @@ import android.content.Context import android.os.Build import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig -import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.KillSwitchRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig @@ -42,7 +40,7 @@ internal class V1DeliveryFeatureTest { @Test fun `v1 session delivery`() { testRule.runTest( - remoteConfig = remoteConfig, + persistedRemoteConfig = remoteConfig, setupAction = { useMockWebServer = false }, @@ -61,7 +59,7 @@ internal class V1DeliveryFeatureTest { @Test fun `v1 background activity delivery`() { testRule.runTest( - remoteConfig = remoteConfig, + persistedRemoteConfig = remoteConfig, setupAction = { useMockWebServer = false }, @@ -79,7 +77,7 @@ internal class V1DeliveryFeatureTest { @Test fun `v1 crash delivery`() { testRule.runTest( - remoteConfig = remoteConfig, + persistedRemoteConfig = remoteConfig, setupAction = { useMockWebServer = false }, @@ -98,7 +96,7 @@ internal class V1DeliveryFeatureTest { @Test fun `v1 log delivery`() { testRule.runTest( - remoteConfig = remoteConfig, + persistedRemoteConfig = remoteConfig, setupAction = { useMockWebServer = false setupFakeAeiData() diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityTest.kt index 5e9d3ee066..003f0d925f 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/BackgroundActivityTest.kt @@ -31,7 +31,7 @@ internal class BackgroundActivityTest { @Test fun `bg activity messages are recorded`() { testRule.runTest( - remoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), + persistedRemoteConfig = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(100f)), testCaseAction = { recordSession() clock.tick(30000) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/ManualSessionTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/ManualSessionTest.kt index 520ac7ebb7..2ab533129c 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/ManualSessionTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/ManualSessionTest.kt @@ -48,7 +48,7 @@ internal class ManualSessionTest { @Test fun `calling endSession when session control enabled does not end sessions`() { testRule.runTest( - remoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig(isEnabled = true)), + persistedRemoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig(isEnabled = true)), testCaseAction = { recordSession { clock.tick(10000) @@ -65,7 +65,7 @@ internal class ManualSessionTest { @Test fun `calling endSession when state session is below 5s has no effect`() { testRule.runTest( - remoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig(isEnabled = true)), + persistedRemoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig(isEnabled = true)), testCaseAction = { recordSession { clock.tick(1000) // not enough to trigger new session diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/OtelSessionGatingTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/OtelSessionGatingTest.kt index ac9ca4240a..759977048b 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/OtelSessionGatingTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/session/OtelSessionGatingTest.kt @@ -34,7 +34,7 @@ internal class OtelSessionGatingTest { @Test fun `session sent in full without gating`() { testRule.runTest( - remoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig()), + persistedRemoteConfig = RemoteConfig(sessionConfig = SessionRemoteConfig()), testCaseAction = { simulateSession() }, @@ -48,7 +48,7 @@ internal class OtelSessionGatingTest { @Test fun `session gated`() { testRule.runTest( - remoteConfig = RemoteConfig( + persistedRemoteConfig = RemoteConfig( sessionConfig = SessionRemoteConfig( sessionComponents = emptySet(), fullSessionEvents = setOf( diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt index 9f55b8ae71..9388068cd4 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt @@ -1,11 +1,13 @@ package io.embrace.android.embracesdk.testframework import android.content.Context +import androidx.test.core.app.ApplicationProvider import io.embrace.android.embracesdk.EmbraceHooks import io.embrace.android.embracesdk.EmbraceImpl import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeDeliveryService +import io.embrace.android.embracesdk.fakes.TestPlatformSerializer import io.embrace.android.embracesdk.fakes.config.FakeBaseUrlConfig import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.injection.FakeCoreModule @@ -25,6 +27,8 @@ import io.embrace.android.embracesdk.testframework.actions.EmbracePreSdkStartInt import io.embrace.android.embracesdk.testframework.actions.EmbraceSetupInterface import io.embrace.android.embracesdk.testframework.export.FilteredSpanExporter import io.embrace.android.embracesdk.testframework.server.FakeApiServer +import java.io.File +import java.util.zip.GZIPOutputStream import okhttp3.Protocol import okhttp3.mockwebserver.MockWebServer import org.junit.Assert.assertEquals @@ -107,7 +111,8 @@ internal class IntegrationTestRule( fun runTest( startSdk: Boolean = true, instrumentedConfig: FakeInstrumentedConfig = FakeInstrumentedConfig(), - remoteConfig: RemoteConfig = RemoteConfig(), + persistedRemoteConfig: RemoteConfig = RemoteConfig(), + serverResponseConfig: RemoteConfig = persistedRemoteConfig, expectSdkToStart: Boolean = startSdk, setupAction: EmbraceSetupInterface.() -> Unit = {}, preSdkStartAction: EmbracePreSdkStartInterface.() -> Unit = {}, @@ -119,7 +124,7 @@ internal class IntegrationTestRule( var apiServer: FakeApiServer? = null if (setup.useMockWebServer) { - apiServer = FakeApiServer(remoteConfig) + apiServer = FakeApiServer(serverResponseConfig) val server: MockWebServer = MockWebServer().apply { protocols = listOf(Protocol.HTTP_2, Protocol.HTTP_1_1) dispatcher = apiServer @@ -129,7 +134,7 @@ internal class IntegrationTestRule( } preSdkStart = EmbracePreSdkStartInterface(setup) - bootstrapper = setup.createBootstrapper(prepareConfig(instrumentedConfig), remoteConfig) + bootstrapper = setup.createBootstrapper(prepareConfig(instrumentedConfig)) action = EmbraceActionInterface(setup, bootstrapper) payloadAssertion = EmbracePayloadAssertionInterface(bootstrapper, apiServer) spanExporter = FilteredSpanExporter() @@ -142,6 +147,9 @@ internal class IntegrationTestRule( preSdkStartAction(preSdkStart) embraceImpl.addSpanExporter(spanExporter) + // persist config here before the SDK starts up + persistConfig(persistedRemoteConfig) + if (startSdk) { embraceImpl.start(overriddenCoreModule.context) assertEquals( @@ -157,6 +165,22 @@ internal class IntegrationTestRule( otelExportAssertion(otelAssertion) } + /** + * Writes a config response to the expected location on disk so the SDK can read it. + */ + private fun persistConfig(persistedRemoteConfig: RemoteConfig) { + val ctx = ApplicationProvider.getApplicationContext() + val storageDir = File(ctx.filesDir, "embrace_remote_config").apply { + mkdirs() + } + File(storageDir, "etag").writeText("persisted_etag") + val responseFile = File(storageDir, "most_recent_response") + + GZIPOutputStream(responseFile.outputStream().buffered()).use { stream -> + TestPlatformSerializer().toJson(persistedRemoteConfig, RemoteConfig::class.java, stream) + } + } + private fun prepareConfig(instrumentedConfig: FakeInstrumentedConfig) = when { setup.useMockWebServer -> instrumentedConfig.copy( diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt index 6e25e0bf52..3297527a05 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt @@ -1,11 +1,15 @@ package io.embrace.android.embracesdk.testframework.actions +import android.content.Context +import androidx.test.core.app.ApplicationProvider import io.embrace.android.embracesdk.ResourceReader import io.embrace.android.embracesdk.assertions.findSessionSpan import io.embrace.android.embracesdk.assertions.getSessionId import io.embrace.android.embracesdk.assertions.returnIfConditionMet import io.embrace.android.embracesdk.fakes.FakeDeliveryService import io.embrace.android.embracesdk.fakes.FakeNativeCrashService +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse import io.embrace.android.embracesdk.internal.injection.ModuleInitBootstrapper import io.embrace.android.embracesdk.internal.opentelemetry.embCleanExit import io.embrace.android.embracesdk.internal.opentelemetry.embState @@ -16,9 +20,11 @@ import io.embrace.android.embracesdk.internal.payload.SessionPayload import io.embrace.android.embracesdk.internal.spans.findAttributeValue import io.embrace.android.embracesdk.testframework.assertions.JsonComparator import io.embrace.android.embracesdk.testframework.server.FakeApiServer +import java.io.File import java.io.IOException import java.util.Locale import java.util.concurrent.TimeoutException +import java.util.zip.GZIPInputStream import org.json.JSONObject import org.junit.Assert @@ -33,6 +39,9 @@ internal class EmbracePayloadAssertionInterface( companion object { private const val WAIT_TIME_MS = 10000 + private const val CONFIG_OUTPUT_DIR = "embrace_remote_config" + private const val CONFIG_RESPONSE_FILE = "most_recent_response" + private const val CONFIG_ETAG_FILE = "etag" } private val deliveryService by lazy { bootstrapper.deliveryModule.deliveryService as FakeDeliveryService } @@ -190,6 +199,45 @@ internal class EmbracePayloadAssertionInterface( } } + /** + * Asserts that config was persisted on disk and returns the persisted information. + */ + internal fun readPersistedConfigResponse(): ConfigHttpResponse { + val ctx = ApplicationProvider.getApplicationContext() + val storageDir = File(ctx.filesDir, "embrace_remote_config") + if (!storageDir.exists()) { + throw IllegalStateException("Config storage directory does not exist.") + } + val responseFile = File(storageDir, "most_recent_response") + if (!responseFile.exists()) { + throw IllegalStateException("Config response file does not exist.") + } + val etagFile = File(storageDir, "etag") + if (!etagFile.exists()) { + throw IllegalStateException("Config etag file does not exist.") + } + val remoteConfig = readRemoteConfigFile(responseFile) + return ConfigHttpResponse(remoteConfig, readEtagFile(etagFile)) + } + + private fun readRemoteConfigFile(file: File): RemoteConfig { + try { + return GZIPInputStream(file.inputStream().buffered()).use { + serializer.fromJson(it, RemoteConfig::class.java) + } + } catch (exc: Throwable) { + throw IllegalStateException("Failed to read remote config file.", exc) + } + } + + private fun readEtagFile(etagFile: File): String { + try { + return etagFile.readText() + } catch (exc: Throwable) { + throw IllegalStateException("Failed to read etag file for config.", exc) + } + } + /*** Native ***/ internal fun getSentNativeCrashes() = nativeCrashService.nativeCrashesSent.toList() diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePreSdkStartInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePreSdkStartInterface.kt index 2c02d206b7..a78a8f341e 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePreSdkStartInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePreSdkStartInterface.kt @@ -1,7 +1,10 @@ package io.embrace.android.embracesdk.testframework.actions +import android.content.Context +import androidx.test.core.app.ApplicationProvider import io.embrace.android.embracesdk.Embrace import io.embrace.android.embracesdk.fakes.FakeClock +import java.io.File internal class EmbracePreSdkStartInterface( private val setup: EmbraceSetupInterface, @@ -10,4 +13,15 @@ internal class EmbracePreSdkStartInterface( val clock: FakeClock get() = setup.overriddenClock -} \ No newline at end of file + + /** + * Asserts that no config has been persisted on disk yet. + */ + internal fun assertNoConfigPersisted() { + val ctx = ApplicationProvider.getApplicationContext() + val storageDir = File(ctx.filesDir, "embrace_remote_config") + if (storageDir.exists()) { + throw IllegalStateException("Did not expect config storage directory to exist.") + } + } +} diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt index 7c5666eb2b..3283327aed 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceSetupInterface.kt @@ -7,8 +7,6 @@ import io.embrace.android.embracesdk.fakes.FakeDeliveryService import io.embrace.android.embracesdk.fakes.FakeEmbLogger import io.embrace.android.embracesdk.fakes.FakeNativeFeatureModule import io.embrace.android.embracesdk.fakes.FakeNetworkConnectivityService -import io.embrace.android.embracesdk.fakes.FakeRemoteConfigSource -import io.embrace.android.embracesdk.fakes.FakeRemoteConfigStore import io.embrace.android.embracesdk.fakes.FakeRequestExecutionService import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig import io.embrace.android.embracesdk.fakes.injection.FakeAnrModule @@ -16,8 +14,6 @@ import io.embrace.android.embracesdk.fakes.injection.FakeCoreModule import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeNativeCoreModule import io.embrace.android.embracesdk.internal.comms.delivery.DeliveryService -import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse import io.embrace.android.embracesdk.internal.delivery.execution.RequestExecutionService import io.embrace.android.embracesdk.internal.delivery.storage.PayloadStorageService import io.embrace.android.embracesdk.internal.injection.AndroidServicesModule @@ -26,7 +22,6 @@ import io.embrace.android.embracesdk.internal.injection.ModuleInitBootstrapper import io.embrace.android.embracesdk.internal.injection.OpenTelemetryModule import io.embrace.android.embracesdk.internal.injection.WorkerThreadModule import io.embrace.android.embracesdk.internal.injection.createAndroidServicesModule -import io.embrace.android.embracesdk.internal.injection.createConfigModule import io.embrace.android.embracesdk.internal.injection.createDeliveryModule import io.embrace.android.embracesdk.internal.injection.createEssentialServiceModule import io.embrace.android.embracesdk.internal.injection.createWorkerThreadModule @@ -57,7 +52,6 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( ) { fun createBootstrapper( instrumentedConfig: FakeInstrumentedConfig, - remoteConfig: RemoteConfig, ): ModuleInitBootstrapper = ModuleInitBootstrapper( initModule = overriddenInitModule.apply { this.instrumentedConfig = instrumentedConfig @@ -103,18 +97,6 @@ internal class EmbraceSetupInterface @JvmOverloads constructor( deliveryServiceProvider = deliveryServiceProvider ) }, - configModuleSupplier = { initModule, coreModule, openTelemetryModule, workerThreadModule, androidServicesModule, appFramework, _ -> - createConfigModule( - initModule, - coreModule, - openTelemetryModule, - workerThreadModule, - androidServicesModule, - appFramework, - ) { - FakeRemoteConfigStore(ConfigHttpResponse(remoteConfig, null)) - } - }, anrModuleSupplier = { _, _, _ -> fakeAnrModule }, nativeCoreModuleSupplier = { FakeNativeCoreModule() }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> fakeNativeFeatureModule } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt index 6c852cedaa..dc055162d9 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/server/FakeApiServer.kt @@ -12,6 +12,9 @@ import java.util.zip.GZIPInputStream import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest +import okio.Buffer +import okio.GzipSink +import okio.buffer import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -30,9 +33,6 @@ internal class FakeApiServer(private val remoteConfig: RemoteConfig) : Dispatche private val sessionRequests = ConcurrentLinkedQueue>() private val logRequests = ConcurrentLinkedQueue>() private val configRequests = ConcurrentLinkedQueue() - private val configResponse by lazy { - serializer.toJson(remoteConfig) - } /** * Returns a list of session envelopes in the order in which the server received them. @@ -77,7 +77,19 @@ internal class FakeApiServer(private val remoteConfig: RemoteConfig) : Dispatche private fun handleConfigRequest(request: RecordedRequest): MockResponse { configRequests.add(request.requestUrl?.toUrl()?.query) - return MockResponse().setBody(configResponse).setResponseCode(200) + + // serialize the config response + val configResponseBuffer = Buffer() + val gzipSink = GzipSink(configResponseBuffer).buffer() + serializer.toJson( + remoteConfig, + RemoteConfig::class.java, + gzipSink.outputStream() + ) + return MockResponse() + .setBody(configResponseBuffer) + .addHeader("etag", "server_etag_value") + .setResponseCode(200) } private fun validateHeaders(headers: Map) { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index a471968a8b..c4d371cedf 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -145,7 +145,7 @@ internal class ModuleInitBootstrapper( workerThreadModule, androidServicesModule, appFramework - ) { null } + ) } Systrace.traceSynchronous("sdk-disable-check") { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt index 13e234ddab..2f1a869bfe 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeModuleInitBootstrapper.kt @@ -43,7 +43,7 @@ internal fun fakeModuleInitBootstrapper( workerThreadModuleSupplier: WorkerThreadModuleSupplier = { FakeWorkerThreadModule() }, storageModuleSupplier: StorageModuleSupplier = { _, _, _ -> FakeStorageModule() }, essentialServiceModuleSupplier: EssentialServiceModuleSupplier = { _, _, _, _, _, _, _, _, _, _ -> FakeEssentialServiceModule() }, - configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule() }, + configModuleSupplier: ConfigModuleSupplier = { _, _, _, _, _, _ -> FakeConfigModule() }, dataSourceModuleSupplier: DataSourceModuleSupplier = { _, _ -> FakeDataSourceModule() }, dataCaptureServiceModuleSupplier: DataCaptureServiceModuleSupplier = { _, _, _, _, _, _ -> FakeDataCaptureServiceModule() }, deliveryModuleSupplier: DeliveryModuleSupplier = { _, _, _, _, _, _, _, _, _, _, _, _ -> FakeDeliveryModule() }, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt index 855214923e..77801b3b23 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/injection/ModuleInitBootstrapperTest.kt @@ -32,7 +32,7 @@ internal class ModuleInitBootstrapperTest { logger = EmbLoggerImpl() coreModule = FakeCoreModule() moduleInitBootstrapper = ModuleInitBootstrapper( - configModuleSupplier = { _, _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, + configModuleSupplier = { _, _, _, _, _, _ -> FakeConfigModule(FakeConfigService()) }, coreModuleSupplier = { _, _ -> coreModule }, nativeFeatureModuleSupplier = { _, _, _, _, _, _, _, _, _ -> FakeNativeFeatureModule() }, logger = logger From a81794fc05bc896f1c53993a0b23ede6e888dd0e Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Wed, 13 Nov 2024 17:00:25 +0000 Subject: [PATCH 23/28] refactor: store payload uncompressed --- .../internal/config/store/RemoteConfigStoreImpl.kt | 6 ++---- .../android/embracesdk/testframework/IntegrationTestRule.kt | 3 +-- .../actions/EmbracePayloadAssertionInterface.kt | 3 +-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt index ee237f9b44..51f2cab38a 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt @@ -4,8 +4,6 @@ import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.source.ConfigHttpResponse import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer import java.io.File -import java.util.zip.GZIPInputStream -import java.util.zip.GZIPOutputStream internal class RemoteConfigStoreImpl( private val serializer: PlatformSerializer, @@ -26,7 +24,7 @@ internal class RemoteConfigStoreImpl( override fun loadResponse(): ConfigHttpResponse? { try { - val cfg = GZIPInputStream(configFile.inputStream().buffered()).use { + val cfg = configFile.inputStream().buffered().use { serializer.fromJson(it, RemoteConfig::class.java) } return ConfigHttpResponse( @@ -42,7 +40,7 @@ internal class RemoteConfigStoreImpl( override fun saveResponse(response: ConfigHttpResponse) { try { - GZIPOutputStream(configFile.outputStream().buffered()).use { stream -> + configFile.outputStream().buffered().use { stream -> serializer.toJson(response.cfg, RemoteConfig::class.java, stream) } response.etag?.let(etagFile::writeText) diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt index 9388068cd4..1640f41aab 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/IntegrationTestRule.kt @@ -28,7 +28,6 @@ import io.embrace.android.embracesdk.testframework.actions.EmbraceSetupInterface import io.embrace.android.embracesdk.testframework.export.FilteredSpanExporter import io.embrace.android.embracesdk.testframework.server.FakeApiServer import java.io.File -import java.util.zip.GZIPOutputStream import okhttp3.Protocol import okhttp3.mockwebserver.MockWebServer import org.junit.Assert.assertEquals @@ -176,7 +175,7 @@ internal class IntegrationTestRule( File(storageDir, "etag").writeText("persisted_etag") val responseFile = File(storageDir, "most_recent_response") - GZIPOutputStream(responseFile.outputStream().buffered()).use { stream -> + responseFile.outputStream().buffered().use { stream -> TestPlatformSerializer().toJson(persistedRemoteConfig, RemoteConfig::class.java, stream) } } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt index 3297527a05..0c546b7cc1 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbracePayloadAssertionInterface.kt @@ -24,7 +24,6 @@ import java.io.File import java.io.IOException import java.util.Locale import java.util.concurrent.TimeoutException -import java.util.zip.GZIPInputStream import org.json.JSONObject import org.junit.Assert @@ -222,7 +221,7 @@ internal class EmbracePayloadAssertionInterface( private fun readRemoteConfigFile(file: File): RemoteConfig { try { - return GZIPInputStream(file.inputStream().buffered()).use { + return file.inputStream().buffered().use { serializer.fromJson(it, RemoteConfig::class.java) } } catch (exc: Throwable) { From 4bb7b8ad93ec54d00d8b927f3005c8919c6fbe7f Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Wed, 13 Nov 2024 17:59:33 +0000 Subject: [PATCH 24/28] perf: systrace improvements --- .../source/CombinedRemoteConfigSource.kt | 25 +++++++++++++------ .../injection/ModuleInitBootstrapper.kt | 10 +++++--- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt index ded006a02b..27fe0abaa6 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.config.source +import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.store.RemoteConfigStore import io.embrace.android.embracesdk.internal.worker.BackgroundWorker @@ -13,18 +14,26 @@ class CombinedRemoteConfigSource( ) { // the remote config that is used for the lifetime of the process. - private val response by lazy { store.loadResponse() } + private val response by lazy { + Systrace.traceSynchronous("load-config-from-store") { + store.loadResponse() + } + } fun getConfig(): RemoteConfig? = response?.cfg fun scheduleConfigRequests() { - response?.etag?.let(httpSource::setInitialEtag) - worker.scheduleWithFixedDelay( - ::attemptConfigRequest, - 0, - intervalMs, - TimeUnit.MILLISECONDS - ) + Systrace.traceSynchronous("set-initial-etag") { + response?.etag?.let(httpSource::setInitialEtag) + } + Systrace.traceSynchronous("schedule-http-request") { + worker.scheduleWithFixedDelay( + ::attemptConfigRequest, + 0, + intervalMs, + TimeUnit.MILLISECONDS + ) + } } private fun attemptConfigRequest() { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index a471968a8b..d57b0b94b6 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -150,10 +150,14 @@ internal class ModuleInitBootstrapper( Systrace.traceSynchronous("sdk-disable-check") { // kick off config HTTP request first so the SDK can't get in a permanently disabled state - configModule.combinedRemoteConfigSource?.scheduleConfigRequests() + Systrace.traceSynchronous("load-config-response") { + configModule.combinedRemoteConfigSource?.scheduleConfigRequests() + } - if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { - return false + Systrace.traceSynchronous("behavior-check") { + if (configModule.configService.sdkModeBehavior.isSdkDisabled()) { + return false + } } } From 4bac7e0960eefe5c29ffe60d8b341e0417a7e5ce Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Wed, 13 Nov 2024 17:22:51 +0000 Subject: [PATCH 25/28] refactor: purge config --- .../config/source/CombinedRemoteConfigSource.kt | 4 +++- .../internal/config/store/RemoteConfigStoreImpl.kt | 13 +++++++++++++ .../config/source/CombinedRemoteConfigSourceTest.kt | 1 + .../config/store/RemoteConfigStoreImplTest.kt | 4 ++++ .../embracesdk/fakes/FakeRemoteConfigStore.kt | 3 +++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt index 27fe0abaa6..d2b96a877d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt @@ -37,6 +37,8 @@ class CombinedRemoteConfigSource( } private fun attemptConfigRequest() { - httpSource.getConfig()?.let(store::saveResponse) + httpSource.getConfig()?.let { + store.saveResponse(it) + } } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt index 51f2cab38a..b72c82d1ca 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImpl.kt @@ -44,6 +44,19 @@ internal class RemoteConfigStoreImpl( serializer.toJson(response.cfg, RemoteConfig::class.java, stream) } response.etag?.let(etagFile::writeText) + } catch (exc: Exception) { + // paranoia: purge the cache + // to avoid the possibility of getting trapped with stale config + // where the SDK is disabled & persistence fails. In that scenario we prefer + // the default SDK behavior which will fetch the correct config eventually + purgeCache() + } + } + + private fun purgeCache() { + try { + configFile.delete() + etagFile.delete() } catch (ignored: Exception) { } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt index 5d055b94cf..d3d8983ecd 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt @@ -64,5 +64,6 @@ class CombinedRemoteConfigSourceTest { assertEquals("etag", remoteConfigSource.etag) executorService.runCurrentlyBlocked() assertEquals(1, remoteConfigSource.callCount) + assertEquals(1, remoteConfigStore.saveCount) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt index a99a263e06..6cd61be8df 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/store/RemoteConfigStoreImplTest.kt @@ -40,5 +40,9 @@ internal class RemoteConfigStoreImplTest { val newLoaded = checkNotNull(store.loadResponse()) assertEquals(newConfig, newLoaded.cfg) assertEquals("another", newLoaded.etag) + + store.saveResponse(ConfigHttpResponse(newConfig, "another")) + assertEquals(newConfig, newLoaded.cfg) + assertEquals("another", newLoaded.etag) } } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt index 250dbbee3d..9974210a4b 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeRemoteConfigStore.kt @@ -7,9 +7,12 @@ class FakeRemoteConfigStore( var impl: ConfigHttpResponse? = null, ) : RemoteConfigStore { + var saveCount: Int = 0 + override fun loadResponse(): ConfigHttpResponse? = impl override fun saveResponse(response: ConfigHttpResponse) { + saveCount++ impl = response } } From 35e8b528d771dfa54b1f94d61532d24258561a79 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Thu, 14 Nov 2024 12:03:10 +0000 Subject: [PATCH 26/28] simplify config behavior implementation --- .../internal/config/ConfigServiceImpl.kt | 111 +++--------------- .../internal/config/behavior/AnrBehavior.kt | 4 +- .../config/behavior/AnrBehaviorImpl.kt | 20 ++-- .../config/behavior/AppExitInfoBehavior.kt | 5 +- .../behavior/AppExitInfoBehaviorImpl.kt | 25 ++-- .../behavior/AutoDataCaptureBehavior.kt | 5 +- .../behavior/AutoDataCaptureBehaviorImpl.kt | 33 +++--- .../behavior/BackgroundActivityBehavior.kt | 5 +- .../BackgroundActivityBehaviorImpl.kt | 21 ++-- .../config/behavior/BreadcrumbBehavior.kt | 5 +- .../config/behavior/BreadcrumbBehaviorImpl.kt | 25 ++-- .../config/behavior/ConfigBehavior.kt | 17 +++ .../behavior/DataCaptureEventBehavior.kt | 5 +- .../behavior/DataCaptureEventBehaviorImpl.kt | 11 +- .../config/behavior/LogMessageBehavior.kt | 5 +- .../config/behavior/LogMessageBehaviorImpl.kt | 13 +- .../config/behavior/MergedConfigBehavior.kt | 50 -------- .../config/behavior/NetworkBehavior.kt | 4 +- .../config/behavior/NetworkBehaviorImpl.kt | 18 +-- .../behavior/NetworkSpanForwardingBehavior.kt | 5 +- .../NetworkSpanForwardingBehaviorImpl.kt | 21 ++-- .../config/behavior/SdkModeBehavior.kt | 5 +- .../config/behavior/SdkModeBehaviorImpl.kt | 12 +- .../config/behavior/SensitiveKeysBehavior.kt | 5 +- .../behavior/SensitiveKeysBehaviorImpl.kt | 11 +- .../config/behavior/SessionBehavior.kt | 5 +- .../config/behavior/SessionBehaviorImpl.kt | 19 ++- .../config/behavior/WebViewVitalsBehavior.kt | 5 +- .../behavior/WebViewVitalsBehaviorImpl.kt | 12 +- .../internal/injection/ConfigModuleImpl.kt | 15 ++- .../arch/schema/TelemetryAttributesTest.kt | 24 ++-- .../SessionPropertiesServiceImplTest.kt | 6 +- .../internal/config/ConfigServiceImplTest.kt | 4 +- .../config/behavior/AnrBehaviorImplTest.kt | 3 +- .../behavior/AppExitInfoBehaviorImplTest.kt | 2 +- .../AutoDataCaptureBehaviorImplTest.kt | 6 +- .../BackgroundActivityBehaviorImplTest.kt | 3 +- .../behavior/BreadcrumbBehaviorImplTest.kt | 13 +- .../DataCaptureEventBehaviorImplTest.kt | 2 +- .../behavior/LogMessageBehaviorImplTest.kt | 15 ++- .../behavior/NetworkBehaviorImplTest.kt | 4 +- .../NetworkSpanForwardingBehaviorImplTest.kt | 11 +- .../behavior/SdkModeBehaviorImplTest.kt | 8 +- .../behavior/SensitiveKeysBehaviorImplTest.kt | 26 ++-- .../behavior/SessionBehaviorImplImplTest.kt | 10 +- .../config/behavior/WebVitalsBehaviorTest.kt | 2 +- .../EmbraceGatingServiceV2PayloadTest.kt | 4 +- .../internal/logs/EmbraceLogServiceTest.kt | 18 ++- .../EmbraceNetworkCaptureServiceTest.kt | 33 +++--- .../internal/session/PayloadFactoryBaTest.kt | 7 +- .../SessionOrchestrationModuleImplTest.kt | 7 +- .../session/message/PayloadFactoryImplTest.kt | 13 +- .../orchestrator/SessionOrchestratorTest.kt | 19 +-- .../spans/EmbraceSpanFactoryImplTest.kt | 6 +- .../internal/spans/EmbraceSpanImplTest.kt | 6 +- .../detection/LivenessCheckSchedulerTest.kt | 18 +-- .../EmbraceNativeThreadSamplerServiceTest.kt | 15 ++- .../crumbs/BreadcrumbDataSourceTest.kt | 2 +- .../crumbs/WebViewUrlDataSourceTest.kt | 2 +- .../testcases/features/ActivityFeatureTest.kt | 1 - .../android/embracesdk/fakes/Behavior.kt | 53 ++++----- .../embracesdk/fakes/FakeConfigService.kt | 1 + .../fakes/behavior/FakeAnrBehavior.kt | 7 ++ .../fakes/behavior/FakeAppExitInfoBehavior.kt | 7 ++ .../behavior/FakeAutoDataCaptureBehavior.kt | 7 ++ .../{ => behavior}/FakeBreadcrumbBehavior.kt | 10 +- .../fakes/behavior/FakeLogMessageBehavior.kt | 6 + .../fakes/behavior/FakeNetworkBehavior.kt | 8 ++ .../FakeNetworkSpanForwardingBehavior.kt | 7 ++ .../fakes/behavior/FakeSdkModeBehavior.kt | 7 ++ .../fakes/behavior/FakeSessionBehavior.kt | 7 ++ .../behavior/FakeWebViewVitalsBehavior.kt | 6 + 72 files changed, 440 insertions(+), 473 deletions(-) create mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/ConfigBehavior.kt delete mode 100644 embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/MergedConfigBehavior.kt rename embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/{ => behavior}/FakeBreadcrumbBehavior.kt (76%) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt index 5c7cb65e98..24a82b3ccb 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/ConfigServiceImpl.kt @@ -1,38 +1,24 @@ package io.embrace.android.embracesdk.internal.config -import io.embrace.android.embracesdk.internal.config.behavior.AnrBehavior import io.embrace.android.embracesdk.internal.config.behavior.AnrBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.AppExitInfoBehavior import io.embrace.android.embracesdk.internal.config.behavior.AppExitInfoBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.AutoDataCaptureBehavior import io.embrace.android.embracesdk.internal.config.behavior.AutoDataCaptureBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.BackgroundActivityBehavior import io.embrace.android.embracesdk.internal.config.behavior.BackgroundActivityBehaviorImpl import io.embrace.android.embracesdk.internal.config.behavior.BehaviorThresholdCheck -import io.embrace.android.embracesdk.internal.config.behavior.BreadcrumbBehavior import io.embrace.android.embracesdk.internal.config.behavior.BreadcrumbBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.DataCaptureEventBehavior import io.embrace.android.embracesdk.internal.config.behavior.DataCaptureEventBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.LogMessageBehavior import io.embrace.android.embracesdk.internal.config.behavior.LogMessageBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.SessionBehavior import io.embrace.android.embracesdk.internal.config.behavior.SessionBehaviorImpl -import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehavior import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehaviorImpl import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.OpenTelemetryConfiguration import io.embrace.android.embracesdk.internal.payload.AppFramework import io.embrace.android.embracesdk.internal.prefs.PreferencesService -import io.embrace.android.embracesdk.internal.utils.Provider /** * Loads configuration for the app from the Embrace API. @@ -41,89 +27,26 @@ internal class ConfigServiceImpl( openTelemetryCfg: OpenTelemetryConfiguration, preferencesService: PreferencesService, suppliedFramework: AppFramework, - configProvider: Provider, - thresholdCheck: BehaviorThresholdCheck = BehaviorThresholdCheck { preferencesService.deviceIdentifier }, instrumentedConfig: InstrumentedConfig, + remoteConfig: RemoteConfig?, + thresholdCheck: BehaviorThresholdCheck = BehaviorThresholdCheck { preferencesService.deviceIdentifier }, ) : ConfigService { - override val backgroundActivityBehavior: BackgroundActivityBehavior = - BackgroundActivityBehaviorImpl( - thresholdCheck = thresholdCheck, - remoteSupplier = { configProvider()?.backgroundActivityConfig }, - instrumentedConfig = instrumentedConfig - ) - - override val autoDataCaptureBehavior: AutoDataCaptureBehavior = - AutoDataCaptureBehaviorImpl( - thresholdCheck = thresholdCheck, - remoteSupplier = configProvider, - instrumentedConfig = instrumentedConfig - ) - - override val breadcrumbBehavior: BreadcrumbBehavior = - BreadcrumbBehaviorImpl( - thresholdCheck, - remoteSupplier = configProvider, - instrumentedConfig = instrumentedConfig - ) - - override val sensitiveKeysBehavior: SensitiveKeysBehavior = - SensitiveKeysBehaviorImpl(instrumentedConfig = instrumentedConfig) - - override val logMessageBehavior: LogMessageBehavior = - LogMessageBehaviorImpl( - thresholdCheck, - remoteSupplier = { configProvider()?.logConfig } - ) - - override val anrBehavior: AnrBehavior = - AnrBehaviorImpl( - thresholdCheck, - remoteSupplier = { configProvider()?.anrConfig }, - instrumentedConfig = instrumentedConfig - ) - - override val sessionBehavior: SessionBehavior = SessionBehaviorImpl( - thresholdCheck, - remoteSupplier = configProvider, - instrumentedConfig = instrumentedConfig - ) - - override val networkBehavior: NetworkBehavior = - NetworkBehaviorImpl( - thresholdCheck = thresholdCheck, - remoteSupplier = configProvider, - instrumentedConfig = instrumentedConfig - ) - - override val dataCaptureEventBehavior: DataCaptureEventBehavior = DataCaptureEventBehaviorImpl( - thresholdCheck = thresholdCheck, - remoteSupplier = configProvider - ) - - override val sdkModeBehavior: SdkModeBehavior = SdkModeBehaviorImpl( - thresholdCheck = thresholdCheck, - remoteSupplier = configProvider - ) - - override val appExitInfoBehavior: AppExitInfoBehavior = AppExitInfoBehaviorImpl( - thresholdCheck = thresholdCheck, - remoteSupplier = configProvider, - instrumentedConfig = instrumentedConfig - ) - - override val networkSpanForwardingBehavior: NetworkSpanForwardingBehavior = - NetworkSpanForwardingBehaviorImpl( - thresholdCheck = thresholdCheck, - remoteSupplier = { configProvider()?.networkSpanForwardingRemoteConfig }, - instrumentedConfig = instrumentedConfig - ) - - override val webViewVitalsBehavior: WebViewVitalsBehavior = - WebViewVitalsBehaviorImpl( - thresholdCheck = thresholdCheck, - remoteSupplier = configProvider - ) + override val backgroundActivityBehavior = + BackgroundActivityBehaviorImpl(thresholdCheck, instrumentedConfig, remoteConfig) + override val autoDataCaptureBehavior = AutoDataCaptureBehaviorImpl(thresholdCheck, instrumentedConfig, remoteConfig) + override val breadcrumbBehavior = BreadcrumbBehaviorImpl(instrumentedConfig, remoteConfig) + override val sensitiveKeysBehavior = SensitiveKeysBehaviorImpl(instrumentedConfig) + override val logMessageBehavior = LogMessageBehaviorImpl(remoteConfig) + override val anrBehavior = AnrBehaviorImpl(thresholdCheck, instrumentedConfig, remoteConfig) + override val sessionBehavior = SessionBehaviorImpl(instrumentedConfig, remoteConfig) + override val networkBehavior = NetworkBehaviorImpl(instrumentedConfig, remoteConfig) + override val dataCaptureEventBehavior = DataCaptureEventBehaviorImpl(remoteConfig) + override val sdkModeBehavior = SdkModeBehaviorImpl(thresholdCheck, remoteConfig) + override val appExitInfoBehavior = AppExitInfoBehaviorImpl(thresholdCheck, instrumentedConfig, remoteConfig) + override val webViewVitalsBehavior = WebViewVitalsBehaviorImpl(thresholdCheck, remoteConfig) + override val networkSpanForwardingBehavior = + NetworkSpanForwardingBehaviorImpl(thresholdCheck, instrumentedConfig, remoteConfig) override val appId: String? = resolveAppId(instrumentedConfig.project.getAppId(), openTelemetryCfg) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehavior.kt index 9e605a0492..4e52db4b4d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehavior.kt @@ -1,9 +1,11 @@ package io.embrace.android.embracesdk.internal.config.behavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig import io.embrace.android.embracesdk.internal.config.remote.AllowedNdkSampleMethod +import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.Unwinder -interface AnrBehavior { +interface AnrBehavior : ConfigBehavior { /** * Percentage of users for which ANR stack trace capture is enabled. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImpl.kt index 09a0270019..fc956976c9 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImpl.kt @@ -1,23 +1,18 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.AllowedNdkSampleMethod -import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.remote.Unwinder -import io.embrace.android.embracesdk.internal.utils.Provider /** * Provides the behavior that the ANR feature should follow. */ class AnrBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, - private val instrumentedConfig: InstrumentedConfig, -) : AnrBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + private val thresholdCheck: BehaviorThresholdCheck, + local: InstrumentedConfig, + remote: RemoteConfig?, +) : AnrBehavior { private companion object { private const val DEFAULT_ANR_PCT_ENABLED = true @@ -34,6 +29,9 @@ class AnrBehaviorImpl( ) } + override val local = local.enabledFeatures + override val remote = remote?.anrConfig + override fun isAnrCaptureEnabled(): Boolean { return thresholdCheck.isBehaviorEnabled(remote?.pctEnabled) ?: DEFAULT_ANR_PCT_ENABLED @@ -65,7 +63,7 @@ class AnrBehaviorImpl( override fun isUnityAnrCaptureEnabled(): Boolean { return thresholdCheck.isBehaviorEnabled(remote?.pctNativeThreadAnrSamplingEnabled) - ?: instrumentedConfig.enabledFeatures.isUnityAnrCaptureEnabled() + ?: local.isUnityAnrCaptureEnabled() } override fun isNativeThreadAnrSamplingOffsetEnabled(): Boolean = diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehavior.kt index 59891abe99..4dd2ba5f6a 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface AppExitInfoBehavior { +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.AppExitInfoConfig + +interface AppExitInfoBehavior : ConfigBehavior { fun getTraceMaxLimit(): Int diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImpl.kt index 640b471e35..b9d5cbca3c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImpl.kt @@ -1,22 +1,18 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider /** * Provides the behavior that should be followed for select services that automatically * capture data. */ class AppExitInfoBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, - private val instrumentedConfig: InstrumentedConfig, -) : AppExitInfoBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + private val thresholdCheck: BehaviorThresholdCheck, + local: InstrumentedConfig, + remote: RemoteConfig?, +) : AppExitInfoBehavior { + companion object { /** * Max size of bytes to allow capturing AppExitInfo ndk/anr traces @@ -25,14 +21,17 @@ class AppExitInfoBehaviorImpl( const val AEI_MAX_NUM_DEFAULT: Int = 0 // 0 means no limit } + override val local = local.enabledFeatures + override val remote = remote?.appExitInfoConfig + override fun getTraceMaxLimit(): Int = - remote?.appExitInfoConfig?.appExitInfoTracesLimit + remote?.appExitInfoTracesLimit ?: MAX_TRACE_SIZE_BYTES override fun isAeiCaptureEnabled(): Boolean { - return thresholdCheck.isBehaviorEnabled(remote?.appExitInfoConfig?.pctAeiCaptureEnabled) - ?: instrumentedConfig.enabledFeatures.isAeiCaptureEnabled() + return thresholdCheck.isBehaviorEnabled(remote?.pctAeiCaptureEnabled) + ?: local.isAeiCaptureEnabled() } - override fun appExitInfoMaxNum(): Int = remote?.appExitInfoConfig?.aeiMaxNum ?: AEI_MAX_NUM_DEFAULT + override fun appExitInfoMaxNum(): Int = remote?.aeiMaxNum ?: AEI_MAX_NUM_DEFAULT } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehavior.kt index a80d09b792..81641b26ff 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface AutoDataCaptureBehavior { +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +interface AutoDataCaptureBehavior : ConfigBehavior { /** * Returns true if [io.embrace.android.embracesdk.MemoryService] should diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImpl.kt index 3800b0ac1e..bd85048f23 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImpl.kt @@ -1,22 +1,17 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider /** * Provides the behavior that should be followed for select services that automatically * capture data. */ class AutoDataCaptureBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, - instrumentedConfig: InstrumentedConfig, -) : AutoDataCaptureBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + private val thresholdCheck: BehaviorThresholdCheck, + local: InstrumentedConfig, + override val remote: RemoteConfig?, +) : AutoDataCaptureBehavior { private companion object { const val THERMAL_STATUS_ENABLED_DEFAULT = true @@ -24,30 +19,30 @@ class AutoDataCaptureBehaviorImpl( const val USE_OKHTTP_DEFAULT = true } - private val cfg = instrumentedConfig.enabledFeatures + override val local = local.enabledFeatures @Suppress("DEPRECATION") - override fun isMemoryWarningCaptureEnabled(): Boolean = cfg.isMemoryWarningCaptureEnabled() + override fun isMemoryWarningCaptureEnabled(): Boolean = local.isMemoryWarningCaptureEnabled() override fun isThermalStatusCaptureEnabled(): Boolean { return thresholdCheck.isBehaviorEnabled(remote?.dataConfig?.pctThermalStatusEnabled) ?: THERMAL_STATUS_ENABLED_DEFAULT } - override fun isPowerSaveModeCaptureEnabled(): Boolean = cfg.isPowerSaveModeCaptureEnabled() + override fun isPowerSaveModeCaptureEnabled(): Boolean = local.isPowerSaveModeCaptureEnabled() override fun isNetworkConnectivityCaptureEnabled(): Boolean = - cfg.isNetworkConnectivityCaptureEnabled() + local.isNetworkConnectivityCaptureEnabled() - override fun isAnrCaptureEnabled(): Boolean = cfg.isAnrCaptureEnabled() - override fun isJvmCrashCaptureEnabled(): Boolean = cfg.isJvmCrashCaptureEnabled() + override fun isAnrCaptureEnabled(): Boolean = local.isAnrCaptureEnabled() + override fun isJvmCrashCaptureEnabled(): Boolean = local.isJvmCrashCaptureEnabled() override fun isComposeClickCaptureEnabled(): Boolean = - remote?.killSwitchConfig?.jetpackCompose ?: cfg.isComposeClickCaptureEnabled() + remote?.killSwitchConfig?.jetpackCompose ?: local.isComposeClickCaptureEnabled() override fun is3rdPartySigHandlerDetectionEnabled(): Boolean = - remote?.killSwitchConfig?.sigHandlerDetection ?: cfg.is3rdPartySigHandlerDetectionEnabled() + remote?.killSwitchConfig?.sigHandlerDetection ?: local.is3rdPartySigHandlerDetectionEnabled() - override fun isNativeCrashCaptureEnabled(): Boolean = cfg.isNativeCrashCaptureEnabled() - override fun isDiskUsageCaptureEnabled(): Boolean = cfg.isDiskUsageCaptureEnabled() + override fun isNativeCrashCaptureEnabled(): Boolean = local.isNativeCrashCaptureEnabled() + override fun isDiskUsageCaptureEnabled(): Boolean = local.isDiskUsageCaptureEnabled() private val v2StorageImpl by lazy { thresholdCheck.isBehaviorEnabled(remote?.killSwitchConfig?.v2StoragePct) ?: V2_STORAGE_ENABLED_DEFAULT diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehavior.kt index 6eef92ec52..9593815f3b 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface BackgroundActivityBehavior { +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig + +interface BackgroundActivityBehavior : ConfigBehavior { /** * Whether the feature is enabled or not. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImpl.kt index 2ba0f80eba..12e488b02e 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImpl.kt @@ -1,26 +1,25 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig /** * Provides the behavior that the Background Activity feature should follow. */ class BackgroundActivityBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, - private val instrumentedConfig: InstrumentedConfig, -) : BackgroundActivityBehavior, - MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier - ) { + private val thresholdCheck: BehaviorThresholdCheck, + local: InstrumentedConfig, + remote: RemoteConfig?, +) : BackgroundActivityBehavior { + + override val local: EnabledFeatureConfig = local.enabledFeatures + override val remote: BackgroundActivityRemoteConfig? = remote?.backgroundActivityConfig override fun isBackgroundActivityCaptureEnabled(): Boolean { return remote?.threshold?.let(thresholdCheck::isBehaviorEnabled) - ?: instrumentedConfig.enabledFeatures.isBackgroundActivityCaptureEnabled() + ?: local.isBackgroundActivityCaptureEnabled() } override fun getManualBackgroundActivityLimit(): Int = 100 diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehavior.kt index 275313f5d6..2d28ea5845 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface BreadcrumbBehavior { +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +interface BreadcrumbBehavior : ConfigBehavior { fun getCustomBreadcrumbLimit(): Int fun getFragmentBreadcrumbLimit(): Int diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImpl.kt index 5e2f877e41..0e9ae8217c 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImpl.kt @@ -1,22 +1,17 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider /** * Provides the behavior that should be followed for select services that automatically * capture data. */ class BreadcrumbBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, - instrumentedConfig: InstrumentedConfig, -) : BreadcrumbBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + local: InstrumentedConfig, + override val remote: RemoteConfig?, +) : BreadcrumbBehavior { private companion object { @@ -26,7 +21,7 @@ class BreadcrumbBehaviorImpl( const val DEFAULT_BREADCRUMB_LIMIT = 100 } - private val cfg = instrumentedConfig.enabledFeatures + override val local: EnabledFeatureConfig = local.enabledFeatures override fun getCustomBreadcrumbLimit(): Int = remote?.uiConfig?.breadcrumbs ?: DEFAULT_BREADCRUMB_LIMIT @@ -39,16 +34,16 @@ class BreadcrumbBehaviorImpl( remote?.uiConfig?.webViews ?: DEFAULT_BREADCRUMB_LIMIT override fun isViewClickCoordinateCaptureEnabled(): Boolean = - cfg.isViewClickCoordinateCaptureEnabled() + local.isViewClickCoordinateCaptureEnabled() override fun isActivityBreadcrumbCaptureEnabled(): Boolean = - cfg.isActivityBreadcrumbCaptureEnabled() + local.isActivityBreadcrumbCaptureEnabled() override fun isWebViewBreadcrumbCaptureEnabled(): Boolean = - cfg.isWebViewBreadcrumbCaptureEnabled() + local.isWebViewBreadcrumbCaptureEnabled() override fun isWebViewBreadcrumbQueryParamCaptureEnabled(): Boolean = - cfg.isWebViewBreadcrumbQueryParamCaptureEnabled() + local.isWebViewBreadcrumbQueryParamCaptureEnabled() - override fun isFcmPiiDataCaptureEnabled(): Boolean = cfg.isFcmPiiDataCaptureEnabled() + override fun isFcmPiiDataCaptureEnabled(): Boolean = local.isFcmPiiDataCaptureEnabled() } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/ConfigBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/ConfigBehavior.kt new file mode 100644 index 0000000000..12272e1ebe --- /dev/null +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/ConfigBehavior.kt @@ -0,0 +1,17 @@ +package io.embrace.android.embracesdk.internal.config.behavior + +/** + * Determines the SDK's behavior at runtime. These values are immutable for the process lifetime. + */ +interface ConfigBehavior { + + /** + * The local config. + */ + val local: L + + /** + * The remote config. + */ + val remote: R? +} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehavior.kt index 1bd75beb70..0baa98ef53 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface DataCaptureEventBehavior { +import io.embrace.android.embracesdk.internal.config.UnimplementedConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +interface DataCaptureEventBehavior : ConfigBehavior { fun isInternalExceptionCaptureEnabled(): Boolean fun isEventEnabled(eventName: String): Boolean fun isLogMessageEnabled(logMessage: String): Boolean diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehaviorImpl.kt index 975db6ae24..9ec6135610 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehaviorImpl.kt @@ -3,20 +3,17 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.PatternCache import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider class DataCaptureEventBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider = { null }, -) : DataCaptureEventBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + override val remote: RemoteConfig?, +) : DataCaptureEventBehavior { private companion object { private const val DEFAULT_INTERNAL_EXCEPTION_CAPTURE = true } + override val local: UnimplementedConfig = null + private val patternCache = PatternCache() override fun isInternalExceptionCaptureEnabled(): Boolean = diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehavior.kt index 616fba870e..8c3286880d 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface LogMessageBehavior { +import io.embrace.android.embracesdk.internal.config.UnimplementedConfig +import io.embrace.android.embracesdk.internal.config.remote.LogRemoteConfig + +interface LogMessageBehavior : ConfigBehavior { fun getLogMessageMaximumAllowedLength(): Int fun getInfoLogLimit(): Int fun getWarnLogLimit(): Int diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehaviorImpl.kt index a6a35771a1..57865b27c2 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehaviorImpl.kt @@ -2,18 +2,14 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.remote.LogRemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig /** * Provides the behavior that should be followed for remote log message functionality. */ class LogMessageBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, -) : LogMessageBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + remote: RemoteConfig?, +) : LogMessageBehavior { private companion object { private const val DEFAULT_LOG_INFO_LIMIT = 100 @@ -21,6 +17,9 @@ class LogMessageBehaviorImpl( private const val DEFAULT_LOG_ERROR_LIMIT = 250 } + override val remote: LogRemoteConfig? = remote?.logConfig + override val local: UnimplementedConfig = null + override fun getLogMessageMaximumAllowedLength(): Int { return remote?.logMessageMaximumAllowedLength ?: LOG_MESSAGE_MAXIMUM_ALLOWED_LENGTH } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/MergedConfigBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/MergedConfigBehavior.kt deleted file mode 100644 index 82cec4268c..0000000000 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/MergedConfigBehavior.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.embrace.android.embracesdk.internal.config.behavior - -import io.embrace.android.embracesdk.internal.utils.Provider - -/** - * Merges multiple sources of config and tells the SDK how its functionality should behave. This - * means the caller doesn't need to worry about whether the remote config has been fetched or its - * precedence rules - it just gets told whether it should enable something or not. - * - * There are three sources of config: remote (from the config endpoint); local (from the - * embrace-config.json); and default (defined in subclasses of this type). - * - * Config is typically evaluated in the following precedence: Remote > Local > Default. Remote/local - * configs might not exist for every single field, as it doesn't always make sense for every value - * to be configurable by end-users. However, there should always be a default value. - */ -open class MergedConfigBehavior( - - /** - * Checks whether percent-based thresholds should be enabled or not. We should always return - * booleans about whether functionality is enabled - and should never expose percentages etc - * to the caller. - */ - protected val thresholdCheck: BehaviorThresholdCheck, - - /** - * Supplier for local config, from the embrace-config.json file. - */ - private val localSupplier: Provider = { null }, - - /** - * Supplier for remote config, from the config endpoint. - */ - private val remoteSupplier: Provider = { null }, -) { - - /** - * The local config. This property always returns the most up-to-date value, or null if - * no local config is available. - */ - protected val local: L? - get() = localSupplier() - - /** - * The remote config. This property always returns the most up-to-date value, or null if - * no remote config is available. - */ - protected val remote: R? - get() = remoteSupplier() -} diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehavior.kt index 93f1619f48..cecd77f8ba 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehavior.kt @@ -1,8 +1,10 @@ package io.embrace.android.embracesdk.internal.config.behavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -interface NetworkBehavior { +interface NetworkBehavior : ConfigBehavior { /** * Control whether request size for native Android requests is captured. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImpl.kt index cd9d8758c1..6ff586806b 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImpl.kt @@ -1,10 +1,8 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider import java.util.regex.Pattern import kotlin.math.min @@ -12,14 +10,10 @@ import kotlin.math.min * Provides the behavior that functionality relating to network call capture should follow. */ class NetworkBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, + override val local: InstrumentedConfig, + override val remote: RemoteConfig?, private val disabledUrlPatterns: List? = null, - private val instrumentedConfig: InstrumentedConfig, -) : NetworkBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { +) : NetworkBehavior { companion object { @@ -35,13 +29,13 @@ class NetworkBehaviorImpl( ) } - private val cfg = instrumentedConfig.networkCapture + private val cfg = local.networkCapture override fun isRequestContentLengthCaptureEnabled(): Boolean = - instrumentedConfig.enabledFeatures.isRequestContentLengthCaptureEnabled() + local.enabledFeatures.isRequestContentLengthCaptureEnabled() override fun isHttpUrlConnectionCaptureEnabled(): Boolean = - instrumentedConfig.enabledFeatures.isHttpUrlConnectionCaptureEnabled() + local.enabledFeatures.isHttpUrlConnectionCaptureEnabled() override fun getLimitsByDomain(): Map { val limits = remote?.networkConfig?.domainLimits ?: cfg.getLimitsByDomain() diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehavior.kt index 9eca2f524d..cf27edb0af 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehavior.kt @@ -1,5 +1,8 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface NetworkSpanForwardingBehavior { +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.NetworkSpanForwardingRemoteConfig + +interface NetworkSpanForwardingBehavior : ConfigBehavior { fun isNetworkSpanForwardingEnabled(): Boolean } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImpl.kt index 6dc6db7e4f..e9dce91e95 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImpl.kt @@ -1,18 +1,16 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.NetworkSpanForwardingRemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig class NetworkSpanForwardingBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, - private val instrumentedConfig: InstrumentedConfig, -) : NetworkSpanForwardingBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + private val thresholdCheck: BehaviorThresholdCheck, + local: InstrumentedConfig, + remote: RemoteConfig?, +) : NetworkSpanForwardingBehavior { + companion object { /** * Header name for the W3C traceparent @@ -20,8 +18,11 @@ class NetworkSpanForwardingBehaviorImpl( const val TRACEPARENT_HEADER_NAME: String = "traceparent" } + override val local: EnabledFeatureConfig = local.enabledFeatures + override val remote: NetworkSpanForwardingRemoteConfig? = remote?.networkSpanForwardingRemoteConfig + override fun isNetworkSpanForwardingEnabled(): Boolean { return remote?.pctEnabled?.let { thresholdCheck.isBehaviorEnabled(it) } - ?: instrumentedConfig.enabledFeatures.isNetworkSpanForwardingEnabled() + ?: local.isNetworkSpanForwardingEnabled() } } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehavior.kt index e0dcd39c8c..402f546fef 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface SdkModeBehavior { +import io.embrace.android.embracesdk.internal.config.UnimplementedConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +interface SdkModeBehavior : ConfigBehavior { /** * Given a Config instance, computes if the SDK is enabled based on the threshold and the offset. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehaviorImpl.kt index 7821b8a1b5..fa6edb95da 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehaviorImpl.kt @@ -2,7 +2,6 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider import kotlin.math.max import kotlin.math.min @@ -10,12 +9,9 @@ import kotlin.math.min * Provides whether the SDK should enable certain 'behavior' modes, such as 'integration mode' */ class SdkModeBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, -) : SdkModeBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + private val thresholdCheck: BehaviorThresholdCheck, + override val remote: RemoteConfig?, +) : SdkModeBehavior { private companion object { @@ -30,6 +26,8 @@ class SdkModeBehaviorImpl( private const val DEFAULT_OFFSET = 0 } + override val local: UnimplementedConfig = null + /** * The % of devices that should be enabled. */ diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehavior.kt index 17384d73e2..9aa968c074 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -fun interface SensitiveKeysBehavior { +import io.embrace.android.embracesdk.internal.config.UnimplementedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.RedactionConfig + +interface SensitiveKeysBehavior : ConfigBehavior { /** * Checks if the given key is sensitive. diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImpl.kt index 4e3f9d3d43..50240078f3 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImpl.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.internal.config.behavior +import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.RedactionConfig private const val SENSITIVE_KEY_MAX_LENGTH = 128 private const val SENSITIVE_KEYS_LIST_MAX_SIZE = 10000 @@ -8,12 +10,13 @@ private const val SENSITIVE_KEYS_LIST_MAX_SIZE = 10000 const val REDACTED_LABEL: String = "" class SensitiveKeysBehaviorImpl( - denyList: List? = null, - instrumentedConfig: InstrumentedConfig + local: InstrumentedConfig, ) : SensitiveKeysBehavior { - private val denyList = - (denyList ?: instrumentedConfig.redaction.getSensitiveKeysDenylist())?.take(SENSITIVE_KEYS_LIST_MAX_SIZE) + override val local: RedactionConfig = local.redaction + override val remote: UnimplementedConfig = null + + private val denyList = local.redaction.getSensitiveKeysDenylist()?.take(SENSITIVE_KEYS_LIST_MAX_SIZE) override fun isSensitiveKey(key: String): Boolean { return denyList?.any { it.take(SENSITIVE_KEY_MAX_LENGTH) == key } ?: false diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehavior.kt index cdb2f81f62..3ef59e5479 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface SessionBehavior { +import io.embrace.android.embracesdk.internal.config.instrumented.schema.SessionConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +interface SessionBehavior : ConfigBehavior { /** * The whitelist of events (crashes, errors) that should send a full session payload even diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImpl.kt index 2d4a8b655d..b7c44824ea 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImpl.kt @@ -1,35 +1,32 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.SessionConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.gating.SessionGatingKeys -import io.embrace.android.embracesdk.internal.utils.Provider import java.util.Locale /** * Provides the behavior that functionality relating to sessions should follow. */ class SessionBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, - private val instrumentedConfig: InstrumentedConfig, -) : SessionBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + local: InstrumentedConfig, + override val remote: RemoteConfig?, +) : SessionBehavior { companion object { const val SESSION_PROPERTY_LIMIT: Int = 10 } + override val local: SessionConfig = local.session + override fun getFullSessionEvents(): Set { - val strings = remote?.sessionConfig?.fullSessionEvents ?: instrumentedConfig.session.getFullSessionEvents() + val strings = remote?.sessionConfig?.fullSessionEvents ?: local.getFullSessionEvents() return strings.map { it.lowercase(Locale.US) }.toSet() } override fun getSessionComponents(): Set? = - (remote?.sessionConfig?.sessionComponents ?: instrumentedConfig.session.getSessionComponents())?.toSet() + (remote?.sessionConfig?.sessionComponents ?: local.getSessionComponents())?.toSet() override fun isGatingFeatureEnabled(): Boolean = getSessionComponents() != null diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/WebViewVitalsBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/WebViewVitalsBehavior.kt index 7950e3b9e3..3222362af8 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/WebViewVitalsBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/WebViewVitalsBehavior.kt @@ -1,6 +1,9 @@ package io.embrace.android.embracesdk.internal.config.behavior -interface WebViewVitalsBehavior { +import io.embrace.android.embracesdk.internal.config.UnimplementedConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig + +interface WebViewVitalsBehavior : ConfigBehavior { fun getMaxWebViewVitals(): Int fun isWebViewVitalsEnabled(): Boolean } diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/WebViewVitalsBehaviorImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/WebViewVitalsBehaviorImpl.kt index 2131bf498e..d3db9519af 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/WebViewVitalsBehaviorImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/WebViewVitalsBehaviorImpl.kt @@ -2,15 +2,11 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.UnimplementedConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider class WebViewVitalsBehaviorImpl( - thresholdCheck: BehaviorThresholdCheck, - remoteSupplier: Provider, -) : WebViewVitalsBehavior, MergedConfigBehavior( - thresholdCheck = thresholdCheck, - remoteSupplier = remoteSupplier -) { + private val thresholdCheck: BehaviorThresholdCheck, + override val remote: RemoteConfig?, +) : WebViewVitalsBehavior { private companion object { /** @@ -24,6 +20,8 @@ class WebViewVitalsBehaviorImpl( private const val DEFAULT_MAX_VITALS = 300 } + override val local: UnimplementedConfig = null + private fun getWebVitalsPct(): Float = remote?.webViewVitals?.pctEnabled ?: DEFAULT_WEB_VITALS_PCT override fun getMaxWebViewVitals(): Int = remote?.webViewVitals?.maxVitals ?: DEFAULT_MAX_VITALS diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index ccf63b526c..d534449d86 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -40,7 +40,7 @@ internal class ConfigModuleImpl( } override val combinedRemoteConfigSource: CombinedRemoteConfigSource? by singleton { - configService.appId ?: return@singleton null + if (initModule.onlyOtelExportEnabled()) return@singleton null CombinedRemoteConfigSource( store = remoteConfigStore, httpSource = remoteConfigSource ?: return@singleton null, @@ -55,16 +55,16 @@ internal class ConfigModuleImpl( preferencesService = androidServicesModule.preferencesService, suppliedFramework = framework, instrumentedConfig = initModule.instrumentedConfig, - configProvider = { combinedRemoteConfigSource?.getConfig() }, + remoteConfig = combinedRemoteConfigSource?.getConfig(), ) } } override val remoteConfigSource by singleton { - val builder = urlBuilder ?: return@singleton null + if (initModule.onlyOtelExportEnabled()) return@singleton null OkHttpRemoteConfigSource( okhttpClient = okHttpClient, - apiUrlBuilder = builder, + apiUrlBuilder = urlBuilder ?: return@singleton null, serializer = initModule.jsonSerializer, ) } @@ -77,7 +77,7 @@ internal class ConfigModuleImpl( } override val urlBuilder: ApiUrlBuilder? by singleton { - configService.appId ?: return@singleton null + if (initModule.onlyOtelExportEnabled()) return@singleton null Systrace.traceSynchronous("url-builder-init") { EmbraceApiUrlBuilder( deviceId = androidServicesModule.preferencesService.deviceIdentifier, @@ -86,4 +86,9 @@ internal class ConfigModuleImpl( ) } } + + private fun InitModule.onlyOtelExportEnabled(): Boolean { + instrumentedConfig.project.getAppId() ?: return true + return false + } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/schema/TelemetryAttributesTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/schema/TelemetryAttributesTest.kt index 50beb6946b..7707dc192b 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/schema/TelemetryAttributesTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/arch/schema/TelemetryAttributesTest.kt @@ -105,14 +105,12 @@ internal class TelemetryAttributesTest { fun `log properties and session properties are not included in the attributes`() { val configService = FakeConfigService( sessionBehavior = createSessionBehavior( - remoteCfg = { - RemoteConfig( - sessionConfig = SessionRemoteConfig( - fullSessionEvents = setOf(), - sessionComponents = setOf() - ) + remoteCfg = RemoteConfig( + sessionConfig = SessionRemoteConfig( + fullSessionEvents = setOf(), + sessionComponents = setOf() ) - } + ) ) ) sessionPropertiesService.addProperty("perm", "permVal", true) @@ -133,14 +131,12 @@ internal class TelemetryAttributesTest { fun `log properties and session properties are included in the attributes`() { val configService = FakeConfigService( sessionBehavior = createSessionBehavior( - remoteCfg = { - RemoteConfig( - sessionConfig = SessionRemoteConfig( - fullSessionEvents = setOf(), - sessionComponents = setOf("s_props", "log_pr") - ) + remoteCfg = RemoteConfig( + sessionConfig = SessionRemoteConfig( + fullSessionEvents = setOf(), + sessionComponents = setOf("s_props", "log_pr") ) - } + ) ) ) sessionPropertiesService.addProperty("perm", "permVal", true) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/SessionPropertiesServiceImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/SessionPropertiesServiceImplTest.kt index b3983bb352..406a45f15d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/SessionPropertiesServiceImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/SessionPropertiesServiceImplTest.kt @@ -3,9 +3,10 @@ package io.embrace.android.embracesdk.internal.capture.session import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan import io.embrace.android.embracesdk.fakes.FakePreferenceService +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeRedactionConfig import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -23,8 +24,7 @@ internal class SessionPropertiesServiceImplTest { val fakeConfigService = FakeConfigService( sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( - listOf("password"), - InstrumentedConfigImpl + FakeInstrumentedConfig(redaction = FakeRedactionConfig(sensitiveKeys = listOf("password"))), ) ) fakeCurrentSessionSpan = FakeCurrentSessionSpan() diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt index 1c74ed9d8f..ec62bc11f9 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/ConfigServiceImplTest.kt @@ -197,8 +197,8 @@ internal class ConfigServiceImplTest { preferencesService = fakePreferenceService, suppliedFramework = AppFramework.NATIVE, instrumentedConfig = FakeInstrumentedConfig(project = FakeProjectConfig(appId = appId)), - thresholdCheck = thresholdCheck, - configProvider = ::remoteConfig + remoteConfig = remoteConfig, + thresholdCheck = thresholdCheck ) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImplTest.kt index 14d9a46d71..aa73c8d73f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AnrBehaviorImplTest.kt @@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.fakes.createAnrBehavior import io.embrace.android.embracesdk.internal.config.remote.AllowedNdkSampleMethod import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.remote.Unwinder import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -57,7 +58,7 @@ internal class AnrBehaviorImplTest { @Test fun testRemoteAndLocal() { - with(createAnrBehavior(remoteCfg = { remote })) { + with(createAnrBehavior(remoteCfg = RemoteConfig(anrConfig = remote))) { assertEquals(200L, getSamplingIntervalMs()) assertFalse(isNativeThreadAnrSamplingOffsetEnabled()) assertFalse(isNativeThreadAnrSamplingAllowlistIgnored()) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImplTest.kt index 6bf0c3b282..a14cdd2810 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AppExitInfoBehaviorImplTest.kt @@ -23,7 +23,7 @@ internal class AppExitInfoBehaviorImplTest { @Test fun testLocalAndRemote() { - with(createAppExitInfoBehavior(remoteCfg = { remote })) { + with(createAppExitInfoBehavior(remoteCfg = remote)) { assertEquals(55209, getTraceMaxLimit()) assertTrue(isAeiCaptureEnabled()) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImplTest.kt index 314567bd9c..60159ac88a 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehaviorImplTest.kt @@ -40,7 +40,7 @@ internal class AutoDataCaptureBehaviorImplTest { @Test fun testLocalAndRemote() { - with(createAutoDataCaptureBehavior(remoteCfg = { remote })) { + with(createAutoDataCaptureBehavior(remoteCfg = remote)) { assertFalse(is3rdPartySigHandlerDetectionEnabled()) assertFalse(isComposeClickCaptureEnabled()) assertFalse(isThermalStatusCaptureEnabled()) @@ -56,7 +56,7 @@ internal class AutoDataCaptureBehaviorImplTest { } // Jetpack Compose disabled remotely - with(createAutoDataCaptureBehavior(remoteCfg = { remote })) { + with(createAutoDataCaptureBehavior(remoteCfg = remote)) { assertFalse(isComposeClickCaptureEnabled()) } val remoteComposeKillSwitchOff = RemoteConfig( @@ -69,7 +69,7 @@ internal class AutoDataCaptureBehaviorImplTest { // Jetpack Compose enabled remotely with( createAutoDataCaptureBehavior( - remoteCfg = { remoteComposeKillSwitchOff } + remoteCfg = remoteComposeKillSwitchOff ) ) { assertTrue(isComposeClickCaptureEnabled()) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImplTest.kt index 5e7e84fd16..2bb34475f7 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BackgroundActivityBehaviorImplTest.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Test @@ -24,7 +25,7 @@ internal class BackgroundActivityBehaviorImplTest { @Test fun testRemoteAndLocal() { - with(createBackgroundActivityBehavior(remoteCfg = { remote })) { + with(createBackgroundActivityBehavior(remoteCfg = RemoteConfig(backgroundActivityConfig = remote))) { assertFalse(isBackgroundActivityCaptureEnabled()) assertEquals(100, getManualBackgroundActivityLimit()) assertEquals(5000L, getMinBackgroundActivityDuration()) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImplTest.kt index 641e7fff1a..3393d857b2 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/BreadcrumbBehaviorImplTest.kt @@ -3,7 +3,6 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.remote.UiRemoteConfig -import io.embrace.android.embracesdk.internal.utils.Uuid import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -20,15 +19,12 @@ internal class BreadcrumbBehaviorImplTest { ) ) - private val behaviorThresholdCheck = BehaviorThresholdCheck { Uuid.getEmbUuid() } - @Test fun testDefaults() { with( BreadcrumbBehaviorImpl( - thresholdCheck = behaviorThresholdCheck, - { null }, - InstrumentedConfigImpl + InstrumentedConfigImpl, + null, ) ) { assertEquals(100, getCustomBreadcrumbLimit()) @@ -47,9 +43,8 @@ internal class BreadcrumbBehaviorImplTest { fun testRemoteAndLocal() { with( BreadcrumbBehaviorImpl( - thresholdCheck = behaviorThresholdCheck, - { remote }, - InstrumentedConfigImpl + InstrumentedConfigImpl, + remote, ) ) { assertEquals(99, getCustomBreadcrumbLimit()) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehaviorImplTest.kt index 81e61c6824..18171aadee 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/DataCaptureEventBehaviorImplTest.kt @@ -26,7 +26,7 @@ internal class DataCaptureEventBehaviorImplTest { @Test fun testRemoteOnly() { - with(createDataCaptureEventBehavior(remoteCfg = { remote })) { + with(createDataCaptureEventBehavior(remoteCfg = remote)) { assertFalse(isInternalExceptionCaptureEnabled()) assertFalse(isEventEnabled("my_event")) assertTrue(isEventEnabled("other_event")) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehaviorImplTest.kt index a3b1f5967f..c73c0b553b 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/LogMessageBehaviorImplTest.kt @@ -2,16 +2,19 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.fakes.createLogMessageBehavior import io.embrace.android.embracesdk.internal.config.remote.LogRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import org.junit.Assert.assertEquals import org.junit.Test internal class LogMessageBehaviorImplTest { - private val remote = LogRemoteConfig( - 256, - 200, - 300, - 400 + private val remote = RemoteConfig( + logConfig = LogRemoteConfig( + 256, + 200, + 300, + 400 + ) ) @Test @@ -26,7 +29,7 @@ internal class LogMessageBehaviorImplTest { @Test fun testRemoteAndLocal() { - with(createLogMessageBehavior(remoteCfg = { remote })) { + with(createLogMessageBehavior(remoteCfg = remote)) { assertEquals(256, getLogMessageMaximumAllowedLength()) assertEquals(200, getInfoLogLimit()) assertEquals(300, getWarnLogLimit()) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImplTest.kt index 0671dbc969..1d782f5781 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/NetworkBehaviorImplTest.kt @@ -32,7 +32,7 @@ internal class NetworkBehaviorImplTest { @Test fun testDefaults() { - with(createNetworkBehavior(remoteCfg = { null })) { + with(createNetworkBehavior(remoteCfg = null)) { assertFalse(isRequestContentLengthCaptureEnabled()) assertTrue(isHttpUrlConnectionCaptureEnabled()) assertEquals(1000, getRequestLimitPerDomain()) @@ -46,7 +46,7 @@ internal class NetworkBehaviorImplTest { @Test fun testRemoteOnly() { - with(createNetworkBehavior(remoteCfg = { remote })) { + with(createNetworkBehavior(remoteCfg = remote)) { assertEquals(409, getRequestLimitPerDomain()) assertEquals(mapOf("google.com" to 50), getLimitsByDomain()) assertTrue(isUrlEnabled("google.com")) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImplTest.kt index 5f63257132..19cc45a929 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/NetworkSpanForwardingBehaviorImplTest.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.config.behavior import io.embrace.android.embracesdk.fakes.createNetworkSpanForwardingBehavior import io.embrace.android.embracesdk.internal.config.remote.NetworkSpanForwardingRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test @@ -17,17 +18,19 @@ internal class NetworkSpanForwardingBehaviorImplTest { @Test fun testRemote() { - with(createNetworkSpanForwardingBehavior(remoteConfig = { remoteEnabled })) { + with(createNetworkSpanForwardingBehavior(remoteConfig = remoteEnabled)) { assertTrue(isNetworkSpanForwardingEnabled()) } - with(createNetworkSpanForwardingBehavior(remoteConfig = { remoteDisabled })) { + with(createNetworkSpanForwardingBehavior(remoteConfig = remoteDisabled)) { assertFalse(isNetworkSpanForwardingEnabled()) } } companion object { - private val remoteEnabled = NetworkSpanForwardingRemoteConfig(pctEnabled = 100.0f) - private val remoteDisabled = NetworkSpanForwardingRemoteConfig(pctEnabled = 0.0f) + private val remoteEnabled = + RemoteConfig(networkSpanForwardingRemoteConfig = NetworkSpanForwardingRemoteConfig(pctEnabled = 100.0f)) + private val remoteDisabled = + RemoteConfig(networkSpanForwardingRemoteConfig = NetworkSpanForwardingRemoteConfig(pctEnabled = 0.0f)) } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehaviorImplTest.kt index d7bcb0334e..062185e885 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SdkModeBehaviorImplTest.kt @@ -33,28 +33,28 @@ internal class SdkModeBehaviorImplTest { // SDK disabled var behavior = createSdkModeBehavior( thresholdCheck = enabled, - remoteCfg = { RemoteConfig(threshold = 0) } + remoteCfg = RemoteConfig(threshold = 0) ) assertTrue(behavior.isSdkDisabled()) // SDK enabled behavior = createSdkModeBehavior( thresholdCheck = enabled, - remoteCfg = { RemoteConfig(threshold = 100) } + remoteCfg = RemoteConfig(threshold = 100) ) assertFalse(behavior.isSdkDisabled()) // SDK 30% enabled with default offset behavior = createSdkModeBehavior( thresholdCheck = halfEnabled, - remoteCfg = { RemoteConfig(threshold = 30) } + remoteCfg = RemoteConfig(threshold = 30) ) assertTrue(behavior.isSdkDisabled()) // SDK 30% enabled with non-default offset behavior = createSdkModeBehavior( thresholdCheck = halfEnabled, - remoteCfg = { RemoteConfig(threshold = 30, offset = 25) } + remoteCfg = RemoteConfig(threshold = 30, offset = 25) ) assertFalse(behavior.isSdkDisabled()) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImplTest.kt index b1691c538c..c09d239c93 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SensitiveKeysBehaviorImplTest.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.internal.config.behavior -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeRedactionConfig +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test @@ -10,7 +12,7 @@ internal class SensitiveKeysBehaviorImplTest { @Test fun `keys are not sensitive if they are not in the sensitive keys list`() { // given an empty sensitive list - val behavior = SensitiveKeysBehaviorImpl(emptyList(), InstrumentedConfigImpl) + val behavior = SensitiveKeysBehaviorImpl(emptyList().toConfig()) // when checking if a key is sensitive val isSensitive = behavior.isSensitiveKey("password") @@ -22,7 +24,8 @@ internal class SensitiveKeysBehaviorImplTest { @Test fun `keys are not sensitive with a null sensitive keys list`() { // given a null sensitive list - val behavior = SensitiveKeysBehaviorImpl(null, InstrumentedConfigImpl) + val behavior = + SensitiveKeysBehaviorImpl(FakeInstrumentedConfig(redaction = FakeRedactionConfig(sensitiveKeys = null))) // when checking if a key is sensitive val isSensitive = behavior.isSensitiveKey("password") @@ -34,7 +37,7 @@ internal class SensitiveKeysBehaviorImplTest { @Test fun `keys are sensitive when found in the sensitive keys list`() { // given a sensitive list with a key - val behavior = SensitiveKeysBehaviorImpl(listOf("password"), InstrumentedConfigImpl) + val behavior = SensitiveKeysBehaviorImpl(listOf("password").toConfig()) // when checking if a key present in the list is sensitive val isSensitive = behavior.isSensitiveKey("password") @@ -46,7 +49,7 @@ internal class SensitiveKeysBehaviorImplTest { @Test fun `keys in the sensitive list are truncated to 128 characters`() { // given a sensitive list with a long key - val behavior = SensitiveKeysBehaviorImpl(listOf("a".repeat(200)), InstrumentedConfigImpl) + val behavior = SensitiveKeysBehaviorImpl(listOf("a".repeat(200)).toConfig()) // when checking if a key present in the list is sensitive val sensitiveKey = behavior.isSensitiveKey("a".repeat(128)) @@ -61,8 +64,7 @@ internal class SensitiveKeysBehaviorImplTest { fun `sensitive list is truncated to 10000 keys`() { // given a sensitive list with more than 10000 keys val behavior = SensitiveKeysBehaviorImpl( - List(10000) { it.toString() } + "password", - InstrumentedConfigImpl + (List(10000) { it.toString() } + "password").toConfig() ) // when checking a key present in the 10001st position @@ -76,11 +78,7 @@ internal class SensitiveKeysBehaviorImplTest { fun `sensitive list with multiple keys`() { // given a sensitive list with multiple keys val behavior = SensitiveKeysBehaviorImpl( - listOf( - "password", - "passkey" - ), - InstrumentedConfigImpl + listOf("password", "passkey").toConfig(), ) // when checking if a key present in the list is sensitive @@ -93,4 +91,8 @@ internal class SensitiveKeysBehaviorImplTest { assertTrue(anotherSensitiveKey) assertFalse(notSensitiveKey) } + + private fun List.toConfig(): InstrumentedConfig { + return FakeInstrumentedConfig(redaction = FakeRedactionConfig(sensitiveKeys = this)) + } } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImplImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImplImplTest.kt index 94a15b97c7..05746fd16b 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImplImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/SessionBehaviorImplImplTest.kt @@ -33,7 +33,7 @@ internal class SessionBehaviorImplImplTest { @Test fun testRemoteAndLocal() { - with(createSessionBehavior(remoteCfg = { remote })) { + with(createSessionBehavior(remoteCfg = remote)) { assertTrue(isGatingFeatureEnabled()) assertTrue(isSessionControlEnabled()) assertEquals(setOf("test"), getSessionComponents()) @@ -45,9 +45,7 @@ internal class SessionBehaviorImplImplTest { @Test fun `test upper case full session events`() { val behavior = createSessionBehavior( - remoteCfg = { - buildGatingConfig(setOf("CRASHES", "ERRORS")) - } + remoteCfg = buildGatingConfig(setOf("CRASHES", "ERRORS")) ) assertEquals(setOf("crashes", "errors"), behavior.getFullSessionEvents()) } @@ -55,9 +53,7 @@ internal class SessionBehaviorImplImplTest { @Test fun `test lower case full session events`() { val behavior = createSessionBehavior( - remoteCfg = { - buildGatingConfig(setOf("crashes", "errors")) - } + remoteCfg = buildGatingConfig(setOf("crashes", "errors")) ) assertEquals(setOf("crashes", "errors"), behavior.getFullSessionEvents()) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/WebVitalsBehaviorTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/WebVitalsBehaviorTest.kt index cdc92c2b14..fe39ba9c4f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/WebVitalsBehaviorTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/behavior/WebVitalsBehaviorTest.kt @@ -22,7 +22,7 @@ internal class WebVitalsBehaviorTest { @Test fun testRemote() { - with(createWebViewVitalsBehavior(remoteCfg = { remote })) { + with(createWebViewVitalsBehavior(remoteCfg = remote)) { assertEquals(100, getMaxWebViewVitals()) assertFalse(isWebViewVitalsEnabled()) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/event/gating/EmbraceGatingServiceV2PayloadTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/event/gating/EmbraceGatingServiceV2PayloadTest.kt index cd04ae0a30..e9b75af7da 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/event/gating/EmbraceGatingServiceV2PayloadTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/event/gating/EmbraceGatingServiceV2PayloadTest.kt @@ -32,8 +32,8 @@ internal class EmbraceGatingServiceV2PayloadTest { @Before fun setUp() { - sessionBehavior = createSessionBehavior { cfg } - configService = FakeConfigService(sessionBehavior = createSessionBehavior { cfg }) + sessionBehavior = createSessionBehavior(cfg) + configService = FakeConfigService(sessionBehavior = createSessionBehavior(cfg)) logService = FakeLogService() gatingService = EmbraceGatingService(configService, logService) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt index 37b8b5ff2c..280867355a 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt @@ -7,11 +7,12 @@ import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeLogWriter import io.embrace.android.embracesdk.fakes.FakeSessionPropertiesService import io.embrace.android.embracesdk.fakes.behavior.FakeLogMessageBehavior +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeRedactionConfig import io.embrace.android.embracesdk.fakes.createSessionBehavior import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.remote.SessionRemoteConfig import io.embrace.android.embracesdk.internal.opentelemetry.embExceptionHandling @@ -39,8 +40,7 @@ internal class EmbraceLogServiceTest { fun setUp() { fakeConfigService = FakeConfigService( sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( - listOf("password"), - InstrumentedConfigImpl + FakeInstrumentedConfig(redaction = FakeRedactionConfig(sensitiveKeys = listOf("password"))), ) ) fakeSessionPropertiesService = FakeSessionPropertiesService() @@ -109,14 +109,12 @@ internal class EmbraceLogServiceTest { // given a config that gates info and warning logs fakeConfigService = FakeConfigService( sessionBehavior = createSessionBehavior( - remoteCfg = { - RemoteConfig( - sessionConfig = SessionRemoteConfig( - isEnabled = true, - sessionComponents = emptySet() // empty set will gate everything - ) + remoteCfg = RemoteConfig( + sessionConfig = SessionRemoteConfig( + isEnabled = true, + sessionComponents = emptySet() // empty set will gate everything ) - } + ) ) ) logService = createEmbraceLogService() diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt index 376243f3d5..d4dfb9d345 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt @@ -37,7 +37,7 @@ internal class EmbraceNetworkCaptureServiceTest { fun setUp() { cfg = RemoteConfig() configService = FakeConfigService( - networkBehavior = createNetworkBehavior(remoteCfg = { cfg }) + networkBehavior = createNetworkBehavior(remoteCfg = cfg) ) preferenceService = FakePreferenceService() networkCaptureDataSource = FakeNetworkCaptureDataSource() @@ -191,19 +191,24 @@ internal class EmbraceNetworkCaptureServiceTest { assertEquals(2, networkCaptureDataSource.loggedCalls.size) } - private fun getService() = EmbraceNetworkCaptureService( - sessionIdTracker, - preferenceService, - { networkCaptureDataSource }, - configService, - EmbraceApiUrlBuilder( - "deviceId", - "1.0.0", - FakeInstrumentedConfig() - ), - EmbraceSerializer(), - EmbLoggerImpl() - ) + private fun getService(): EmbraceNetworkCaptureService { + configService = FakeConfigService( + networkBehavior = createNetworkBehavior(remoteCfg = cfg) + ) + return EmbraceNetworkCaptureService( + sessionIdTracker, + preferenceService, + { networkCaptureDataSource }, + configService, + EmbraceApiUrlBuilder( + "deviceId", + "1.0.0", + FakeInstrumentedConfig() + ), + EmbraceSerializer(), + EmbLoggerImpl() + ) + } private fun getDefaultRule( id: String = "123", diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt index 9646eba7b8..504d405629 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/PayloadFactoryBaTest.kt @@ -20,6 +20,7 @@ import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.capture.metadata.MetadataService import io.embrace.android.embracesdk.internal.capture.user.UserService import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.envelope.session.SessionEnvelopeSourceImpl import io.embrace.android.embracesdk.internal.envelope.session.SessionPayloadSourceImpl import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl @@ -70,9 +71,9 @@ internal class PayloadFactoryBaTest { currentSessionSpan = initModule.openTelemetryModule.currentSessionSpan spanService = initModule.openTelemetryModule.spanService configService = FakeConfigService( - backgroundActivityBehavior = createBackgroundActivityBehavior { - BackgroundActivityRemoteConfig(threshold = 100f) - } + backgroundActivityBehavior = createBackgroundActivityBehavior( + remoteCfg = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(threshold = 100f)) + ) ) blockingExecutorService = BlockingScheduledExecutorService(blockingMode = false) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt index b60f3cf77d..8e838904be 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/SessionOrchestrationModuleImplTest.kt @@ -12,6 +12,7 @@ import io.embrace.android.embracesdk.fakes.injection.FakeLogModule import io.embrace.android.embracesdk.fakes.injection.FakePayloadSourceModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.injection.SessionOrchestrationModuleImpl import io.embrace.android.embracesdk.internal.injection.createDataSourceModule import io.embrace.android.embracesdk.internal.worker.Worker @@ -78,9 +79,9 @@ internal class SessionOrchestrationModuleImplTest { private fun createEnabledBehavior(): FakeConfigModule { return FakeConfigModule( configService = FakeConfigService( - backgroundActivityBehavior = createBackgroundActivityBehavior { - BackgroundActivityRemoteConfig(threshold = 100f) - }, + backgroundActivityBehavior = createBackgroundActivityBehavior( + remoteCfg = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(threshold = 100f)) + ), ) ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImplTest.kt index 3f0602e8f1..60569154e4 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/message/PayloadFactoryImplTest.kt @@ -9,6 +9,7 @@ import io.embrace.android.embracesdk.fakes.FakeSessionPayloadSource import io.embrace.android.embracesdk.fakes.createBackgroundActivityBehavior import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.envelope.session.SessionEnvelopeSourceImpl import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessState import io.embrace.android.embracesdk.internal.session.lifecycle.ProcessState.BACKGROUND @@ -56,9 +57,9 @@ internal class PayloadFactoryImplTest { @Test fun `verify expected payloads with ba enabled`() { - configService.backgroundActivityBehavior = createBackgroundActivityBehavior { - BackgroundActivityRemoteConfig(threshold = 100f) - } + configService.backgroundActivityBehavior = createBackgroundActivityBehavior( + remoteCfg = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(threshold = 100f)) + ) verifyPayloadWithState(state = FOREGROUND, zygoteCreated = true, startNewSession = true) verifyPayloadWithState(state = BACKGROUND, zygoteCreated = true, startNewSession = true) verifyPayloadWithManual() @@ -66,9 +67,9 @@ internal class PayloadFactoryImplTest { @Test fun `verify expected payloads with ba disabled`() { - configService.backgroundActivityBehavior = createBackgroundActivityBehavior { - BackgroundActivityRemoteConfig(threshold = 0f) - } + configService.backgroundActivityBehavior = createBackgroundActivityBehavior( + remoteCfg = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(threshold = 0f)) + ) verifyPayloadWithState(state = FOREGROUND, zygoteCreated = true, startNewSession = false) verifyPayloadWithState(state = BACKGROUND, zygoteCreated = false, startNewSession = false) verifyPayloadWithManual() diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt index 9b93b34c55..34c06072e7 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/session/orchestrator/SessionOrchestratorTest.kt @@ -24,6 +24,7 @@ import io.embrace.android.embracesdk.internal.arch.datasource.DataSourceState import io.embrace.android.embracesdk.internal.capture.session.SessionPropertiesService import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.delivery.caching.PayloadCachingService import io.embrace.android.embracesdk.internal.delivery.caching.PayloadCachingServiceImpl import io.embrace.android.embracesdk.internal.logging.EmbLogger @@ -68,9 +69,9 @@ internal class SessionOrchestratorTest { clock = FakeClock() logger = EmbLoggerImpl() configService = FakeConfigService( - backgroundActivityBehavior = createBackgroundActivityBehavior { - BackgroundActivityRemoteConfig(threshold = 100f) - } + backgroundActivityBehavior = createBackgroundActivityBehavior( + remoteCfg = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(threshold = 100f)) + ) ) } @@ -260,9 +261,9 @@ internal class SessionOrchestratorTest { @Test fun `end with crash in background`() { configService = FakeConfigService( - backgroundActivityBehavior = createBackgroundActivityBehavior { - BackgroundActivityRemoteConfig(threshold = 100f) - } + backgroundActivityBehavior = createBackgroundActivityBehavior( + remoteCfg = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(threshold = 100f)) + ) ) createOrchestrator(true) orchestrator.handleCrash("crashId") @@ -272,9 +273,9 @@ internal class SessionOrchestratorTest { @Test fun `end with crash in foreground`() { configService = FakeConfigService( - backgroundActivityBehavior = createBackgroundActivityBehavior { - BackgroundActivityRemoteConfig(threshold = 100f) - } + backgroundActivityBehavior = createBackgroundActivityBehavior( + remoteCfg = RemoteConfig(backgroundActivityConfig = BackgroundActivityRemoteConfig(threshold = 100f)) + ) ) createOrchestrator(false) orchestrator.handleCrash("crashId") diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanFactoryImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanFactoryImplTest.kt index c9f22c50b9..fab712cbb0 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanFactoryImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanFactoryImplTest.kt @@ -4,11 +4,12 @@ import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakePayloadCachingService import io.embrace.android.embracesdk.fakes.FakePersistableEmbraceSpan import io.embrace.android.embracesdk.fakes.FakeTracer +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeRedactionConfig import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.arch.schema.PrivateSpan import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.opentelemetry.embraceSpanBuilder import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -41,8 +42,7 @@ internal class EmbraceSpanFactoryImplTest { openTelemetryClock = initModule.openTelemetryModule.openTelemetryClock, spanRepository = spanRepository, sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( - listOf("password"), - InstrumentedConfigImpl + FakeInstrumentedConfig(redaction = FakeRedactionConfig(sensitiveKeys = listOf("password"))), ), ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt index 43910921e0..8eecb7c963 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt @@ -2,6 +2,8 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeOpenTelemetryClock +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.fakes.config.FakeRedactionConfig import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fixtures.MAX_LENGTH_ATTRIBUTE_KEY import io.embrace.android.embracesdk.fixtures.MAX_LENGTH_ATTRIBUTE_KEY_FOR_INTERNAL_SPAN @@ -23,7 +25,6 @@ import io.embrace.android.embracesdk.internal.clock.millisToNanos import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehaviorImpl -import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl import io.embrace.android.embracesdk.internal.opentelemetry.embraceSpanBuilder import io.embrace.android.embracesdk.internal.payload.Span import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer @@ -55,8 +56,7 @@ internal class EmbraceSpanImplTest { .getTracer(EmbraceSpanImplTest::class.java.name) private val sensitiveKeysBehavior = SensitiveKeysBehaviorImpl( - listOf("password"), - InstrumentedConfigImpl + FakeInstrumentedConfig(redaction = FakeRedactionConfig(sensitiveKeys = listOf("password"))), ) @Before diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/anr/detection/LivenessCheckSchedulerTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/anr/detection/LivenessCheckSchedulerTest.kt index fef31f841c..67e399d010 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/anr/detection/LivenessCheckSchedulerTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/anr/detection/LivenessCheckSchedulerTest.kt @@ -7,6 +7,7 @@ import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.createAnrBehavior import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.logging.EmbLogger import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl import io.embrace.android.embracesdk.internal.worker.BackgroundWorker @@ -40,7 +41,7 @@ internal class LivenessCheckSchedulerTest { anrMonitorThread = AtomicReference(Thread.currentThread()) cfg = AnrRemoteConfig() fakeClock = FakeClock(160982340900) - configService = FakeConfigService(anrBehavior = createAnrBehavior { cfg }) + configService = FakeConfigService(anrBehavior = createAnrBehavior(remoteCfg = RemoteConfig(anrConfig = cfg))) anrExecutorService = BlockingScheduledExecutorService(fakeClock) logger = EmbLoggerImpl() looper = mockk { @@ -146,21 +147,6 @@ internal class LivenessCheckSchedulerTest { assertEquals(160982340900, state.lastMonitorThreadResponseMs) } - @Test - fun testExecuteHealthCheckDifferentIntervalMs() { - // alter the intervalMs to trigger rescheduling - scheduler.startMonitoringThread() - anrExecutorService.runCurrentlyBlocked() - assertEquals(fakeClock.now(), state.lastMonitorThreadResponseMs) - cfg = cfg.copy(sampleIntervalMs = 10) - assertEquals(1, anrExecutorService.scheduledTasksCount()) - anrExecutorService.moveForwardAndRunBlocked(100) - anrExecutorService.runCurrentlyBlocked() - anrExecutorService.moveForwardAndRunBlocked(10) - assertEquals(fakeClock.now(), state.lastMonitorThreadResponseMs) - assertEquals(1, anrExecutorService.scheduledTasksCount()) - } - @Test fun `starting monitoring thread twice does not result in multiple recurring tasks`() { repeat(2) { diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/anr/ndk/EmbraceNativeThreadSamplerServiceTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/anr/ndk/EmbraceNativeThreadSamplerServiceTest.kt index 2773fd5355..f41d45fa4c 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/anr/ndk/EmbraceNativeThreadSamplerServiceTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/anr/ndk/EmbraceNativeThreadSamplerServiceTest.kt @@ -11,6 +11,7 @@ import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.behavior.AnrBehavior import io.embrace.android.embracesdk.internal.config.remote.AllowedNdkSampleMethod import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig import io.embrace.android.embracesdk.internal.config.remote.Unwinder import io.embrace.android.embracesdk.internal.payload.NativeThreadAnrInterval import io.embrace.android.embracesdk.internal.payload.NativeThreadAnrSample @@ -46,7 +47,12 @@ internal class EmbraceNativeThreadSamplerServiceTest { @Before fun setUp() { cfg = AnrRemoteConfig(pctNativeThreadAnrSamplingEnabled = 100f) - anrBehavior = createAnrBehavior { cfg } + setupSampler() + every { random.nextInt(any()) } returns 0 + } + + private fun setupSampler() { + anrBehavior = createAnrBehavior(remoteCfg = RemoteConfig(anrConfig = cfg)) configService = FakeConfigService(anrBehavior = anrBehavior) sharedObjectLoader = FakeSharedObjectLoader() delegate = mockk(relaxed = true) @@ -62,7 +68,6 @@ internal class EmbraceNativeThreadSamplerServiceTest { FakeDeviceArchitecture(), sharedObjectLoader ) - every { random.nextInt(any()) } returns 0 } @Test @@ -90,6 +95,7 @@ internal class EmbraceNativeThreadSamplerServiceTest { @Test fun testSessionEndDisabledSampling() { cfg = cfg.copy(pctNativeThreadAnrSamplingEnabled = 0f) + setupSampler() sampler.intervals = mutableListOf(obj) simulateUnityThreadSample() assertNull(sampler.getCapturedIntervals(false)) @@ -210,6 +216,7 @@ internal class EmbraceNativeThreadSamplerServiceTest { ), ignoreNativeThreadAnrSamplingAllowlist = false ) + setupSampler() val unityPlayer = StackTraceElement("com.unity3d.player.UnityPlayer", "pauseUnity", null, -1) @@ -252,6 +259,7 @@ internal class EmbraceNativeThreadSamplerServiceTest { cfg = cfg.copy( ignoreNativeThreadAnrSamplingAllowlist = false ) + setupSampler() assertEquals(-1, sampler.count) assertEquals(-1, sampler.factor) assertTrue(sampler.ignored) @@ -268,6 +276,7 @@ internal class EmbraceNativeThreadSamplerServiceTest { cfg = cfg.copy( pctNativeThreadAnrSamplingEnabled = 0f ) + setupSampler() sampler.onThreadBlocked(Thread.currentThread(), 0) verify(exactly = 0) { delegate.startSampling(any(), any()) } @@ -281,6 +290,7 @@ internal class EmbraceNativeThreadSamplerServiceTest { cfg = cfg.copy( ignoreNativeThreadAnrSamplingAllowlist = false ) + setupSampler() sampler.onThreadBlocked(Thread.currentThread(), 0) repeat(10) { @@ -294,6 +304,7 @@ internal class EmbraceNativeThreadSamplerServiceTest { cfg = cfg.copy( pctNativeThreadAnrSamplingEnabled = 0f ) + setupSampler() sampler.onThreadBlocked(Thread.currentThread(), 0) repeat(10) { diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/crumbs/BreadcrumbDataSourceTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/crumbs/BreadcrumbDataSourceTest.kt index 39dfae0f7a..c3e15a1de1 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/crumbs/BreadcrumbDataSourceTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/crumbs/BreadcrumbDataSourceTest.kt @@ -1,9 +1,9 @@ package io.embrace.android.embracesdk.internal.capture.crumbs import io.embrace.android.embracesdk.arch.assertIsType -import io.embrace.android.embracesdk.fakes.FakeBreadcrumbBehavior import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan import io.embrace.android.embracesdk.fakes.FakeEmbLogger +import io.embrace.android.embracesdk.fakes.behavior.FakeBreadcrumbBehavior import io.embrace.android.embracesdk.internal.arch.schema.EmbType import org.junit.Assert.assertEquals import org.junit.Before diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/crumbs/WebViewUrlDataSourceTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/crumbs/WebViewUrlDataSourceTest.kt index fcd18f2343..6d7a756b03 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/crumbs/WebViewUrlDataSourceTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/crumbs/WebViewUrlDataSourceTest.kt @@ -1,8 +1,8 @@ package io.embrace.android.embracesdk.internal.capture.crumbs -import io.embrace.android.embracesdk.fakes.FakeBreadcrumbBehavior import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan +import io.embrace.android.embracesdk.fakes.behavior.FakeBreadcrumbBehavior import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ActivityFeatureTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ActivityFeatureTest.kt index 6eaecd1c66..16ee1053be 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ActivityFeatureTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/ActivityFeatureTest.kt @@ -3,7 +3,6 @@ package io.embrace.android.embracesdk.testcases.features import android.os.Build import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.assertions.findSpanOfType -import io.embrace.android.embracesdk.fakes.FakeBreadcrumbBehavior import io.embrace.android.embracesdk.internal.arch.schema.EmbType import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.testframework.IntegrationTestRule diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt index 0c04b4a055..3f5eb3495b 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/Behavior.kt @@ -25,12 +25,7 @@ import io.embrace.android.embracesdk.internal.config.behavior.SessionBehaviorImp import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehavior import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehaviorImpl import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl -import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig -import io.embrace.android.embracesdk.internal.config.remote.BackgroundActivityRemoteConfig -import io.embrace.android.embracesdk.internal.config.remote.LogRemoteConfig -import io.embrace.android.embracesdk.internal.config.remote.NetworkSpanForwardingRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig -import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.internal.utils.Uuid private val behaviorThresholdCheck = BehaviorThresholdCheck(Uuid::getEmbUuid) @@ -40,64 +35,60 @@ private val behaviorThresholdCheck = BehaviorThresholdCheck(Uuid::getEmbUuid) */ fun createAnrBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, -): AnrBehavior = AnrBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) + remoteCfg: RemoteConfig? = null, +): AnrBehavior = AnrBehaviorImpl(thresholdCheck, InstrumentedConfigImpl, remoteCfg) /** * A [SessionBehaviorImpl] that returns default values. */ fun createSessionBehavior( - thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, -): SessionBehavior = SessionBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) + remoteCfg: RemoteConfig? = null, +): SessionBehavior = SessionBehaviorImpl(InstrumentedConfigImpl, remoteCfg) /** * A [NetworkBehaviorImpl] that returns default values. */ fun createNetworkBehavior( - thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, + remoteCfg: RemoteConfig? = null, disabledUrlPatterns: List? = null, -): NetworkBehavior = NetworkBehaviorImpl(thresholdCheck, remoteCfg, disabledUrlPatterns, InstrumentedConfigImpl) +): NetworkBehavior = NetworkBehaviorImpl(InstrumentedConfigImpl, remoteCfg, disabledUrlPatterns) /** * A [BackgroundActivityBehaviorImpl] that returns default values. */ fun createBackgroundActivityBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, -): BackgroundActivityBehavior = BackgroundActivityBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) + remoteCfg: RemoteConfig? = null, +): BackgroundActivityBehavior = BackgroundActivityBehaviorImpl(thresholdCheck, InstrumentedConfigImpl, remoteCfg) /** * A [AutoDataCaptureBehaviorImpl] that returns default values. */ fun createAutoDataCaptureBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, -): AutoDataCaptureBehavior = AutoDataCaptureBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) + remoteCfg: RemoteConfig? = null, +): AutoDataCaptureBehavior = AutoDataCaptureBehaviorImpl(thresholdCheck, InstrumentedConfigImpl, remoteCfg) /** * A [LogMessageBehaviorImpl] that returns default values. */ fun createLogMessageBehavior( - thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, -): LogMessageBehavior = LogMessageBehaviorImpl(thresholdCheck, remoteCfg) + remoteCfg: RemoteConfig? = null, +): LogMessageBehavior = LogMessageBehaviorImpl(remoteCfg) /** * A [DataCaptureEventBehaviorImpl] that returns default values. */ fun createDataCaptureEventBehavior( - thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, -): DataCaptureEventBehavior = DataCaptureEventBehaviorImpl(thresholdCheck, remoteCfg) + remoteCfg: RemoteConfig? = null, +): DataCaptureEventBehavior = DataCaptureEventBehaviorImpl(remoteCfg) /** * A [SdkModeBehaviorImpl] that returns default values. */ fun createSdkModeBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, + remoteCfg: RemoteConfig? = null, ): SdkModeBehavior = SdkModeBehaviorImpl(thresholdCheck, remoteCfg) /** @@ -105,19 +96,19 @@ fun createSdkModeBehavior( */ fun createAppExitInfoBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, -): AppExitInfoBehavior = AppExitInfoBehaviorImpl(thresholdCheck, remoteCfg, InstrumentedConfigImpl) + remoteCfg: RemoteConfig? = null, +): AppExitInfoBehavior = AppExitInfoBehaviorImpl(thresholdCheck, InstrumentedConfigImpl, remoteCfg) /** * A [NetworkSpanForwardingBehaviorImpl] that returns default values. */ fun createNetworkSpanForwardingBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteConfig: Provider = { null }, + remoteConfig: RemoteConfig? = null, ): NetworkSpanForwardingBehavior = NetworkSpanForwardingBehaviorImpl( thresholdCheck, - remoteConfig, - InstrumentedConfigImpl + InstrumentedConfigImpl, + remoteConfig ) /** @@ -125,10 +116,10 @@ fun createNetworkSpanForwardingBehavior( */ fun createWebViewVitalsBehavior( thresholdCheck: BehaviorThresholdCheck = behaviorThresholdCheck, - remoteCfg: Provider = { null }, + remoteCfg: RemoteConfig? = null, ): WebViewVitalsBehavior = WebViewVitalsBehaviorImpl(thresholdCheck, remoteCfg) /** * A [SensitiveKeysBehaviorImpl] that returns default values. */ -internal fun createSensitiveKeysBehavior() = SensitiveKeysBehaviorImpl(instrumentedConfig = InstrumentedConfigImpl) +internal fun createSensitiveKeysBehavior() = SensitiveKeysBehaviorImpl(InstrumentedConfigImpl) diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt index 21323a17cb..e39d48a8b9 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeConfigService.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.fakes +import io.embrace.android.embracesdk.fakes.behavior.FakeBreadcrumbBehavior import io.embrace.android.embracesdk.internal.config.ConfigService import io.embrace.android.embracesdk.internal.config.behavior.AnrBehavior import io.embrace.android.embracesdk.internal.config.behavior.AppExitInfoBehavior diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAnrBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAnrBehavior.kt index fbe4c24c65..7d8ba4bcc4 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAnrBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAnrBehavior.kt @@ -1,7 +1,9 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.AnrBehavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig import io.embrace.android.embracesdk.internal.config.remote.AllowedNdkSampleMethod +import io.embrace.android.embracesdk.internal.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.internal.config.remote.Unwinder class FakeAnrBehavior( @@ -15,6 +17,11 @@ class FakeAnrBehavior( var nativeThreadAnrSamplingAllowlistImpl: List = emptyList(), ) : AnrBehavior { + override val local: EnabledFeatureConfig + get() = throw UnsupportedOperationException() + override val remote: AnrRemoteConfig + get() = throw UnsupportedOperationException() + override fun isAnrCaptureEnabled(): Boolean = anrCaptureEnabled override fun getSamplingIntervalMs(): Long = sampleIntervalMsImpl override fun getMaxStacktracesPerInterval(): Int = 80 diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAppExitInfoBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAppExitInfoBehavior.kt index 596804196a..bddcb7349f 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAppExitInfoBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAppExitInfoBehavior.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.AppExitInfoBehavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.AppExitInfoConfig class FakeAppExitInfoBehavior( private val traceMaxLimit: Int = 20000000, @@ -8,6 +10,11 @@ class FakeAppExitInfoBehavior( private val appExitInfoMaxNum: Int = 0, ) : AppExitInfoBehavior { + override val local: EnabledFeatureConfig + get() = throw UnsupportedOperationException() + override val remote: AppExitInfoConfig + get() = throw UnsupportedOperationException() + override fun getTraceMaxLimit(): Int = traceMaxLimit override fun isAeiCaptureEnabled(): Boolean = enabled override fun appExitInfoMaxNum(): Int = appExitInfoMaxNum diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAutoDataCaptureBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAutoDataCaptureBehavior.kt index be364a32f9..dce1e1830c 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAutoDataCaptureBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAutoDataCaptureBehavior.kt @@ -1,6 +1,8 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.AutoDataCaptureBehavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig class FakeAutoDataCaptureBehavior( private val memoryServiceEnabled: Boolean = true, @@ -17,6 +19,11 @@ class FakeAutoDataCaptureBehavior( private val useOkhttp: Boolean = true, ) : AutoDataCaptureBehavior { + override val local: EnabledFeatureConfig + get() = throw UnsupportedOperationException() + override val remote: RemoteConfig + get() = throw UnsupportedOperationException() + override fun isMemoryWarningCaptureEnabled(): Boolean = memoryServiceEnabled override fun isThermalStatusCaptureEnabled(): Boolean = thermalStatusCaptureEnabled override fun isPowerSaveModeCaptureEnabled(): Boolean = powerSaveModeServiceEnabled diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeBreadcrumbBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeBreadcrumbBehavior.kt similarity index 76% rename from embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeBreadcrumbBehavior.kt rename to embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeBreadcrumbBehavior.kt index fac0ab8466..482c29f541 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeBreadcrumbBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeBreadcrumbBehavior.kt @@ -1,6 +1,8 @@ -package io.embrace.android.embracesdk.fakes +package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.BreadcrumbBehavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig class FakeBreadcrumbBehavior( var customBreadcrumbLimitImpl: Int = 100, @@ -13,6 +15,12 @@ class FakeBreadcrumbBehavior( var queryParamCaptureEnabled: Boolean = true, var captureFcmPiiDataEnabled: Boolean = false, ) : BreadcrumbBehavior { + + override val local: EnabledFeatureConfig + get() = throw UnsupportedOperationException() + override val remote: RemoteConfig + get() = throw UnsupportedOperationException() + override fun getCustomBreadcrumbLimit(): Int = customBreadcrumbLimitImpl override fun getFragmentBreadcrumbLimit(): Int = fragmentBreadcrumbLimitImpl override fun getTapBreadcrumbLimit(): Int = tapBreadcrumbLimitImpl diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeLogMessageBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeLogMessageBehavior.kt index 9cfb2abb8c..dbd494a802 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeLogMessageBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeLogMessageBehavior.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.LogMessageBehavior +import io.embrace.android.embracesdk.internal.config.remote.LogRemoteConfig class FakeLogMessageBehavior( private val logMessageMaximumAllowedLength: Int = 128, @@ -9,6 +10,11 @@ class FakeLogMessageBehavior( private val errorLogLimit: Int = 100, ) : LogMessageBehavior { + override val local: Unit + get() = throw UnsupportedOperationException() + override val remote: LogRemoteConfig + get() = throw UnsupportedOperationException() + override fun getLogMessageMaximumAllowedLength(): Int = logMessageMaximumAllowedLength override fun getInfoLogLimit(): Int = infoLogLimit override fun getWarnLogLimit(): Int = warnLogLimit diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeNetworkBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeNetworkBehavior.kt index 72efa1a25a..c49fb014a1 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeNetworkBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeNetworkBehavior.kt @@ -1,13 +1,21 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkBehavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig import io.embrace.android.embracesdk.internal.config.remote.NetworkCaptureRuleRemoteConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig class FakeNetworkBehavior( private val captureLimit: Int = 1000, private val domains: Map = emptyMap(), private val captureHttpUrlConnectionRequests: Boolean = true, ) : NetworkBehavior { + + override val local: InstrumentedConfig + get() = throw UnsupportedOperationException() + override val remote: RemoteConfig + get() = throw UnsupportedOperationException() + override fun isRequestContentLengthCaptureEnabled(): Boolean = false override fun isHttpUrlConnectionCaptureEnabled(): Boolean = captureHttpUrlConnectionRequests override fun getLimitsByDomain(): Map = domains diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeNetworkSpanForwardingBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeNetworkSpanForwardingBehavior.kt index d9db1aec12..6648621f62 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeNetworkSpanForwardingBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeNetworkSpanForwardingBehavior.kt @@ -1,10 +1,17 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.NetworkSpanForwardingBehavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.EnabledFeatureConfig +import io.embrace.android.embracesdk.internal.config.remote.NetworkSpanForwardingRemoteConfig class FakeNetworkSpanForwardingBehavior( private val networkSpanForwardingEnabled: Boolean = false, ) : NetworkSpanForwardingBehavior { + override val local: EnabledFeatureConfig + get() = throw UnsupportedOperationException() + override val remote: NetworkSpanForwardingRemoteConfig + get() = throw UnsupportedOperationException() + override fun isNetworkSpanForwardingEnabled(): Boolean = networkSpanForwardingEnabled } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkModeBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkModeBehavior.kt index c0a281d6e3..6e202fb949 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkModeBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSdkModeBehavior.kt @@ -1,9 +1,16 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.SdkModeBehavior +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig class FakeSdkModeBehavior( var sdkDisabled: Boolean, ) : SdkModeBehavior { + + override val local: Unit + get() = throw UnsupportedOperationException() + override val remote: RemoteConfig + get() = throw UnsupportedOperationException() + override fun isSdkDisabled() = sdkDisabled } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSessionBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSessionBehavior.kt index 4978a76cd1..bca5ca8d61 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSessionBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeSessionBehavior.kt @@ -1,12 +1,19 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.SessionBehavior +import io.embrace.android.embracesdk.internal.config.instrumented.schema.SessionConfig +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig class FakeSessionBehavior( private val maxSessionProperties: Int = 100, private val sessionControlEnabled: Boolean = false, ) : SessionBehavior { + override val local: SessionConfig + get() = throw UnsupportedOperationException() + override val remote: RemoteConfig + get() = throw UnsupportedOperationException() + override fun getFullSessionEvents(): Set = emptySet() override fun getSessionComponents(): Set? = null override fun isGatingFeatureEnabled(): Boolean = false diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeWebViewVitalsBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeWebViewVitalsBehavior.kt index f51fc6e36b..bec80ba97d 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeWebViewVitalsBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeWebViewVitalsBehavior.kt @@ -1,12 +1,18 @@ package io.embrace.android.embracesdk.fakes.behavior import io.embrace.android.embracesdk.internal.config.behavior.WebViewVitalsBehavior +import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig class FakeWebViewVitalsBehavior( private val maxWebViewVitals: Int = 100, private val webViewVitalsEnabled: Boolean = true, ) : WebViewVitalsBehavior { + override val local: Unit + get() = throw UnsupportedOperationException() + override val remote: RemoteConfig + get() = throw UnsupportedOperationException() + override fun getMaxWebViewVitals(): Int = maxWebViewVitals override fun isWebViewVitalsEnabled(): Boolean = webViewVitalsEnabled } From 5cc24070d14290f144e0b28b58f3d4c6db5e800a Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Thu, 14 Nov 2024 13:06:00 +0000 Subject: [PATCH 27/28] perf: load okhttp async --- .../config/source/CombinedRemoteConfigSource.kt | 4 +++- .../internal/injection/ConfigModuleImpl.kt | 16 +++++++++------- .../source/CombinedRemoteConfigSourceTest.kt | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt index d2b96a877d..ccd23dcb82 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSource.kt @@ -8,11 +8,13 @@ import java.util.concurrent.TimeUnit class CombinedRemoteConfigSource( private val store: RemoteConfigStore, - private val httpSource: RemoteConfigSource, + httpSource: Lazy, private val worker: BackgroundWorker, private val intervalMs: Long = 60 * 60 * 1000 ) { + private val httpSource: RemoteConfigSource by httpSource + // the remote config that is used for the lifetime of the process. private val response by lazy { Systrace.traceSynchronous("load-config-from-store") { diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt index d534449d86..19846aa3a9 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/ConfigModuleImpl.kt @@ -31,19 +31,21 @@ internal class ConfigModuleImpl( } override val okHttpClient by singleton { - OkHttpClient() - .newBuilder() - .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)) - .connectTimeout(DEFAULT_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) - .readTimeout(DEFAULT_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS) - .build() + Systrace.traceSynchronous("okhttp-client-init") { + OkHttpClient() + .newBuilder() + .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)) + .connectTimeout(DEFAULT_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .readTimeout(DEFAULT_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .build() + } } override val combinedRemoteConfigSource: CombinedRemoteConfigSource? by singleton { if (initModule.onlyOtelExportEnabled()) return@singleton null CombinedRemoteConfigSource( store = remoteConfigStore, - httpSource = remoteConfigSource ?: return@singleton null, + httpSource = lazy { checkNotNull(remoteConfigSource) }, worker = workerThreadModule.backgroundWorker(Worker.Background.IoRegWorker), ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt index d3d8983ecd..c3de00ac7d 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/config/source/CombinedRemoteConfigSourceTest.kt @@ -26,7 +26,7 @@ class CombinedRemoteConfigSourceTest { remoteConfigStore = FakeRemoteConfigStore() source = CombinedRemoteConfigSource( remoteConfigStore, - remoteConfigSource, + lazy { remoteConfigSource }, BackgroundWorker(executorService) ) } @@ -41,7 +41,7 @@ class CombinedRemoteConfigSourceTest { val cfg = RemoteConfig(100) source = CombinedRemoteConfigSource( FakeRemoteConfigStore(ConfigHttpResponse(cfg, null)), - remoteConfigSource, + lazy { remoteConfigSource }, BackgroundWorker(executorService) ) assertEquals(cfg, source.getConfig()) From bff72ecd93ad71ad3466996777b651c42d36bbe1 Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Thu, 14 Nov 2024 14:36:07 +0000 Subject: [PATCH 28/28] fix endpoint used for sdk --- .../internal/comms/api/EmbraceApiUrlBuilder.kt | 2 +- .../comms/api/EmbraceApiServiceTest.kt | 18 +++++++++--------- .../comms/api/EmbraceApiUrlBuilderTest.kt | 4 ++-- .../EmbracePendingApiCallsSenderTest.kt | 4 ++-- .../EmbraceNetworkCaptureServiceTest.kt | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt index 02ce4ae2a8..379e065281 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilder.kt @@ -15,7 +15,7 @@ internal class EmbraceApiUrlBuilder( } override val appId: String = checkNotNull(instrumentedConfig.project.getAppId()) - private val coreBaseUrl = instrumentedConfig.baseUrls.getData() ?: "https://a-$appId.$DATA_DEFAULT/api" + private val coreBaseUrl = instrumentedConfig.baseUrls.getData() ?: "https://a-$appId.$DATA_DEFAULT" private val configBaseUrl = instrumentedConfig.baseUrls.getConfig() ?: "https://a-$appId.$CONFIG_DEFAULT" private val operatingSystemCode = Build.VERSION.SDK_INT.toString() + ".0.0" override val baseDataUrl: String = resolveUrl(Endpoint.SESSIONS).split(Endpoint.SESSIONS.path).first() diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt index a8ecd510fa..f293e5b210 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiServiceTest.kt @@ -70,7 +70,7 @@ internal class EmbraceApiServiceTest { var finished = false apiService.sendSession({ it.write(payload) }) { finished = true } verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/spans", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/spans", expectedPayload = payload ) assertTrue(finished) @@ -100,7 +100,7 @@ internal class EmbraceApiServiceTest { val type: ParameterizedType = TypeUtils.parameterizedType(Envelope::class, LogPayload::class) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", expectedPayload = getGenericsExpectedPayloadSerialized(logsEnvelope, type) ) } @@ -130,7 +130,7 @@ internal class EmbraceApiServiceTest { val request = fakePendingApiCallsSender.retryQueue.single().first val payload = fakePendingApiCallsSender.retryQueue.single().second - assertEquals("https://a-$fakeAppId.data.emb-api.com/api/v2/logs", request.url.url) + assertEquals("https://a-$fakeAppId.data.emb-api.com/v2/logs", request.url.url) val type: ParameterizedType = TypeUtils.parameterizedType(Envelope::class, LogPayload::class) assertArrayEquals(getGenericsExpectedPayloadSerialized(logsEnvelope, type), payload) } @@ -149,7 +149,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) assertEquals(0, fakeApiClient.sentRequests.size) val request = fakePendingApiCallsSender.retryQueue.single().first - assertEquals("https://a-$fakeAppId.data.emb-api.com/api/v2/logs", request.url.url) + assertEquals("https://a-$fakeAppId.data.emb-api.com/v2/logs", request.url.url) } @Test @@ -165,7 +165,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(1, fakePendingApiCallsSender.retryQueue.size) @@ -183,7 +183,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(1, fakePendingApiCallsSender.retryQueue.size) @@ -204,7 +204,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) @@ -222,7 +222,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) @@ -243,7 +243,7 @@ internal class EmbraceApiServiceTest { apiService.sendLogEnvelope(logPayload) verifyOnlyRequest( - expectedUrl = "https://a-$fakeAppId.data.emb-api.com/api/v2/logs", + expectedUrl = "https://a-$fakeAppId.data.emb-api.com/v2/logs", expectedPayload = getExpectedPayloadSerialized(logPayload, logType) ) assertEquals(0, fakePendingApiCallsSender.retryQueue.size) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt index ae2942194c..138998099a 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/api/EmbraceApiUrlBuilderTest.kt @@ -29,11 +29,11 @@ internal class EmbraceApiUrlBuilderTest { apiUrlBuilder.resolveUrl(Endpoint.CONFIG) ) assertEquals( - "https://a-$APP_ID.data.emb-api.com/api/v2/logs", + "https://a-$APP_ID.data.emb-api.com/v2/logs", apiUrlBuilder.resolveUrl(Endpoint.LOGS) ) assertEquals( - "https://a-$APP_ID.data.emb-api.com/api/v2/spans", + "https://a-$APP_ID.data.emb-api.com/v2/spans", apiUrlBuilder.resolveUrl(Endpoint.SESSIONS) ) } diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt index b6a3db910d..5d80158e77 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/comms/delivery/EmbracePendingApiCallsSenderTest.kt @@ -270,11 +270,11 @@ internal class EmbracePendingApiCallsSenderTest { // verify logs were added to the queue, and oldest added requests are dropped assertEquals( - "https://a-abcde.data.emb-api.com/api/v2/logs", + "https://a-abcde.data.emb-api.com/v2/logs", queue.pollNextPendingApiCall()?.apiRequest?.url?.url ) assertEquals( - "https://a-abcde.data.emb-api.com/api/v2/logs", + "https://a-abcde.data.emb-api.com/v2/logs", queue.pollNextPendingApiCall()?.apiRequest?.url?.url ) diff --git a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt index d4dfb9d345..b304c6162f 100644 --- a/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt +++ b/embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/network/logging/EmbraceNetworkCaptureServiceTest.kt @@ -67,9 +67,9 @@ internal class EmbraceNetworkCaptureServiceTest { @Test fun `test capture rule doesn't capture Embrace endpoints`() { - val rule = getDefaultRule(urlRegex = "https://a-abcde.data.emb-api.com/api/v2") + val rule = getDefaultRule(urlRegex = "https://a-abcde.data.emb-api.com/v2") cfg = RemoteConfig(networkCaptureRules = setOf(rule)) - val result = getService().getNetworkCaptureRules("https://a-abcde.data.emb-api.com/api/v2/spans", "GET") + val result = getService().getNetworkCaptureRules("https://a-abcde.data.emb-api.com/v2/spans", "GET") assertEquals(0, result.size) }