guru_sdk/guru_app/lib/firebase/remoteconfig/remote_config_manager.dart

223 lines
7.4 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:guru_app/ads/core/ads_config.dart';
import 'package:guru_app/analytics/data/analytics_model.dart';
import 'package:guru_app/firebase/firebase.dart';
import 'package:guru_app/firebase/remoteconfig/reserved_remote_config_models.dart';
import 'package:guru_app/guru_app.dart';
import 'package:guru_utils/http/http_model.dart';
import 'package:guru_utils/log/log.dart';
import 'package:rxdart/rxdart.dart';
import 'package:guru_utils/remote/remote_config.dart';
part "remote_config_interface.dart";
/// Created by Haoyi on 2020/4/22
///
part "remote_config_reserved_constants.dart";
class RemoteConfigManager extends IRemoteConfig {
final BehaviorSubject<FirebaseRemoteConfigWrapper?> _subject = BehaviorSubject.seeded(null);
static RemoteConfigManager? _instance;
static RemoteConfigManager _getInstance() {
_instance ??= RemoteConfigManager._internal();
return _instance!;
}
factory RemoteConfigManager() => _getInstance();
static RemoteConfigManager get instance => _getInstance();
static final RegExp invalidABKeyRegExp = RegExp('[^a-zA-Z0-9_-]');
RemoteConfigManager._internal();
Future<void> init(Map<String, dynamic> defaultConfigs) async {
final remoteConfig = FirebaseRemoteConfig.instance;
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 15),
minimumFetchInterval: const Duration(hours: 2),
));
_subject.add(FirebaseRemoteConfigWrapper._(remoteConfig));
try {
await remoteConfig.setDefaults(defaultConfigs);
await remoteConfig.activate();
} catch (exception) {
Log.d("Unable to fetch remote config. Cached or default values will be used!",
error: exception);
} finally {
_subject.add(FirebaseRemoteConfigWrapper._(remoteConfig));
}
}
Future fetchAndActivate() async {
final remoteConfig = FirebaseRemoteConfig.instance;
try {
await remoteConfig.fetchAndActivate();
} catch (exception) {
Log.d("Unable to fetch remote config. Cached or default values will be used!",
error: exception);
} finally {
_subject.add(FirebaseRemoteConfigWrapper._(remoteConfig));
}
}
Future<String> dumpString() async {
final config = FirebaseRemoteConfig.instance;
final data = config.getAll();
String result = "";
for (var entry in data.entries) {
result += "[${entry.key}] ==> (${entry.value.asString()})\n";
}
return result;
}
static String valueSourceToString(ValueSource source) {
switch (source) {
case ValueSource.valueRemote:
return "Remote";
case ValueSource.valueStatic:
return "Static";
default:
return "Default";
}
}
Map<String, String> allData() {
final config = FirebaseRemoteConfig.instance;
final data = config.getAll();
final result = {
for (var entry in data.entries)
"${entry.key} [${valueSourceToString(entry.value.source)}]": entry.value.asString()
};
result["last_fetch_remote_config_time"] = config.lastFetchTime.toString();
result["last_fetch_remote_config_status"] = config.lastFetchStatus.toString();
return result;
}
Future forceFetch({bool debug = false}) async {
final remoteConfig = FirebaseRemoteConfig.instance;
try {
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 15),
minimumFetchInterval: const Duration(seconds: 0),
));
await remoteConfig.fetchAndActivate();
} catch (exception) {
Log.d("Unable to fetch remote config. Cached or default values will be used $exception",
error: exception);
if (debug) {
rethrow;
}
} finally {
_subject.add(FirebaseRemoteConfigWrapper._(remoteConfig));
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 15),
minimumFetchInterval: const Duration(hours: 2),
));
}
}
Map<String, String> getABProperties() {
final config = FirebaseRemoteConfig.instance;
final data = config.getAll();
final result = <String, String>{};
final invalidABKeys = <String>{};
dynamic cause;
for (var entry in data.entries) {
final valueStr = entry.value.asString();
if (valueStr.contains("guru_ab_")) {
try {
final jsonValue = json.decode(valueStr);
if (jsonValue is Map<String, dynamic>) {
for (var jsonEntry in jsonValue.entries) {
if (jsonEntry.key.contains("guru_ab_")) {
String abName = jsonEntry.key.replaceFirst("guru_ab_", "");
if (abName.contains(invalidABKeyRegExp)) {
Log.w("abName($abName) length is invalid! $abName");
invalidABKeys.add(abName);
} else {
if (abName.length > 20) {
invalidABKeys.add(abName);
abName = abName.substring(0, 20);
}
result[GuruAnalytics.buildVariantKey(abName)] = jsonEntry.value.toString();
Log.i("abName:ab_$abName value:${jsonEntry.value}");
}
}
}
}
} catch (error, stacktrace) {
Log.w("decode json error! $error");
cause = error;
}
}
}
if (invalidABKeys.isNotEmpty) {
GuruAnalytics.instance
.logException(InvalidABPropertyKeysException(invalidABKeys, cause: cause));
}
return result;
}
@override
bool? getBool(String name, {bool? defaultValue}) => _subject.value?.getBool(name) ?? defaultValue;
@override
String? getString(String name, {String? defaultValue}) =>
_subject.value?.getString(name) ??
defaultValue ??
RemoteConfigReservedConstants.getDefaultConfigString(name);
@override
double? getDouble(String name, {double? defaultValue}) =>
_subject.value?.getDouble(name) ?? defaultValue;
@override
int? getInt(String name, {int? defaultValue}) => _subject.value?.getInt(name) ?? defaultValue;
Stream<FirebaseRemoteConfigWrapper> observeConfig() => _subject.stream
.map((config) => config ?? FirebaseRemoteConfigWrapper._(FirebaseRemoteConfig.instance));
@override
Stream<bool?> observeBool(String name, {bool? defaultValue}) =>
observeConfig().map((config) => config.getBool(name));
@override
Stream<String?> observeString(String name, {String? defaultValue}) =>
observeConfig().map((config) => config.getString(name));
@override
Stream<double?> observeDouble(String name, {double? defaultValue}) =>
observeConfig().map((config) => config.getDouble(name));
@override
Stream<int?> observeInt(String name, {int? defaultValue}) =>
observeConfig().map((config) => config.getInt(name));
}
class FirebaseRemoteConfigWrapper {
final FirebaseRemoteConfig config;
const FirebaseRemoteConfigWrapper._(this.config);
DateTime get lastFetchTime => config.lastFetchTime;
RemoteConfigFetchStatus get lastFetchStatus => config.lastFetchStatus;
static String _key(String key) => GuruApp.instance.getRemoteConfigKey(key);
String getString(String key) => config.getString(_key(key));
bool getBool(String key) => config.getBool(_key(key));
double getDouble(String key) => config.getDouble(_key(key));
int getInt(String key) => config.getInt(_key(key));
}