Skip to content

Commit cf99e81

Browse files
Complete the mapping of SplitConfiguration (Flutter SDK config) to JS SDK configuration object
1 parent 505040d commit cf99e81

File tree

6 files changed

+509
-11
lines changed

6 files changed

+509
-11
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ jobs:
2525
run: cd splitio_android/; flutter test
2626
- name: Run flutter splitio_ios test
2727
run: cd splitio_ios/; flutter test
28+
- name: Run flutter splitio_web test
29+
run: cd splitio_web/; flutter test --platform=chrome

splitio_platform_interface/lib/split_configuration.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ class SplitConfiguration {
2121
///
2222
/// [eventFlushInterval] When using .track, how often the events queue is flushed to Split servers.
2323
///
24-
/// [eventsPerPush] Maximum size of the batch to push events.
24+
/// [eventsPerPush] Maximum size of the batch to push events. Not supported in Web.
2525
///
2626
/// [trafficType] The default traffic type for events tracked using the track method. If not specified, every track call should specify a traffic type.
2727
///
2828
/// [enableDebug] (Deprecated; use logLevel instead) If true, the SDK will log debug messages to the console.
2929
///
3030
/// [streamingEnabled] Boolean flag to enable the streaming service as default synchronization mechanism when in foreground. In the event of an issue with streaming, the SDK will fallback to the polling mechanism. If false, the SDK will poll for changes as usual without attempting to use streaming.
3131
///
32-
/// [persistentAttributesEnabled] Enables saving attributes on persistent cache which is loaded as part of the SDK_READY_FROM_CACHE flow. All functions that mutate the stored attributes map affect the persistent cache.
32+
/// [persistentAttributesEnabled] Enables saving attributes on persistent cache which is loaded as part of the SDK_READY_FROM_CACHE flow. All functions that mutate the stored attributes map affect the persistent cache. Not supported in Web.
3333
///
3434
/// [impressionListener] Enables impression listener. If true, generated impressions will be streamed in the impressionsStream() method of Splitio.
3535
///
@@ -41,13 +41,13 @@ class SplitConfiguration {
4141
///
4242
/// [userConsent] User consent status used to control the tracking of events and impressions. Possible values are [UserConsent.granted], [UserConsent.declined], and [UserConsent.unknown].
4343
///
44-
/// [encryptionEnabled] If set to true, the local database contents is encrypted. Defaults to false.
44+
/// [encryptionEnabled] If set to true, the local database contents is encrypted. Defaults to false. Not supported in Web.
4545
///
4646
/// [logLevel] Enables logging according to the level specified. Options are [SplitLogLevel.verbose], [SplitLogLevel.none], [SplitLogLevel.debug], [SplitLogLevel.info], [SplitLogLevel.warning], and [SplitLogLevel.error].
4747
///
4848
/// [readyTimeout] Maximum amount of time in seconds to wait before firing the SDK_READY_TIMED_OUT event. Defaults to 10 seconds.
4949
///
50-
/// [certificatePinningConfiguration] Certificate pinning configuration. Pins need to have the format of a base64 SHA-256 or base64 SHA-1 hashes of the SPKI (ex.: "sha256/7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=").
50+
/// [certificatePinningConfiguration] Certificate pinning configuration. Pins need to have the format of a base64 SHA-256 or base64 SHA-1 hashes of the SPKI (ex.: "sha256/7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y="). Not supported in Web.
5151
SplitConfiguration({
5252
int? featuresRefreshRate,
5353
int? segmentsRefreshRate,

splitio_web/lib/splitio_web.dart

Lines changed: 212 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class SplitioWeb extends SplitioPlatform {
2222
Future<void>? _initFuture;
2323

2424
late JS_IBrowserSDK _factory;
25+
String? _trafficType;
26+
bool _impressionListener = false;
2527

2628
@override
2729
Future<void> init({
@@ -49,6 +51,33 @@ class SplitioWeb extends SplitioPlatform {
4951
// Create factory instance
5052
this._factory = window.splitio!.SplitFactory.callAsFunction(null, config) as JS_IBrowserSDK;
5153

54+
if (sdkConfiguration != null) {
55+
if (sdkConfiguration.configurationMap['trafficType'] is String) {
56+
this._trafficType = sdkConfiguration.configurationMap['trafficType'];
57+
}
58+
59+
if (sdkConfiguration.configurationMap['impressionListener'] is bool) {
60+
this._impressionListener =
61+
sdkConfiguration.configurationMap['impressionListener'];
62+
}
63+
64+
// Log warnings regarding unsupported configs. Not done in _buildConfig to reuse the factory logger
65+
final unsupportedConfigs = [
66+
'encryptionEnabled',
67+
'certificatePinningConfiguration',
68+
'persistentAttributesEnabled',
69+
'eventsPerPush'
70+
];
71+
for (final configName in unsupportedConfigs) {
72+
if (sdkConfiguration.configurationMap[configName] != null) {
73+
this._factory.settings.log.warn.callAsFunction(
74+
this._factory.settings.log,
75+
'Config $configName is not supported by the Web package. This config will be ignored.'
76+
.toJS);
77+
}
78+
}
79+
}
80+
5281
return;
5382
}
5483

@@ -85,17 +114,194 @@ class SplitioWeb extends SplitioPlatform {
85114
}
86115

87116
// Map SplitConfiguration to JS equivalent object
88-
JSObject _buildConfig(String apiKey, String matchingKey, String? bucketingKey, SplitConfiguration? configuration) {
117+
static JSObject _buildConfig(String apiKey, String matchingKey, String? bucketingKey, SplitConfiguration? configuration) {
118+
final config = JSObject();
119+
89120
final core = JSObject();
90121
core.setProperty('authorizationKey'.toJS, apiKey.toJS);
91-
// @TODO: set bucketingKey if provided
92-
core.setProperty('key'.toJS, matchingKey.toJS);
93-
94-
final config = JSObject();
122+
core.setProperty('key'.toJS, _buildKey(matchingKey, bucketingKey));
95123
config.setProperty('core'.toJS, core);
96124

97-
// @TODO: complete config
125+
if (configuration != null) {
126+
final scheduler = JSObject();
127+
if (configuration.configurationMap.containsKey('featuresRefreshRate'))
128+
scheduler.setProperty(
129+
'featuresRefreshRate'.toJS,
130+
(configuration.configurationMap['featuresRefreshRate'] as int)
131+
.toJS);
132+
if (configuration.configurationMap.containsKey('segmentsRefreshRate'))
133+
scheduler.setProperty(
134+
'segmentsRefreshRate'.toJS,
135+
(configuration.configurationMap['segmentsRefreshRate'] as int)
136+
.toJS);
137+
if (configuration.configurationMap.containsKey('impressionsRefreshRate'))
138+
scheduler.setProperty(
139+
'impressionsRefreshRate'.toJS,
140+
(configuration.configurationMap['impressionsRefreshRate'] as int)
141+
.toJS);
142+
if (configuration.configurationMap.containsKey('telemetryRefreshRate'))
143+
scheduler.setProperty(
144+
'telemetryRefreshRate'.toJS,
145+
(configuration.configurationMap['telemetryRefreshRate'] as int)
146+
.toJS);
147+
if (configuration.configurationMap.containsKey('eventsQueueSize'))
148+
scheduler.setProperty('eventsQueueSize'.toJS,
149+
(configuration.configurationMap['eventsQueueSize'] as int).toJS);
150+
if (configuration.configurationMap.containsKey('impressionsQueueSize'))
151+
scheduler.setProperty(
152+
'impressionsQueueSize'.toJS,
153+
(configuration.configurationMap['impressionsQueueSize'] as int)
154+
.toJS);
155+
if (configuration.configurationMap.containsKey('eventFlushInterval'))
156+
scheduler.setProperty('eventsPushRate'.toJS,
157+
(configuration.configurationMap['eventFlushInterval'] as int).toJS);
158+
config.setProperty('scheduler'.toJS, scheduler);
159+
160+
if (configuration.configurationMap.containsKey('streamingEnabled'))
161+
config.setProperty('streamingEnabled'.toJS,
162+
(configuration.configurationMap['streamingEnabled'] as bool).toJS);
163+
164+
final urls = JSObject();
165+
if (configuration.configurationMap.containsKey('sdkEndpoint'))
166+
urls.setProperty('sdk'.toJS,
167+
(configuration.configurationMap['sdkEndpoint'] as String).toJS);
168+
if (configuration.configurationMap.containsKey('eventsEndpoint'))
169+
urls.setProperty('events'.toJS,
170+
(configuration.configurationMap['eventsEndpoint'] as String).toJS);
171+
if (configuration.configurationMap.containsKey('authServiceEndpoint'))
172+
urls.setProperty(
173+
'auth'.toJS,
174+
(configuration.configurationMap['authServiceEndpoint'] as String)
175+
.toJS);
176+
if (configuration.configurationMap
177+
.containsKey('streamingServiceEndpoint'))
178+
urls.setProperty(
179+
'streaming'.toJS,
180+
(configuration.configurationMap['streamingServiceEndpoint']
181+
as String)
182+
.toJS);
183+
if (configuration.configurationMap
184+
.containsKey('telemetryServiceEndpoint'))
185+
urls.setProperty(
186+
'telemetry'.toJS,
187+
(configuration.configurationMap['telemetryServiceEndpoint']
188+
as String)
189+
.toJS);
190+
config.setProperty('urls'.toJS, urls);
191+
192+
final sync = JSObject();
193+
if (configuration.configurationMap['impressionsMode'] != null) {
194+
sync.setProperty(
195+
'impressionsMode'.toJS,
196+
(configuration.configurationMap['impressionsMode'] as String)
197+
.toUpperCase()
198+
.toJS);
199+
}
200+
201+
if (configuration.configurationMap['syncEnabled'] != null) {
202+
sync.setProperty('enabled'.toJS,
203+
(configuration.configurationMap['syncEnabled'] as bool).toJS);
204+
}
205+
206+
if (configuration.configurationMap['syncConfig'] != null) {
207+
final syncConfig = configuration.configurationMap['syncConfig']
208+
as Map<String, List<String>>;
209+
final List<Map<String, dynamic>> splitFilters = [];
210+
211+
if (syncConfig['syncConfigNames'] != null &&
212+
syncConfig['syncConfigNames']!.isNotEmpty) {
213+
splitFilters
214+
.add({'type': 'byName', 'values': syncConfig['syncConfigNames']});
215+
}
216+
217+
if (syncConfig['syncConfigPrefixes'] != null &&
218+
syncConfig['syncConfigPrefixes']!.isNotEmpty) {
219+
splitFilters.add(
220+
{'type': 'byPrefix', 'values': syncConfig['syncConfigPrefixes']});
221+
}
222+
223+
if (syncConfig['syncConfigFlagSets'] != null &&
224+
syncConfig['syncConfigFlagSets']!.isNotEmpty) {
225+
splitFilters.add(
226+
{'type': 'bySet', 'values': syncConfig['syncConfigFlagSets']});
227+
}
228+
sync.setProperty('splitFilters'.toJS, splitFilters.jsify());
229+
}
230+
config.setProperty('sync'.toJS, sync);
231+
232+
if (configuration.configurationMap['userConsent'] != null) {
233+
config.setProperty(
234+
'userConsent'.toJS,
235+
(configuration.configurationMap['userConsent'] as String)
236+
.toUpperCase()
237+
.toJS);
238+
}
239+
240+
final logLevel = configuration.configurationMap['logLevel'];
241+
if (logLevel is String) {
242+
final logger = logLevel == SplitLogLevel.verbose.toString() ||
243+
logLevel == SplitLogLevel.debug.toString()
244+
? window.splitio!.DebugLogger?.callAsFunction(null)
245+
: logLevel == SplitLogLevel.info.toString()
246+
? window.splitio!.InfoLogger?.callAsFunction(null)
247+
: logLevel == SplitLogLevel.warning.toString()
248+
? window.splitio!.WarnLogger?.callAsFunction(null)
249+
: logLevel == SplitLogLevel.error.toString()
250+
? window.splitio!.ErrorLogger?.callAsFunction(null)
251+
: null;
252+
if (logger != null) {
253+
config.setProperty('debug'.toJS, logger); // Browser SDK
254+
} else {
255+
config.setProperty(
256+
'debug'.toJS, logLevel.toUpperCase().toJS); // JS SDK
257+
}
258+
} else if (configuration.configurationMap['enableDebug'] == true) {
259+
config.setProperty(
260+
'debug'.toJS, window.splitio!.DebugLogger?.callAsFunction(null));
261+
}
262+
263+
if (configuration.configurationMap['readyTimeout'] != null) {
264+
final startup = JSObject();
265+
startup.setProperty('readyTimeout'.toJS,
266+
(configuration.configurationMap['readyTimeout'] as int).toJS);
267+
config.setProperty('startup'.toJS, startup);
268+
}
269+
270+
final storageOptions = JSObject();
271+
storageOptions.setProperty('type'.toJS, 'LOCALSTORAGE'.toJS);
272+
if (configuration.configurationMap['rolloutCacheConfiguration'] != null) {
273+
final rolloutCacheConfiguration =
274+
configuration.configurationMap['rolloutCacheConfiguration']
275+
as Map<String, dynamic>;
276+
if (rolloutCacheConfiguration['expirationDays'] != null) {
277+
storageOptions.setProperty('expirationDays'.toJS,
278+
(rolloutCacheConfiguration['expirationDays'] as int).toJS);
279+
}
280+
if (rolloutCacheConfiguration['clearOnInit'] != null) {
281+
storageOptions.setProperty('clearOnInit'.toJS,
282+
(rolloutCacheConfiguration['clearOnInit'] as bool).toJS);
283+
}
284+
}
285+
if (window.splitio!.InLocalStorage != null) {
286+
config.setProperty(
287+
'storage'.toJS,
288+
window.splitio!.InLocalStorage
289+
?.callAsFunction(null, storageOptions)); // Browser SDK
290+
} else {
291+
config.setProperty('storage'.toJS, storageOptions); // JS SDK
292+
}
293+
}
294+
98295
return config;
99296
}
100297

298+
static JSAny _buildKey(String matchingKey, String? bucketingKey) {
299+
if (bucketingKey != null) {
300+
final splitKey = JSObject();
301+
splitKey.setProperty('matchingKey'.toJS, matchingKey.toJS);
302+
splitKey.setProperty('bucketingKey'.toJS, bucketingKey.toJS);
303+
return splitKey;
304+
}
305+
return matchingKey.toJS;
306+
}
101307
}
Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
import 'dart:js_interop';
22

33
@JS()
4-
extension type JS_IBrowserSDK._(JSObject _) implements JSObject {}
4+
extension type JS_Logger._(JSObject _) implements JSObject {
5+
external JSFunction warn;
6+
}
7+
8+
@JS()
9+
extension type JS_ISettings._(JSObject _) implements JSObject {
10+
external JS_Logger log;
11+
}
12+
13+
@JS()
14+
extension type JS_IBrowserSDK._(JSObject _) implements JSObject {
15+
external JS_ISettings settings;
16+
}
517

618
@JS()
719
extension type JS_BrowserSDKPackage._(JSObject _) implements JSObject {
820
external JSFunction SplitFactory;
21+
external JSFunction? InLocalStorage;
22+
external JSFunction? DebugLogger;
23+
external JSFunction? InfoLogger;
24+
external JSFunction? WarnLogger;
25+
external JSFunction? ErrorLogger;
926
}

0 commit comments

Comments
 (0)