Skip to content

Commit

Permalink
Merge pull request #14 from growthbook/fix/sse-connection-and-prepare…
Browse files Browse the repository at this point in the history
…-features

sse connection and prepare features data fix
  • Loading branch information
vazarkevych authored May 10, 2024
2 parents 162d04f + d345c90 commit e76f5fe
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 181 deletions.
62 changes: 34 additions & 28 deletions lib/src/Features/features_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ class FeatureViewModel {
Future<void> connectBackgroundSync() async {
await source.fetchFeatures(
featureRefreshStrategy: FeatureRefreshStrategy.SERVER_SENT_EVENTS,
(data) => delegate.featuresFetchedSuccessfully(
gbFeatures: data.features, isRemote: false),
(data) {
delegate.featuresFetchedSuccessfully(gbFeatures: data.features!, isRemote: false);
prepareFeaturesData(data);
},
(e, s) => delegate.featuresFetchFailed(
error: GBError(
error: e,
Expand All @@ -39,16 +41,14 @@ class FeatureViewModel {
);
}

Future<void> fetchFeatures(String? apiUrl,
{bool remoteEval = false, RemoteEvalModel? payload}) async {
final receivedData =
await manager.getContent(fileName: Constant.featureCache);
Future<void> fetchFeatures(String? apiUrl, {bool remoteEval = false, RemoteEvalModel? payload}) async {
final receivedData = await manager.getContent(fileName: Constant.featureCache);

if (receivedData == null) {
await source.fetchFeatures(
(data) {
delegate.featuresFetchedSuccessfully(
gbFeatures: data.features,
gbFeatures: data.features!,
isRemote: false,
);
cacheFeatures(data);
Expand All @@ -63,10 +63,20 @@ class FeatureViewModel {
);
} else {
String receivedDataJson = utf8Decoder.convert(receivedData);
final receivedDataJsonMap = json.decode(receivedDataJson);
final data = FeaturedDataModel.fromJson(receivedDataJsonMap);
delegate.featuresFetchedSuccessfully(
gbFeatures: data.features, isRemote: false);
final receiveFeatureJsonMap = json.decode(receivedDataJson);
Map<String, GBFeature> featureMap = {};

if (encryptionKey.isNotEmpty) {
receiveFeatureJsonMap.forEach((key, value) {
if (value is Map<String, dynamic>) {
featureMap[key] = GBFeature.fromJson(value);
}
});
} else {
featureMap = FeaturedDataModel.fromJson(receiveFeatureJsonMap).features ?? {};
}

delegate.featuresFetchedSuccessfully(gbFeatures: featureMap, isRemote: false);
}

if (apiUrl != null) {
Expand Down Expand Up @@ -105,7 +115,7 @@ class FeatureViewModel {

void prepareFeaturesData(FeaturedDataModel data) {
try {
if (data.features.isEmpty) {
if (data.features == null && data.encryptedFeatures == null) {
log("JSON is null.");
} else {
handleValidFeatures(data);
Expand All @@ -116,23 +126,21 @@ class FeatureViewModel {
}

void handleValidFeatures(FeaturedDataModel data) {
if (data.features.isNotEmpty) {
if (data.features != null && data.features!.isNotEmpty) {
delegate.featuresAPIModelSuccessfully(data);
// todo manager content save
// manager.putData(fileName: Constant.featureCache, content: utf8.encode(data.features.toString()));
delegate.featuresFetchedSuccessfully(
gbFeatures: data.features, isRemote: true);
String jsonString = json.encode(data.toJson());
final bytes = utf8Encoder.convert(jsonString);
manager.putData(fileName: Constant.featureCache, content: bytes);
delegate.featuresFetchedSuccessfully(gbFeatures: data.features!, isRemote: true);
} else {
handleInvalidFeatures(data.features);
if (data.encryptedFeatures != null) {
handleEncryptedFeatures(data.encryptedFeatures!);
}
}
}

void handleInvalidFeatures(Map<String, dynamic>? jsonPetitions) {
final encryptedString = jsonPetitions?["encryptedFeatures"];

if (encryptedString == null ||
encryptedString is! String ||
encryptedString.isEmpty) {
void handleEncryptedFeatures(String encryptedFeatures) {
if (encryptedFeatures.isEmpty) {
logError("Failed to parse encrypted data.");
return;
}
Expand All @@ -145,13 +153,12 @@ class FeatureViewModel {
try {
final crypto = Crypto();
final extractedFeatures = crypto.getFeaturesFromEncryptedFeatures(
encryptedString,
encryptedFeatures,
encryptionKey,
);

if (extractedFeatures != null) {
delegate.featuresFetchedSuccessfully(
gbFeatures: extractedFeatures, isRemote: false);
delegate.featuresFetchedSuccessfully(gbFeatures: extractedFeatures, isRemote: false);
final featureData = utf8Encoder.convert(jsonEncode(extractedFeatures));
final featureDataOnUint8List = Uint8List.fromList(featureData);
manager.putData(
Expand Down Expand Up @@ -187,7 +194,6 @@ class FeatureViewModel {
}

void cacheFeatures(FeaturedDataModel data) {
// final GBFeatures features = data.toJson(); //.features;
String jsonString = json.encode(data.toJson());
final bytes = utf8Encoder.convert(jsonString);

Expand Down
15 changes: 9 additions & 6 deletions lib/src/Model/features_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ part 'features_model.g.dart';

@JsonSerializable()
class FeaturedDataModel {
FeaturedDataModel({required this.features});
FeaturedDataModel({
required this.features,
required this.encryptedFeatures,
});

@GBFeaturesConverter()
final GBFeatures features;
final GBFeatures? features;

factory FeaturedDataModel.fromJson(Map<String, dynamic> json) =>
_$FeaturedDataModelFromJson(json);
final String? encryptedFeatures;

Map<String, dynamic> toJson() =>
_$FeaturedDataModelToJson(this);
factory FeaturedDataModel.fromJson(Map<String, dynamic> json) => _$FeaturedDataModelFromJson(json);

Map<String, dynamic> toJson() => _$FeaturedDataModelToJson(this);
}
23 changes: 20 additions & 3 deletions lib/src/Model/features_model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 7 additions & 30 deletions lib/src/Network/network.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ class DioClient extends BaseClient {

Dio get client => _dio;

bool errorOccurred = false;

Future<void> listenAndRetry({
required Dio dio,
required String path,
Expand All @@ -53,13 +51,10 @@ class DioClient extends BaseClient {
);

final data = resp.data;
final statusCode = resp.statusCode;

if (data is ResponseBody) {
data.stream
.cast<List<int>>()
.transform(const Utf8Decoder())
.transform(const SseEventTransformer())
.listen(
data.stream.cast<List<int>>().transform(const Utf8Decoder()).transform(const SseEventTransformer()).listen(
(sseModel) {
if (sseModel.name == "features") {
String jsonData = sseModel.data ?? "";
Expand All @@ -69,21 +64,9 @@ class DioClient extends BaseClient {
},
onError: (dynamic e, dynamic s) async {
onError;

if (!errorOccurred) {
errorOccurred = true;
await Future.delayed(const Duration(seconds: 5));
await listenAndRetry(
dio: dio,
onError: onError,
onSuccess: onSuccess,
path: path,
);
}
},
onDone: () async {
if (!errorOccurred) {
await Future.delayed(const Duration(seconds: 5));
if (statusCode != null && shouldReconnect(statusCode)) {
await listenAndRetry(
dio: dio,
onError: onError,
Expand All @@ -96,19 +79,13 @@ class DioClient extends BaseClient {
}
} catch (error) {
onError;
if (!errorOccurred) {
errorOccurred = true;
await Future.delayed(const Duration(seconds: 5));
await listenAndRetry(
dio: dio,
onError: onError,
onSuccess: onSuccess,
path: path,
);
}
}
}

bool shouldReconnect(int statusCode) {
return statusCode >= 200 && statusCode < 300;
}

@override
Future<void> consumeGetRequest(
String baseUrl,
Expand Down
106 changes: 0 additions & 106 deletions lib/src/Network/see_client.dart

This file was deleted.

Loading

0 comments on commit e76f5fe

Please sign in to comment.