-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathunleash.dart
149 lines (123 loc) · 3.99 KB
/
unleash.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:unleash/src/features.dart';
import 'package:unleash/src/strategies.dart';
import 'package:unleash/src/strategy.dart';
import 'package:unleash/src/unleash_client.dart';
import 'package:unleash/src/unleash_settings.dart';
import 'context.dart';
import 'toggle_backup/_web_toggle_backup.dart';
import 'toggle_backup/toggle_backup.dart';
typedef UpdateCallback = void Function();
class Unleash {
Unleash._internal(
this.settings,
this._onUpdate,
this._unleashClient,
ToggleBackup? toggleBackup,
) : _backupRepository = toggleBackup ?? NoOpToggleBackup();
final UnleashClient _unleashClient;
final UnleashSettings settings;
final UpdateCallback? _onUpdate;
final ToggleBackup _backupRepository;
final List<ActivationStrategy> _activationStrategies = [
DefaultStrategy(),
UserIdStrategy(),
];
/// Collection of all available feature toggles
Features? _features;
/// This timer is responsible for starting a new request
/// every time the given [UnleashSettings.pollingInterval] expired.
Timer? _togglePollingTimer;
/// Unleash Context
/// https://docs.getunleash.io/user_guide/unleash_context
Context? context;
/// Initializes an [Unleash] instance, registers it at the backend and
/// starts to load the feature toggles.
/// [settings] are used to specify the backend and various other settings.
/// A [client] can be used for example to further configure http headers
/// according to your needs.
static Future<Unleash> init(
UnleashSettings settings, {
UnleashClient? client,
UpdateCallback? onUpdate,
ToggleBackup? toggleBackup,
}) async {
final unleash = Unleash._internal(
settings,
onUpdate,
client ?? UnleashClient(settings: settings, client: http.Client()),
toggleBackup,
);
unleash._activationStrategies.addAll(settings.strategies ?? []);
await unleash._register();
await unleash._loadToggles();
unleash._setTogglePollingTimer();
return unleash;
}
bool isEnabled(
String toggleName, {
bool defaultValue = false,
Context? localContext,
}) {
final defaultToggle = FeatureToggle(
name: toggleName,
strategies: null,
description: null,
enabled: defaultValue,
strategy: null,
);
final featureToggle = _features?.features?.firstWhere(
(toggle) => toggle.name == toggleName,
orElse: () => defaultToggle,
);
final toggle = featureToggle ?? defaultToggle;
final isEnabled = toggle.enabled ?? defaultValue;
if (!isEnabled) {
return false;
}
final strategies = toggle.strategies ?? [];
if (strategies.isEmpty) {
return isEnabled;
}
for (final strategy in strategies) {
final foundStrategy = _activationStrategies.firstWhere(
(activationStrategy) => activationStrategy.name == strategy.name,
orElse: () => UnknownStrategy(),
);
final parameters = strategy.parameters ?? <String, dynamic>{};
var currentContext = localContext ?? context;
if (foundStrategy.isEnabled(parameters, currentContext)) {
return true;
}
}
return false;
}
/// Cancels all periodic actions of this Unleash instance
void dispose() {
_togglePollingTimer?.cancel();
}
Future<void> _register() {
return _unleashClient.register(DateTime.now(), _activationStrategies);
}
Future<void> _loadToggles() async {
final features = await _unleashClient.getFeatureToggles();
if (features != null) {
await _backupRepository.save(features);
_onUpdate?.call();
_features = features;
} else {
_features = await _backupRepository.load();
}
}
void _setTogglePollingTimer() {
final pollingInterval = settings.pollingInterval;
// disable polling if no pollingInterval is given
if (pollingInterval == null) {
return;
}
_togglePollingTimer = Timer.periodic(pollingInterval, (timer) {
_loadToggles();
});
}
}