diff --git a/CHANGELOG.md b/CHANGELOG.md index 0788d440..5f914303 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## XX.XX.XX * Added a new function "addCustomNetworkRequestHeaders: customHeaderValues" for providing or overriding custom headers after init. +* Added "setRequestTimeoutDuration(requestTimeoutDuration)" init config method to change request timeout duration in seconds. * Default request method is now set to "POST" * Added a new function "recordMetrics: metricsOverride" to send a device metrics request. * Added a new Consent option "metrics" for controlling "recordMetrics" method. (This has no effect on Session metrics.) diff --git a/android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java b/android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java index 25554afa..358e8e60 100644 --- a/android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java +++ b/android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java @@ -1722,6 +1722,10 @@ private void populateConfig(JSONObject _config) throws JSONException { this.config.setRequestDropAgeHours(_config.getInt("requestDropAgeHours")); } + if (_config.has("requestTimeoutDuration")) { + this.config.setRequestTimeoutDuration(_config.getInt("requestTimeoutDuration")); + } + if (_config.has("manualSessionEnabled") && _config.getBoolean("manualSessionEnabled")) { enableManualSessionControl(); } diff --git a/example/integration_test/networking_tests/request_timeout_duration_default_test.dart b/example/integration_test/networking_tests/request_timeout_duration_default_test.dart new file mode 100644 index 00000000..2ee29c42 --- /dev/null +++ b/example/integration_test/networking_tests/request_timeout_duration_default_test.dart @@ -0,0 +1,31 @@ +import 'package:countly_flutter/countly_flutter.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import '../utils.dart'; + +/// Tests for the requestTimeoutDuration configuration option default value. +/// Verifies that the SDK initializes correctly with the default option +/// and that requests are attempted. +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('requestTimeoutDuration_default_test', (WidgetTester tester) async { + // Create a server that delays response by 3 seconds + List>> requestArray = >>[]; + createServer(requestArray, delay: 3); + + // Initialize SDK with default request timeout + CountlyConfig config = CountlyConfig('http://0.0.0.0:8080', APP_KEY).enableManualSessionHandling().setLoggingEnabled(true); // Use default timeout + + await Countly.initWithConfig(config); + + // Start a session to trigger a request + await Countly.instance.sessions.beginSession(); + + // Wait for a bit to allow the request to be processed + await Future.delayed(const Duration(seconds: 5)); + + List requestList = await getRequestQueue(); + equals(requestList.length, 0); + }); +} diff --git a/example/integration_test/networking_tests/request_timeout_duration_test.dart b/example/integration_test/networking_tests/request_timeout_duration_test.dart new file mode 100644 index 00000000..8f8016dc --- /dev/null +++ b/example/integration_test/networking_tests/request_timeout_duration_test.dart @@ -0,0 +1,37 @@ +import 'package:countly_flutter/countly_flutter.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import '../utils.dart'; +import '../views_tests/auto_view_flow1_test.dart'; + +/// Tests for the requestTimeoutDuration configuration option. +/// Verifies that the SDK initializes correctly with the option set +/// and that requests are attempted. +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('requestTimeoutDuration_test', (WidgetTester tester) async { + // Create a server that delays response by 3 seconds + // This is longer than the timeout we will set (1 second) + List>> requestArray = >>[]; + createServer(requestArray, delay: 3); + + // Initialize SDK with a short request timeout + CountlyConfig config = CountlyConfig('http://0.0.0.0:8080', APP_KEY).enableManualSessionHandling().setLoggingEnabled(true).setRequestTimeoutDuration(1); // Set timeout to 1 second + + await Countly.initWithConfig(config); + + // Start a session to trigger a request + await Countly.instance.sessions.beginSession(); + + // Wait for a bit to allow the request to be processed + await Future.delayed(const Duration(seconds: 5)); + + // Verify that we at least attempted to send requests but they timed out thus remained in the queue + List requestList = await getRequestQueue(); + + // Check if the begin_session request in the queue + equals(requestList.length, 1); + validateBeginSessionRequest(requestList[0]); + }); +} diff --git a/example/lib/config_object.dart b/example/lib/config_object.dart index 6f01405a..68cccd98 100644 --- a/example/lib/config_object.dart +++ b/example/lib/config_object.dart @@ -66,6 +66,7 @@ class CountlyConfiguration { // ..setParameterTamperingProtectionSalt('salt') // Set the optional salt to be used for calculating the checksum of requested data which will be sent with each request // ..enableManualSessionHandling() // Enable manual session handling // ..setHttpPostForced(true) // Set to 'false' if you want HTTP POST not to be used for all requests + // ..setRequestTimeoutDuration(15) // Set the request timeout duration to 15 seconds // ..setCustomNetworkRequestHeaders({ // 'Content-Type': 'application/json', // 'Authorization': 'Bearer YOUR_ACCESS_TOKEN', diff --git a/ios/Classes/CountlyFlutterPlugin.m b/ios/Classes/CountlyFlutterPlugin.m index 8796ba3c..4eebc5c4 100644 --- a/ios/Classes/CountlyFlutterPlugin.m +++ b/ios/Classes/CountlyFlutterPlugin.m @@ -1720,6 +1720,11 @@ - (void)populateConfig:(NSDictionary *)_config { config.requestDropAgeHours = [requestDropAgeHours intValue]; } + NSNumber *requestTimeoutDuration = _config[@"requestTimeoutDuration"]; + if (requestTimeoutDuration) { + config.requestTimeoutDuration = [requestTimeoutDuration intValue]; + } + NSNumber *manualSessionEnabled = _config[@"manualSessionEnabled"]; if (manualSessionEnabled && [manualSessionEnabled boolValue]) { config.manualSessionHandling = YES; diff --git a/lib/src/countly_config.dart b/lib/src/countly_config.dart index 7f4992cc..4fc0028b 100644 --- a/lib/src/countly_config.dart +++ b/lib/src/countly_config.dart @@ -47,6 +47,7 @@ class CountlyConfig { String? _sdkBehaviorSettings; bool _backoffMechanismDisabled = false; bool _sdkBehaviorSettingsUpdatesDisabled = false; + int? _requestTimeoutDuration; /// instance of CountlyConfigApm final CountlyConfigApm _countlyConfigApmInstance = CountlyConfigApm(); @@ -143,6 +144,8 @@ class CountlyConfig { bool get sdkBehaviorSettingsUpdatesDisabled => _sdkBehaviorSettingsUpdatesDisabled; + int? get requestTimeoutDuration => _requestTimeoutDuration; + /// getter for CountlyConfigApm instance that is used to access CountlyConfigApm methods CountlyConfigApm get apm => _countlyConfigApmInstance; @@ -401,4 +404,11 @@ class CountlyConfig { _sdkBehaviorSettingsUpdatesDisabled = true; return this; } + + /// Set the request timeout duration in seconds + /// [int requestTimeoutDuration] - duration in seconds + CountlyConfig setRequestTimeoutDuration(int requestTimeoutDuration) { + _requestTimeoutDuration = requestTimeoutDuration; + return this; + } } diff --git a/lib/src/countly_flutter.dart b/lib/src/countly_flutter.dart index 34174d8c..29c6300d 100644 --- a/lib/src/countly_flutter.dart +++ b/lib/src/countly_flutter.dart @@ -2347,6 +2347,11 @@ class Countly { log('"_configToJson", value provided for remoteConfigValueCaching: [${config.remoteConfigValueCaching}]', logLevel: LogLevel.INFO); countlyConfig['remoteConfigValueCaching'] = config.remoteConfigValueCaching; + + if (config.requestTimeoutDuration != null) { + log('"_configToJson", value provided for requestTimeoutDuration: [${config.requestTimeoutDuration}]', logLevel: LogLevel.INFO); + countlyConfig['requestTimeoutDuration'] = config.requestTimeoutDuration; + } } catch (e) { log('"_configToJson", Exception occur during converting config to json: $e', logLevel: LogLevel.ERROR); }