From 9af28ab984cc74b28edeeb87ee8a58c2eecb0f03 Mon Sep 17 00:00:00 2001 From: Vailence Date: Tue, 5 May 2026 19:47:57 +0500 Subject: [PATCH 1/5] MOBILE-160: move operationsDomain after required params Place all required Configuration constructor params consecutively, with operationsDomain among the optional params at the end. --- .../lib/src/types/configuration.dart | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/mindbox_platform_interface/lib/src/types/configuration.dart b/mindbox_platform_interface/lib/src/types/configuration.dart index bcd725d..7962bd2 100644 --- a/mindbox_platform_interface/lib/src/types/configuration.dart +++ b/mindbox_platform_interface/lib/src/types/configuration.dart @@ -9,11 +9,16 @@ class Configuration { this.previousDeviceUUID = '', this.previousInstallationId = '', this.shouldCreateCustomer = true, + this.operationsDomain, }); /// Used for generating baseurl for REST. final String domain; + /// Optional host for sending operations. Overridden by + /// the value from the mobile JSON config when present. Default `nil` (use `domain`). + final String? operationsDomain; + /// Used for app identification on iOS. final String endpointIos; @@ -34,13 +39,19 @@ class Configuration { final bool shouldCreateCustomer; /// Returns map of parameters - Map toMap() => { - 'domain': domain, - 'endpointIos': endpointIos, - 'endpointAndroid': endpointAndroid, - 'previousDeviceUUID': previousDeviceUUID, - 'previousInstallationId': previousInstallationId, - 'subscribeCustomerIfCreated': subscribeCustomerIfCreated, - 'shouldCreateCustomer': shouldCreateCustomer, - }; + Map toMap() { + final map = { + 'domain': domain, + 'endpointIos': endpointIos, + 'endpointAndroid': endpointAndroid, + 'previousDeviceUUID': previousDeviceUUID, + 'previousInstallationId': previousInstallationId, + 'subscribeCustomerIfCreated': subscribeCustomerIfCreated, + 'shouldCreateCustomer': shouldCreateCustomer, + }; + if (operationsDomain != null && operationsDomain!.isNotEmpty) { + map['operationsDomain'] = operationsDomain; + } + return map; + } } From 39f119bcb15befd707024c26447bcfe07c2e160e Mon Sep 17 00:00:00 2001 From: Vailence Date: Tue, 5 May 2026 19:49:37 +0500 Subject: [PATCH 2/5] MOBILE-160: align operationsDomain with sibling fields style Switch from nullable String? with conditional toMap to default empty string with unconditional serialization. Native sides treat empty as "not set" (Android skips builder call; iOS converts to nil), mirroring how previousDeviceUUID and previousInstallationId are wired. --- .../mindbox_android/MindboxAndroidPlugin.kt | 2 ++ .../ios/Classes/SwiftMindboxIosPlugin.swift | 4 ++- .../lib/src/types/configuration.dart | 29 +++++++--------- .../test/src/types/configuration_test.dart | 33 +++++++++++++++++++ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt b/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt index be7d742..1f53ae0 100644 --- a/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt +++ b/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt @@ -72,11 +72,13 @@ class MindboxAndroidPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, Ne val previousInstallId: String = args["previousInstallationId"] as String val subscribeIfCreated: Boolean = args["subscribeCustomerIfCreated"] as Boolean val shouldCreateCustomer: Boolean = args["shouldCreateCustomer"] as Boolean + val operationsDomain: String = args["operationsDomain"] as? String ?: "" val config = MindboxConfiguration.Builder(context.applicationContext, domain, endpointId) .setPreviousDeviceUuid(previousDeviceUuid) .setPreviousInstallationId(previousInstallId) .subscribeCustomerIfCreated(subscribeIfCreated) .shouldCreateCustomer(shouldCreateCustomer) + .apply { if (operationsDomain.isNotEmpty()) operationsDomain(operationsDomain) } .build() Mindbox.init(activity = context, config, listOf()) result.success("initialized") diff --git a/mindbox_ios/ios/Classes/SwiftMindboxIosPlugin.swift b/mindbox_ios/ios/Classes/SwiftMindboxIosPlugin.swift index 6fb7119..5d07fa9 100644 --- a/mindbox_ios/ios/Classes/SwiftMindboxIosPlugin.swift +++ b/mindbox_ios/ios/Classes/SwiftMindboxIosPlugin.swift @@ -84,8 +84,10 @@ public class SwiftMindboxIosPlugin: NSObject, FlutterPlugin { let shouldCreateCustomer = args["shouldCreateCustomer"] as? Bool{ let prevUuid = previousUuid.isEmpty ? nil : previousUuid let prevId = previousInstallId.isEmpty ? nil : previousInstallId + let operationsDomainRaw = args["operationsDomain"] as? String ?? "" + let operationsDomain = operationsDomainRaw.isEmpty ? nil : operationsDomainRaw do{ - let config = try MBConfiguration(endpoint: endpoint, domain: domain,previousInstallationId: prevId, previousDeviceUUID: prevUuid, subscribeCustomerIfCreated: subscribeIfCreated, shouldCreateCustomer: shouldCreateCustomer) + let config = try MBConfiguration(endpoint: endpoint, domain: domain, operationsDomain: operationsDomain, previousInstallationId: prevId, previousDeviceUUID: prevUuid, subscribeCustomerIfCreated: subscribeIfCreated, shouldCreateCustomer: shouldCreateCustomer) Mindbox.shared.initialization(configuration: config) result("initialized") }catch let error { diff --git a/mindbox_platform_interface/lib/src/types/configuration.dart b/mindbox_platform_interface/lib/src/types/configuration.dart index 7962bd2..a2a69cb 100644 --- a/mindbox_platform_interface/lib/src/types/configuration.dart +++ b/mindbox_platform_interface/lib/src/types/configuration.dart @@ -9,7 +9,7 @@ class Configuration { this.previousDeviceUUID = '', this.previousInstallationId = '', this.shouldCreateCustomer = true, - this.operationsDomain, + this.operationsDomain = '', }); /// Used for generating baseurl for REST. @@ -17,7 +17,7 @@ class Configuration { /// Optional host for sending operations. Overridden by /// the value from the mobile JSON config when present. Default `nil` (use `domain`). - final String? operationsDomain; + final String operationsDomain; /// Used for app identification on iOS. final String endpointIos; @@ -39,19 +39,14 @@ class Configuration { final bool shouldCreateCustomer; /// Returns map of parameters - Map toMap() { - final map = { - 'domain': domain, - 'endpointIos': endpointIos, - 'endpointAndroid': endpointAndroid, - 'previousDeviceUUID': previousDeviceUUID, - 'previousInstallationId': previousInstallationId, - 'subscribeCustomerIfCreated': subscribeCustomerIfCreated, - 'shouldCreateCustomer': shouldCreateCustomer, - }; - if (operationsDomain != null && operationsDomain!.isNotEmpty) { - map['operationsDomain'] = operationsDomain; - } - return map; - } + Map toMap() => { + 'domain': domain, + 'endpointIos': endpointIos, + 'endpointAndroid': endpointAndroid, + 'previousDeviceUUID': previousDeviceUUID, + 'previousInstallationId': previousInstallationId, + 'subscribeCustomerIfCreated': subscribeCustomerIfCreated, + 'shouldCreateCustomer': shouldCreateCustomer, + 'operationsDomain': operationsDomain, + }; } diff --git a/mindbox_platform_interface/test/src/types/configuration_test.dart b/mindbox_platform_interface/test/src/types/configuration_test.dart index b5c6647..ee1576d 100644 --- a/mindbox_platform_interface/test/src/types/configuration_test.dart +++ b/mindbox_platform_interface/test/src/types/configuration_test.dart @@ -12,6 +12,7 @@ void main() { previousDeviceUUID: 'previousDeviceUUID', subscribeCustomerIfCreated: true, shouldCreateCustomer: true, + operationsDomain: 'operations.example.com', ); // Assert @@ -22,5 +23,37 @@ void main() { expect(configuration.previousDeviceUUID, 'previousDeviceUUID'); expect(configuration.subscribeCustomerIfCreated, true); expect(configuration.shouldCreateCustomer, true); + expect(configuration.operationsDomain, 'operations.example.com'); + }); + + test('operationsDomain defaults to empty string when not provided', () { + final Configuration configuration = Configuration( + domain: 'domain', + endpointIos: 'iOSEndpoint', + endpointAndroid: 'androidEndpoint', + ); + + expect(configuration.operationsDomain, ''); + }); + + test('toMap includes operationsDomain when set', () { + final Configuration configuration = Configuration( + domain: 'domain', + endpointIos: 'iOSEndpoint', + endpointAndroid: 'androidEndpoint', + operationsDomain: 'operations.example.com', + ); + + expect(configuration.toMap()['operationsDomain'], 'operations.example.com'); + }); + + test('toMap returns empty operationsDomain when not provided', () { + final Configuration configuration = Configuration( + domain: 'domain', + endpointIos: 'iOSEndpoint', + endpointAndroid: 'androidEndpoint', + ); + + expect(configuration.toMap()['operationsDomain'], ''); }); } From 0598966ca6834528b644716fbc5664b8240c3aeb Mon Sep 17 00:00:00 2001 From: Vailence Date: Tue, 5 May 2026 19:50:18 +0500 Subject: [PATCH 3/5] MOBILE-160: add channel-level tests for operationsDomain Capture init MethodCall args and verify operationsDomain is forwarded across the platform channel both when set and when defaulted. --- .../types/mindbox_method_handler_test.dart | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/mindbox_platform_interface/test/src/types/mindbox_method_handler_test.dart b/mindbox_platform_interface/test/src/types/mindbox_method_handler_test.dart index dd04d93..ed685e0 100644 --- a/mindbox_platform_interface/test/src/types/mindbox_method_handler_test.dart +++ b/mindbox_platform_interface/test/src/types/mindbox_method_handler_test.dart @@ -50,6 +50,56 @@ void main() { }, ); + test( + 'init() forwards operationsDomain to native channel', + () async { + final capturedArgs = {}; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (call) async { + if (call.method == 'init') { + capturedArgs.addAll(Map.from(call.arguments)); + } + return mindboxMockMethodCallHandler(call); + }); + + await handler.init( + configuration: Configuration( + domain: 'domain', + endpointIos: 'endpointIos', + endpointAndroid: 'endpointAndroid', + operationsDomain: 'operations.example.com', + ), + ); + + expect(capturedArgs['operationsDomain'], 'operations.example.com'); + }, + ); + + test( + 'init() forwards empty operationsDomain by default', + () async { + final capturedArgs = {}; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (call) async { + if (call.method == 'init') { + capturedArgs.addAll(Map.from(call.arguments)); + } + return mindboxMockMethodCallHandler(call); + }); + + await handler.init( + configuration: Configuration( + domain: 'domain', + endpointIos: 'endpointIos', + endpointAndroid: 'endpointAndroid', + ), + ); + + expect(capturedArgs.containsKey('operationsDomain'), isTrue); + expect(capturedArgs['operationsDomain'], ''); + }, + ); + test( 'When config is invalid, init() calling should throws MindboxException', () async { From ad7f4cab3a7bd3244e94add45a8674adae2b2dd2 Mon Sep 17 00:00:00 2001 From: Vailence Date: Tue, 5 May 2026 19:51:01 +0500 Subject: [PATCH 4/5] MOBILE-160: minor cleanups around operationsDomain - Configuration dartdoc: use Dart `null`/empty wording, remove the native-config reference that leaks SDK-internal behavior. - MindboxAndroidPlugin: rename local to operationsDomainArg and apply the builder method outside the chain to avoid name collision with the builder method. - example: drop the placeholder commented-out arg from the sample app. --- .../mindbox/mindbox_android/MindboxAndroidPlugin.kt | 10 ++++++---- .../lib/src/types/configuration.dart | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt b/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt index 1f53ae0..106cf86 100644 --- a/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt +++ b/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt @@ -72,14 +72,16 @@ class MindboxAndroidPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, Ne val previousInstallId: String = args["previousInstallationId"] as String val subscribeIfCreated: Boolean = args["subscribeCustomerIfCreated"] as Boolean val shouldCreateCustomer: Boolean = args["shouldCreateCustomer"] as Boolean - val operationsDomain: String = args["operationsDomain"] as? String ?: "" - val config = MindboxConfiguration.Builder(context.applicationContext, domain, endpointId) + val operationsDomainArg: String = args["operationsDomain"] as? String ?: "" + val builder = MindboxConfiguration.Builder(context.applicationContext, domain, endpointId) .setPreviousDeviceUuid(previousDeviceUuid) .setPreviousInstallationId(previousInstallId) .subscribeCustomerIfCreated(subscribeIfCreated) .shouldCreateCustomer(shouldCreateCustomer) - .apply { if (operationsDomain.isNotEmpty()) operationsDomain(operationsDomain) } - .build() + if (operationsDomainArg.isNotEmpty()) { + builder.operationsDomain(operationsDomainArg) + } + val config = builder.build() Mindbox.init(activity = context, config, listOf()) result.success("initialized") } else { diff --git a/mindbox_platform_interface/lib/src/types/configuration.dart b/mindbox_platform_interface/lib/src/types/configuration.dart index a2a69cb..00fbe35 100644 --- a/mindbox_platform_interface/lib/src/types/configuration.dart +++ b/mindbox_platform_interface/lib/src/types/configuration.dart @@ -15,8 +15,8 @@ class Configuration { /// Used for generating baseurl for REST. final String domain; - /// Optional host for sending operations. Overridden by - /// the value from the mobile JSON config when present. Default `nil` (use `domain`). + /// Optional separate host for sending operations. When empty, [domain] + /// is used. Default is an empty string. final String operationsDomain; /// Used for app identification on iOS. From 5cfafe6ee643a833f3eb1e445eb596ea75696722 Mon Sep 17 00:00:00 2001 From: Vailence Date: Wed, 6 May 2026 17:04:55 +0500 Subject: [PATCH 5/5] MOBILE-160 Fix PR Comms --- .../cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt b/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt index 106cf86..e587c8b 100644 --- a/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt +++ b/mindbox_android/android/src/main/kotlin/cloud/mindbox/mindbox_android/MindboxAndroidPlugin.kt @@ -73,15 +73,13 @@ class MindboxAndroidPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, Ne val subscribeIfCreated: Boolean = args["subscribeCustomerIfCreated"] as Boolean val shouldCreateCustomer: Boolean = args["shouldCreateCustomer"] as Boolean val operationsDomainArg: String = args["operationsDomain"] as? String ?: "" - val builder = MindboxConfiguration.Builder(context.applicationContext, domain, endpointId) + val config = MindboxConfiguration.Builder(context.applicationContext, domain, endpointId) .setPreviousDeviceUuid(previousDeviceUuid) .setPreviousInstallationId(previousInstallId) .subscribeCustomerIfCreated(subscribeIfCreated) .shouldCreateCustomer(shouldCreateCustomer) - if (operationsDomainArg.isNotEmpty()) { - builder.operationsDomain(operationsDomainArg) - } - val config = builder.build() + .operationsDomain(operationsDomainArg) + .build() Mindbox.init(activity = context, config, listOf()) result.success("initialized") } else {