diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index 89d9093..fe24b49 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -5,6 +5,16 @@ on: branches: [ "master" ] pull_request: branches: [ "master" ] + # workflow_dispatch: + # inputs: + # sdk_branch: + # description: "Specify the SDK branch" + # required: false + # default: "master" + # testapp_branch: + # description: "Specify the test app branch" + # required: false + # default: "master" jobs: unit_test_coverage: @@ -36,6 +46,21 @@ jobs: repository: 'optimizely/travisci-tools' path: 'home/runner/travisci-tools' ref: 'master' + # Set SDK Branch based on input or PR/Push + # - name: Set SDK Branch and Test App Branch + # run: | + # # If manually triggered + # if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + # echo "SDK_BRANCH=${{ github.event.inputs.sdk_branch || 'master' }}" >> $GITHUB_ENV + # echo "TESTAPP_BRANCH=${{ github.event.inputs.testapp_branch || 'master' }}" >> $GITHUB_ENV + # # If triggered by PR + # elif [[ "${{ github.event_name }}" == "pull_request" ]]; then + # echo "SDK_BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV + # # If triggered by push + # else + # echo "SDK_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV + # echo "TRAVIS_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV + # fi - name: set SDK Branch if PR env: HEAD_REF: ${{ github.head_ref }} diff --git a/android/build.gradle b/android/build.gradle index cdec407..12f972d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -74,7 +74,7 @@ dependencies { implementation 'org.slf4j:slf4j-api:2.0.7' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10" - implementation "com.optimizely.ab:android-sdk:4.0.0" + implementation "com.optimizely.ab:android-sdk:5.0.0" implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4' implementation ('com.google.guava:guava:19.0') { exclude group:'com.google.guava', module:'listenablefuture' diff --git a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterClient.java b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterClient.java index c571c2f..cf85e3d 100644 --- a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterClient.java +++ b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterClient.java @@ -55,6 +55,7 @@ import static com.optimizely.optimizely_flutter_sdk.helper_classes.Constants.*; import static com.optimizely.optimizely_flutter_sdk.helper_classes.Constants.RequestParameterKey.DISABLE_ODP; +import static com.optimizely.optimizely_flutter_sdk.helper_classes.Constants.RequestParameterKey.ENABLE_VUID; import static com.optimizely.optimizely_flutter_sdk.helper_classes.Constants.RequestParameterKey.SEGMENTS_CACHE_SIZE; import static com.optimizely.optimizely_flutter_sdk.helper_classes.Constants.RequestParameterKey.SEGMENTS_CACHE_TIMEOUT_IN_SECONDS; import static com.optimizely.optimizely_flutter_sdk.helper_classes.Constants.RequestParameterKey.TIMEOUT_FOR_ODP_EVENT_IN_SECONDS; @@ -144,6 +145,7 @@ protected void initializeOptimizely(@NonNull ArgumentsParser argumentsParser, @N int timeoutForSegmentFetchInSecs = 10; int timeoutForOdpEventInSecs = 10; boolean disableOdp = false; + boolean enableVuid = false; Map sdkSettings = argumentsParser.getOptimizelySdkSettings(); if (sdkSettings != null) { if (sdkSettings.containsKey(SEGMENTS_CACHE_SIZE)) { @@ -161,6 +163,9 @@ protected void initializeOptimizely(@NonNull ArgumentsParser argumentsParser, @N if (sdkSettings.containsKey(DISABLE_ODP)) { disableOdp = (boolean) sdkSettings.get(DISABLE_ODP); } + if (sdkSettings.containsKey(ENABLE_VUID)) { + enableVuid = (boolean) sdkSettings.get(ENABLE_VUID); + } } // Creating new instance OptimizelyManager.Builder optimizelyManagerBuilder = OptimizelyManager.builder() @@ -179,6 +184,9 @@ protected void initializeOptimizely(@NonNull ArgumentsParser argumentsParser, @N if (disableOdp) { optimizelyManagerBuilder.withODPDisabled(); } + if (enableVuid) { + optimizelyManagerBuilder.withVuidEnabled(); + } OptimizelyManager optimizelyManager = optimizelyManagerBuilder.build(context); optimizelyManager.initialize(context, null, (OptimizelyClient client) -> { @@ -471,7 +479,7 @@ protected void getVuid(ArgumentsParser argumentsParser, @NonNull Result result) if (!isOptimizelyClientValid(sdkKey, optimizelyClient, result)) { return; } - result.success(createResponse(true, Collections.singletonMap(RequestParameterKey.VUID, optimizelyClient.getVuid()), "")); + result.success(createResponse(optimizelyClient.getVuid() != null, Collections.singletonMap(RequestParameterKey.VUID, optimizelyClient.getVuid()), "")); } /// Checks if the user is qualified for the given segment. diff --git a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/helper_classes/Constants.java b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/helper_classes/Constants.java index 0c73d32..62f0ce9 100644 --- a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/helper_classes/Constants.java +++ b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/helper_classes/Constants.java @@ -96,6 +96,7 @@ public static class RequestParameterKey { public static final String TIMEOUT_FOR_SEGMENT_FETCH_IN_SECONDS = "timeoutForSegmentFetchInSecs"; public static final String TIMEOUT_FOR_ODP_EVENT_IN_SECONDS = "timeoutForOdpEventInSecs"; public static final String DISABLE_ODP = "disableOdp"; + public static final String ENABLE_VUID = "enableVuid"; } public static class ErrorMessage { diff --git a/ios/Classes/HelperClasses/Constants.swift b/ios/Classes/HelperClasses/Constants.swift index f3ca6d5..a29370a 100644 --- a/ios/Classes/HelperClasses/Constants.swift +++ b/ios/Classes/HelperClasses/Constants.swift @@ -114,6 +114,7 @@ struct RequestParameterKey { static let timeoutForSegmentFetchInSecs = "timeoutForSegmentFetchInSecs" static let timeoutForOdpEventInSecs = "timeoutForOdpEventInSecs" static let disableOdp = "disableOdp" + static let enableVuid = "enableVuid" static let sdkVersion = "sdkVersion"; } diff --git a/ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift b/ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift index 8a2fd75..7c093c4 100644 --- a/ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift +++ b/ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift @@ -118,6 +118,7 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin { var timeoutForSegmentFetchInSecs: Int = 10 var timeoutForOdpEventInSecs: Int = 10 var disableOdp: Bool = false + var enableVuid: Bool = false var sdkVersion = parameters[RequestParameterKey.sdkVersion] as? String var sdkName = Utils.sdkName @@ -137,8 +138,11 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin { if let isOdpDisabled = sdkSettings[RequestParameterKey.disableOdp] as? Bool { disableOdp = isOdpDisabled } + if let isEnableVuid = sdkSettings[RequestParameterKey.enableVuid] as? Bool { + enableVuid = isEnableVuid + } } - let optimizelySdkSettings = OptimizelySdkSettings(segmentsCacheSize: segmentsCacheSize, segmentsCacheTimeoutInSecs: segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: timeoutForSegmentFetchInSecs, timeoutForOdpEventInSecs: timeoutForOdpEventInSecs, disableOdp: disableOdp, sdkName: sdkName, sdkVersion: sdkVersion) + let optimizelySdkSettings = OptimizelySdkSettings(segmentsCacheSize: segmentsCacheSize, segmentsCacheTimeoutInSecs: segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: timeoutForSegmentFetchInSecs, timeoutForOdpEventInSecs: timeoutForOdpEventInSecs, disableOdp: disableOdp, enableVuid: enableVuid, sdkName: sdkName, sdkVersion: sdkVersion) // Datafile Download Interval var datafilePeriodicDownloadInterval = 10 * 60 // seconds @@ -374,7 +378,7 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin { } else { userContextsTracker[sdkKey] = [userContextId: userContext] } - result(self.createResponse(success: true, result: [RequestParameterKey.userContextId: userContextId])) + result(self.createResponse(success: userContext != nil, result: [RequestParameterKey.userContextId: userContextId])) } /// Returns userId for the user context. @@ -442,7 +446,7 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin { guard let optimizelyClient = getOptimizelyClient(sdkKey: sdkKey, result: result) else { return } - result(self.createResponse(success: true, result: [RequestParameterKey.vuid: optimizelyClient.vuid])) + result(self.createResponse(success: optimizelyClient.vuid != nil, result: [RequestParameterKey.vuid: optimizelyClient.vuid])) } /// Checks if the user is qualified for the given segment. diff --git a/ios/optimizely_flutter_sdk.podspec b/ios/optimizely_flutter_sdk.podspec index 4da4408..14f4dbc 100644 --- a/ios/optimizely_flutter_sdk.podspec +++ b/ios/optimizely_flutter_sdk.podspec @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'OptimizelySwiftSDK', '4.0.0' + s.dependency 'OptimizelySwiftSDK', '5.0.0' s.platform = :ios, '10.0' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/lib/src/data_objects/get_vuid_response.dart b/lib/src/data_objects/get_vuid_response.dart index 4be045b..e5fcfb0 100644 --- a/lib/src/data_objects/get_vuid_response.dart +++ b/lib/src/data_objects/get_vuid_response.dart @@ -18,7 +18,7 @@ import 'package:optimizely_flutter_sdk/src/data_objects/base_response.dart'; import 'package:optimizely_flutter_sdk/src/utils/constants.dart'; class GetVuidResponse extends BaseResponse { - String vuid = ""; + String? vuid; GetVuidResponse(Map json) : super(json) { if (json[Constants.responseResult] is Map) { diff --git a/lib/src/data_objects/sdk_settings.dart b/lib/src/data_objects/sdk_settings.dart index 412432a..448a2e8 100644 --- a/lib/src/data_objects/sdk_settings.dart +++ b/lib/src/data_objects/sdk_settings.dart @@ -25,6 +25,8 @@ class SDKSettings { final int timeoutForOdpEventInSecs; // Set this flag to true (default = false) to disable ODP features final bool disableOdp; + // Set this flag to true (default = false) to enable VUID feature + final bool enableVuid; const SDKSettings({ this.segmentsCacheSize = 100, // Default segmentsCacheSize @@ -33,5 +35,6 @@ class SDKSettings { 10, // Default timeoutForSegmentFetchInSecs this.timeoutForOdpEventInSecs = 10, // Default timeoutForOdpEventInSecs this.disableOdp = false, // Default disableOdp + this.enableVuid = false, // Default disableVuid }); } diff --git a/lib/src/optimizely_client_wrapper.dart b/lib/src/optimizely_client_wrapper.dart index 4d6e0c0..fb4fce0 100644 --- a/lib/src/optimizely_client_wrapper.dart +++ b/lib/src/optimizely_client_wrapper.dart @@ -90,6 +90,7 @@ class OptimizelyClientWrapper { sdkSettings.timeoutForSegmentFetchInSecs, Constants.timeoutForOdpEventInSecs: sdkSettings.timeoutForOdpEventInSecs, Constants.disableOdp: sdkSettings.disableOdp, + Constants.enableVuid: sdkSettings.enableVuid, }; requestDict[Constants.optimizelySdkSettings] = optimizelySdkSettings; diff --git a/lib/src/utils/constants.dart b/lib/src/utils/constants.dart index 7ef9547..fb33033 100644 --- a/lib/src/utils/constants.dart +++ b/lib/src/utils/constants.dart @@ -131,6 +131,7 @@ class Constants { "timeoutForSegmentFetchInSecs"; static const String timeoutForOdpEventInSecs = "timeoutForOdpEventInSecs"; static const String disableOdp = "disableOdp"; + static const String enableVuid = "enableVuid"; // Response keys static const String responseSuccess = "success"; diff --git a/test/optimizely_flutter_sdk_test.dart b/test/optimizely_flutter_sdk_test.dart index 821e234..862c4b0 100644 --- a/test/optimizely_flutter_sdk_test.dart +++ b/test/optimizely_flutter_sdk_test.dart @@ -56,7 +56,6 @@ void main() { SDKSettings sdkSettings = const SDKSettings(); int datafilePeriodicDownloadInterval = 0; String defaultLogLevel = "error"; - const MethodChannel channel = MethodChannel("optimizely_flutter_sdk"); dynamic mockOptimizelyConfig; @@ -106,6 +105,7 @@ void main() { timeoutForOdpEventInSecs: settings[Constants.timeoutForOdpEventInSecs], disableOdp: settings[Constants.disableOdp], + enableVuid: settings[Constants.enableVuid], ); } @@ -175,9 +175,19 @@ void main() { }; case Constants.createUserContextMethod: expect(methodCall.arguments[Constants.sdkKey], isNotEmpty); - if (methodCall.arguments[Constants.userId] != null) { + var resultUserId = userContextId; + if (methodCall.arguments[Constants.userId] == null) { + if (sdkSettings.enableVuid) { + resultUserId = vuid; + } else { + return { + Constants.responseSuccess: false, + }; + } + } else if (methodCall.arguments[Constants.userId] != null) { expect(methodCall.arguments[Constants.userId], equals(userId)); } + if (methodCall.arguments[Constants.attributes]["abc"] != null) { expect(methodCall.arguments[Constants.attributes]["abc"], equals(attributes["abc"])); @@ -185,7 +195,7 @@ void main() { expect(methodCall.arguments[Constants.userContextId], isNull); return { Constants.responseSuccess: true, - Constants.responseResult: {Constants.userContextId: userContextId}, + Constants.responseResult: {Constants.userContextId: resultUserId}, }; case Constants.getUserIdMethod: expect(methodCall.arguments[Constants.sdkKey], isNotEmpty); @@ -266,6 +276,8 @@ void main() { case Constants.getVuidMethod: expect(methodCall.arguments[Constants.sdkKey], isNotEmpty); expect(methodCall.arguments[Constants.userContextId], isNull); + expect(methodCall.arguments[Constants.vuid], isNull); + var vuid = sdkSettings.enableVuid ? "vuid_123" : null; return { Constants.responseSuccess: true, Constants.responseResult: {Constants.vuid: vuid}, @@ -376,6 +388,7 @@ void main() { tearDown(() { tester?.setMockMethodCallHandler(channel, null); + sdkSettings = const SDKSettings(); }); group("Integration: OptimizelyFlutterSdk MethodChannel", () { @@ -650,8 +663,17 @@ void main() { expect(userContext, isNotNull); }); - test("should succeed null userId", () async { + test("should fail when disable vuid and userId null", () async { var sdk = OptimizelyFlutterSdk(testSDKKey); + sdk.initializeClient(); + var userContext = await sdk.createUserContext(attributes: attributes); + expect(userContext, isNull); + }); + + test("should succed when enable vuid and userId null", () async { + const settings = SDKSettings(enableVuid: true); + var sdk = OptimizelyFlutterSdk(testSDKKey, sdkSettings: settings); + sdk.initializeClient(); var userContext = await sdk.createUserContext(attributes: attributes); expect(userContext, isNotNull); }); @@ -662,10 +684,11 @@ void main() { expect(userContext, isNotNull); }); - test("should succeed null userId and attributes", () async { + test("should not succeed null userId and attributes", () async { var sdk = OptimizelyFlutterSdk(testSDKKey); + sdk.initializeClient(); var userContext = await sdk.createUserContext(); - expect(userContext, isNotNull); + expect(userContext, isNull); }); }); @@ -769,11 +792,20 @@ void main() { }); group("getVuid()", () { - test("should succeed", () async { + test("by default should return null vuid", () async { var sdk = OptimizelyFlutterSdk(testSDKKey); + sdk.initializeClient(); + var response = await sdk.getVuid(); + expect(response.success, isTrue); + expect(response.vuid, isNull); + }); + test("should return vuid when enableVuid true", () async { + const settings = SDKSettings(enableVuid: true); + var sdk = OptimizelyFlutterSdk(testSDKKey, sdkSettings: settings); + sdk.initializeClient(); var response = await sdk.getVuid(); expect(response.success, isTrue); - expect(response.vuid, equals(vuid)); + expect(response.vuid, "vuid_123"); }); });