From b1d9a30e8e692d328f0ec309968d7152973c26bc Mon Sep 17 00:00:00 2001 From: Mark Fransen Date: Wed, 12 Apr 2023 14:51:57 -0700 Subject: [PATCH] Update init to take features parameter: Map Recently the growthbook_sdk_flutter package was updated. This change removed the ability to "seed" the feature values which are used while the network request is happening or as fallback if the request fails. Alongside this work, I have updated the Uptech fork of growthbook_sdk_flutter to allow passing in "seeded" features and values at init, instead of after. This commits points the dependency at the Uptech fork, which is still in review. Add features param to init constructor. Add Deprecated tag to seeds so we know, while it is still possible to use that parameter, we should be using features. Add comments explaining why we have both params and what changes in the growthbook_sdk_flutter package inspired these changes. If a Map is passed in to the seeds param, we convert them to the new feature Map and pass that map along. Update the tests to use GBFeature for map values. --- lib/src/uptech_growthbook_wrapper.dart | 48 +++++++++---- test/uptech_growthbook_sdk_flutter_test.dart | 73 ++++++++++---------- 2 files changed, 72 insertions(+), 49 deletions(-) diff --git a/lib/src/uptech_growthbook_wrapper.dart b/lib/src/uptech_growthbook_wrapper.dart index 53262d6..d1f2cd6 100644 --- a/lib/src/uptech_growthbook_wrapper.dart +++ b/lib/src/uptech_growthbook_wrapper.dart @@ -27,10 +27,25 @@ class UptechGrowthBookWrapper { /// Initialize for use in app, seeds allow you to specify value of /// toggles prior to fetching remote toggle states. These will be /// the values if on init it fails to fetch the toggles from the remote. - Future init( - {Map? seeds, - Map? overrides, - Map? attributes}) async { + Future init({ + // Updates to the growthbook_sdk_flutter package changed the structure of features + // This workaround allows seeded features to still be passed in using the old format, + // as well as features using the new format with GBFeature. We simply convert the + // seeds into GBFeatures and add them to the final Map. + Map features = const {}, + @Deprecated('Instead use "features" as Map') + Map seeds = const {}, + Map? overrides, + Map? attributes, + }) async { + Map finalFeatures = Map.from(features); + // Convert seeds to GBFeatures and add to finalFeatures Map + if (seeds.isNotEmpty) { + seeds.forEach((key, value) { + finalFeatures[key] = GBFeature(defaultValue: value); + }); + } + _overrides.clear(); _attributes.clear(); if (overrides != null) { @@ -40,13 +55,18 @@ class UptechGrowthBookWrapper { _attributes.addAll(attributes); } _client = await _createLiveClient( - apiHost: apiHost, clientKey: clientKey, seeds: seeds); + apiHost: apiHost, + clientKey: clientKey, + features: finalFeatures, + ); await refresh(); } /// Initialize for use in automated test suite Future initForTests( - {Map? seeds, + {Map features = const {}, + @Deprecated('Instead use "features" as Map') + Map seeds = const {}, Map? overrides, Map? attributes, List>? rules}) async { @@ -102,17 +122,17 @@ class UptechGrowthBookWrapper { Future _createLiveClient({ required String apiHost, required String clientKey, - required Map? seeds, + required Map features, }) async { final app = await GBSDKBuilderApp( - apiKey: clientKey, - qaMode: false, - attributes: _attributes, - hostURL: apiHost, - growthBookTrackingCallBack: (gbExperiment, gbExperimentResult) {}) - .initialize(); + apiKey: clientKey, + qaMode: false, + attributes: _attributes, + hostURL: apiHost, + growthBookTrackingCallBack: (gbExperiment, gbExperimentResult) {}, + features: features, + ).initialize(); - app.featuresFetchedSuccessfully(_seedsToGBFeatures(seeds: seeds)); return app; } diff --git a/test/uptech_growthbook_sdk_flutter_test.dart b/test/uptech_growthbook_sdk_flutter_test.dart index 9416eec..fa57360 100644 --- a/test/uptech_growthbook_sdk_flutter_test.dart +++ b/test/uptech_growthbook_sdk_flutter_test.dart @@ -1,4 +1,5 @@ import 'package:flutter/widgets.dart'; +import 'package:growthbook_sdk_flutter/growthbook_sdk_flutter.dart'; import 'package:uptech_growthbook_sdk_flutter/uptech_growthbook_sdk_flutter.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -17,7 +18,8 @@ void main() { final instance = UptechGrowthBookWrapper( apiHost: 'https://cdn.growthbook.io/', clientKey: 'sdk-rcGyvixKw6PHXQ24'); - await instance.init(seeds: {'my-feature': false}); + await instance + .init(features: {'my-feature': GBFeature(defaultValue: false)}); await instance.refresh(); expect(instance.isOn('my-feature'), isTrue); }); @@ -28,7 +30,8 @@ void main() { final instance = UptechGrowthBookWrapper( apiHost: 'https://cdn.growthbook.io/', clientKey: 'sdk-rcGyvixKw6PHXQ24'); - await instance.init(seeds: {'my-value-feature': 'bar'}); + await instance + .init(features: {'my-value-feature': GBFeature(defaultValue: 'bar')}); await instance.refresh(); expect(instance.value('my-value-feature'), 'foo'); }); @@ -38,8 +41,8 @@ void main() { group('#isOn', () { group('when no value is found for the feature', () { setUp(() async { - await ToglTest.instance.initForTests(seeds: { - 'some-feature-name': true, + await ToglTest.instance.initForTests(features: { + 'some-feature-name': GBFeature(defaultValue: true), }); }); @@ -50,8 +53,8 @@ void main() { group('when a feature value is present', () { setUp(() async { - await ToglTest.instance.initForTests(seeds: { - 'some-feature-name': true, + await ToglTest.instance.initForTests(features: { + 'some-feature-name': GBFeature(defaultValue: true), }); }); @@ -77,8 +80,8 @@ void main() { setUp(() async { const String greaterThan = '\$gt'; await ToglTest.instance.initForTests( - seeds: { - 'some-feature': false, + features: { + 'some-feature': GBFeature(defaultValue: false), }, rules: [ { @@ -101,8 +104,8 @@ void main() { setUp(() async { const String greaterThan = '\$gt'; await ToglTest.instance.initForTests( - seeds: { - 'some-feature': false, + features: { + 'some-feature': GBFeature(defaultValue: false), }, rules: [ { @@ -125,8 +128,8 @@ void main() { setUp(() async { const String greaterThan = '\$gt'; await ToglTest.instance.initForTests( - seeds: { - 'some-feature': false, + features: { + 'some-feature': GBFeature(defaultValue: false), }, rules: [ { @@ -149,8 +152,8 @@ void main() { setUp(() async { const String greaterThan = '\$gt'; await ToglTest.instance.initForTests( - seeds: { - 'some-feature': false, + features: { + 'some-feature': GBFeature(defaultValue: false), }, rules: [ { @@ -173,8 +176,8 @@ void main() { setUp(() async { const String greaterThan = '\$gt'; await ToglTest.instance.initForTests( - seeds: { - 'some-feature': false, + features: { + 'some-feature': GBFeature(defaultValue: false), }, rules: [ { @@ -197,8 +200,8 @@ void main() { setUp(() async { const String greaterThan = '\$gt'; await ToglTest.instance.initForTests( - seeds: { - 'some-feature': false, + features: { + 'some-feature': GBFeature(defaultValue: false), }, rules: [ { @@ -223,8 +226,8 @@ void main() { setUp(() async { const String greaterThan = '\$gt'; await ToglTest.instance.initForTests( - seeds: { - 'some-feature': false, + features: { + 'some-feature': GBFeature(defaultValue: false), }, rules: [ { @@ -247,8 +250,8 @@ void main() { setUp(() async { const String greaterThan = '\$gt'; await ToglTest.instance.initForTests( - seeds: { - 'some-feature': false, + features: { + 'some-feature': GBFeature(defaultValue: false), }, rules: [ { @@ -273,10 +276,10 @@ void main() { group('#value', () { group('when no value is found for the feature', () { setUp(() async { - await ToglTest.instance.initForTests(seeds: { - 'string-value-feature': 'value', - 'int-value-feature': 1, - 'bool-value-feature': true, + await ToglTest.instance.initForTests(features: { + 'string-value-feature': GBFeature(defaultValue: 'value'), + 'int-value-feature': GBFeature(defaultValue: 1), + 'bool-value-feature': GBFeature(defaultValue: true), }); }); @@ -287,10 +290,10 @@ void main() { group('when a feature value is present', () { setUp(() async { - await ToglTest.instance.initForTests(seeds: { - 'string-value-feature': 'value', - 'int-value-feature': 1, - 'bool-value-feature': true, + await ToglTest.instance.initForTests(features: { + 'string-value-feature': GBFeature(defaultValue: 'value'), + 'int-value-feature': GBFeature(defaultValue: 1), + 'bool-value-feature': GBFeature(defaultValue: true), }); }); @@ -306,9 +309,9 @@ void main() { setUp(() async { await ToglTest.instance.initForTests( overrides: { - 'string-value-feature': 'value', - 'int-value-feature': 1, - 'bool-value-feature': true, + 'string-value-feature': GBFeature(defaultValue: 'value'), + 'int-value-feature': GBFeature(defaultValue: 1), + 'bool-value-feature': GBFeature(defaultValue: true), }, ); }); @@ -325,8 +328,8 @@ void main() { group('loadOverridesFromAssets', () { setUp(() async { - await ToglTest.instance.initForTests(seeds: { - 'some-feature-name': true, + await ToglTest.instance.initForTests(features: { + 'some-feature-name': GBFeature(defaultValue: true), }); });