From 6383d19fa3171156ec07a6d5ddace1752ebcba45 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 7 Aug 2025 17:34:36 -0300 Subject: [PATCH 1/3] Add prereqs to SplitView --- .../lib/split_prerequisite.dart | 49 +++++++++++++++++++ .../lib/split_view.dart | 22 +++++++-- splitio_platform_interface/pubspec.yaml | 2 +- .../test/prerequisites_test.dart | 32 ++++++++++++ .../test/split_view_test.dart | 15 ++++++ 5 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 splitio_platform_interface/lib/split_prerequisite.dart create mode 100644 splitio_platform_interface/test/prerequisites_test.dart diff --git a/splitio_platform_interface/lib/split_prerequisite.dart b/splitio_platform_interface/lib/split_prerequisite.dart new file mode 100644 index 0000000..b8be081 --- /dev/null +++ b/splitio_platform_interface/lib/split_prerequisite.dart @@ -0,0 +1,49 @@ +class Prerequisite { + final String _name; + + final Set _treatments; + + String get name => _name; + + Set get treatments => _treatments; + + Prerequisite(this._name, this._treatments); + + static Prerequisite fromEntry(el) { + final String name = (el['n'] ?? el['n:'] ?? '').toString(); + final List rawTreatments = (el['t'] as List?) ?? []; + final Set treatments = + rawTreatments.map((e) => e.toString()).toSet(); + + return Prerequisite(name, treatments); + } + + @override + String toString() { + return '''Prerequisite = { + name: $name, + treatments: $treatments + }'''; + } + + equals(Prerequisite other) { + return name == other.name && treatments == other.treatments; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is Prerequisite && + name == other.name && + other.treatments.containsAll(treatments); + } + + @override + int get hashCode { + int treatmentsHash = 0; + for (final t in _treatments) { + treatmentsHash ^= t.hashCode; + } + return name.hashCode ^ treatmentsHash; + } +} diff --git a/splitio_platform_interface/lib/split_view.dart b/splitio_platform_interface/lib/split_view.dart index f8e0a30..e5f9fc9 100644 --- a/splitio_platform_interface/lib/split_view.dart +++ b/splitio_platform_interface/lib/split_view.dart @@ -1,7 +1,8 @@ import 'dart:core'; -class SplitView { +import 'package:splitio_platform_interface/split_prerequisite.dart'; +class SplitView { static const String _keyName = 'name'; static const String _keyTrafficType = 'trafficType'; static const String _keyKilled = 'killed'; @@ -11,6 +12,7 @@ class SplitView { static const String _keyDefaultTreatment = 'defaultTreatment'; static const String _keySets = 'sets'; static const String _keyImpressionsDisabled = 'impressionsDisabled'; + static const String _keyPrerequisites = 'prerequisites'; String name; String trafficType; @@ -21,10 +23,14 @@ class SplitView { String defaultTreatment; List sets = []; bool impressionsDisabled = false; + Set prerequisites = {}; SplitView(this.name, this.trafficType, this.killed, this.treatments, this.changeNumber, this.configs, - [this.defaultTreatment = '', this.sets = const [], this.impressionsDisabled = false]); + [this.defaultTreatment = '', + this.sets = const [], + this.impressionsDisabled = false, + this.prerequisites = const {}]); static SplitView? fromEntry(Map? entry) { if (entry == null || entry.isEmpty) { @@ -48,6 +54,14 @@ class SplitView { entry[_keyImpressionsDisabled] = false; } + if (entry[_keyPrerequisites] == null) { + entry[_keyPrerequisites] = []; + } + + final List prereqRaw = (entry[_keyPrerequisites] as List?) ?? []; + final Set prerequisites = + prereqRaw.map((el) => Prerequisite.fromEntry(el)).toSet(); + return SplitView( entry[_keyName], entry[_keyTrafficType], @@ -57,8 +71,8 @@ class SplitView { mappedConfig, entry[_keyDefaultTreatment] ?? '', (entry[_keySets] as List).map((el) => el as String).toList(), - entry[_keyImpressionsDisabled] ?? false - ); + entry[_keyImpressionsDisabled] ?? false, + prerequisites); } @override diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index 72db712..0200526 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: splitio_platform_interface description: A common platform interface for the splitio plugin. # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.5.0 +version: 1.6.0-rc.1 repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_platform_interface environment: diff --git a/splitio_platform_interface/test/prerequisites_test.dart b/splitio_platform_interface/test/prerequisites_test.dart new file mode 100644 index 0000000..c3c0909 --- /dev/null +++ b/splitio_platform_interface/test/prerequisites_test.dart @@ -0,0 +1,32 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_platform_interface/split_prerequisite.dart'; + +void main() { + group('Prerequisite', () { + test('fromEntry creates correct instance', () { + final entry = { + 'n': 'feature1', + 't': ['on', 'off'] + }; + final prereq = Prerequisite.fromEntry(entry); + expect(prereq.name, 'feature1'); + expect(prereq.treatments, {'on', 'off'}); + }); + + test('equality and hashCode', () { + final a = Prerequisite('feat', {'a', 'b'}); + final b = Prerequisite('feat', {'b', 'a'}); + final c = Prerequisite('feat2', {'a', 'b'}); + expect(a, equals(b)); + expect(a.hashCode, equals(b.hashCode)); + expect(a, isNot(equals(c))); + }); + + test('toString contains name and treatments', () { + final prereq = Prerequisite('myFeature', {'t1'}); + final str = prereq.toString(); + expect(str, contains('myFeature')); + expect(str, contains('t1')); + }); + }); +} diff --git a/splitio_platform_interface/test/split_view_test.dart b/splitio_platform_interface/test/split_view_test.dart index a45f6eb..f3040b5 100644 --- a/splitio_platform_interface/test/split_view_test.dart +++ b/splitio_platform_interface/test/split_view_test.dart @@ -1,4 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_platform_interface/split_prerequisite.dart'; import 'package:splitio_platform_interface/split_view.dart'; void main() { @@ -25,6 +26,16 @@ void main() { 'defaultTreatment': 'on', 'sets': ['set1', 'set2'], 'impressionsDisabled': true, + 'prerequisites': [ + { + 'n:': 'pre1', + 't': ['on', 'off'] + }, + { + 'n': 'pre2', + 't': ['off'] + } + ], }); expect(splitView?.name, 'my_split'); @@ -36,5 +47,9 @@ void main() { expect(splitView?.defaultTreatment, 'on'); expect(splitView?.sets, ['set1', 'set2']); expect(splitView?.impressionsDisabled, true); + expect(splitView?.prerequisites, { + Prerequisite('pre1', {'on', 'off'}), + Prerequisite('pre2', {'off'}) + }); }); } From aa924b18525687f8993d2944585c5cc3616d062e Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Mon, 11 Aug 2025 17:02:32 -0300 Subject: [PATCH 2/3] Update gradle --- splitio/example/android/app/build.gradle | 29 +++--------------------- splitio/example/android/build.gradle | 11 --------- splitio/example/android/settings.gradle | 29 +++++++++++++++++------- splitio_android/android/build.gradle | 12 ++++++---- 4 files changed, 31 insertions(+), 50 deletions(-) diff --git a/splitio/example/android/app/build.gradle b/splitio/example/android/app/build.gradle index e231f55..947a9f1 100644 --- a/splitio/example/android/app/build.gradle +++ b/splitio/example/android/app/build.gradle @@ -1,29 +1,8 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' +plugins { + id 'com.android.application' + id 'dev.flutter.flutter-gradle-plugin' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdk flutter.compileSdkVersion @@ -37,8 +16,6 @@ android { applicationId "io.split.splitio_example" minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName } buildTypes { diff --git a/splitio/example/android/build.gradle b/splitio/example/android/build.gradle index 45f577c..bc157bd 100644 --- a/splitio/example/android/build.gradle +++ b/splitio/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.4.0' - } -} - allprojects { repositories { google() diff --git a/splitio/example/android/settings.gradle b/splitio/example/android/settings.gradle index 44e62bc..ea55ff6 100644 --- a/splitio/example/android/settings.gradle +++ b/splitio/example/android/settings.gradle @@ -1,11 +1,24 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" // apply true + id "com.android.application" version "8.4.0" apply false +} + +include ":app" \ No newline at end of file diff --git a/splitio_android/android/build.gradle b/splitio_android/android/build.gradle index e808137..0cd5200 100644 --- a/splitio_android/android/build.gradle +++ b/splitio_android/android/build.gradle @@ -1,6 +1,3 @@ -group 'io.split.splitio' -version '0.0.1' - buildscript { repositories { google() @@ -12,6 +9,10 @@ buildscript { } } +plugins { + id 'com.android.library' +} + rootProject.allprojects { repositories { google() @@ -19,7 +20,8 @@ rootProject.allprojects { } } -apply plugin: 'com.android.library' +group 'io.split.splitio' +version '0.0.1' android { compileSdk 31 @@ -36,7 +38,7 @@ android { } dependencies { - implementation 'io.split.client:android-client:5.1.0-rc1' + implementation 'io.split.client:android-client:5.3.1' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.12.4' From 4ee106067e25b58d59b319b9a9fd0ea37bac79d9 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Mon, 11 Aug 2025 17:22:51 -0300 Subject: [PATCH 3/3] Update android tests --- .../main/java/io/split/splitio/ImpressionListenerImp.java | 2 +- .../java/io/split/splitio/ImpressionListenerImpTest.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/splitio_android/android/src/main/java/io/split/splitio/ImpressionListenerImp.java b/splitio_android/android/src/main/java/io/split/splitio/ImpressionListenerImp.java index 838d76b..38d9c96 100644 --- a/splitio_android/android/src/main/java/io/split/splitio/ImpressionListenerImp.java +++ b/splitio_android/android/src/main/java/io/split/splitio/ImpressionListenerImp.java @@ -33,7 +33,6 @@ public void log(Impression impression) { public void close() { } - private static Map impressionToMap(final Impression impression) { final Map impressionMap = new HashMap<>(); @@ -45,6 +44,7 @@ private static Map impressionToMap(final Impression impression) impressionMap.put("appliedRule", impression.appliedRule()); impressionMap.put("changeNumber", impression.changeNumber()); impressionMap.put("attributes", impression.attributes()); + impressionMap.put("properties", impression.properties()); return impressionMap; } diff --git a/splitio_android/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java b/splitio_android/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java index 69be7b9..91b624c 100644 --- a/splitio_android/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java +++ b/splitio_android/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java @@ -31,7 +31,7 @@ public void setUp() { @Test public void loggingInvokesMethodOnMethodChannel() { - Impression impression = new Impression("key", null, "my_split", "on", 20021002, "on treatment", 1002L, Collections.emptyMap()); + Impression impression = new Impression("key", null, "my_split", "on", 20021002, "on treatment", 1002L, Collections.emptyMap(), "[{\"prop1\", \"value1\"}, {\"prop2\", \"value2\"}]"); mImpressionListener.log(impression); verify(mMethodChannel).invokeMethod(eq("impressionLog"), any()); @@ -39,7 +39,7 @@ public void loggingInvokesMethodOnMethodChannel() { @Test public void loggingInvokesMethodOnMethodChannelWithCorrectArgument() { - Impression impression = new Impression("key", null, "my_split", "on", 20021002, "on treatment", 1002L, Collections.singletonMap("age", 25)); + Impression impression = new Impression("key", null, "my_split", "on", 20021002, "on treatment", 1002L, Collections.singletonMap("age", 25), "[{\"prop1\", \"value1\"}, {\"prop2\", \"value2\"}]"); Map expectedImpressionMap = new HashMap<>(); expectedImpressionMap.put("key", "key"); expectedImpressionMap.put("bucketingKey", null); @@ -49,6 +49,7 @@ public void loggingInvokesMethodOnMethodChannelWithCorrectArgument() { expectedImpressionMap.put("appliedRule", "on treatment"); expectedImpressionMap.put("changeNumber", 1002L); expectedImpressionMap.put("attributes", Collections.singletonMap("age", 25)); + expectedImpressionMap.put("properties", "[{\"prop1\", \"value1\"}, {\"prop2\", \"value2\"}]"); mImpressionListener.log(impression); verify(mMethodChannel).invokeMethod("impressionLog", expectedImpressionMap);