featurevisor 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +9 -2
- data/bin/commands/test.rb +1 -1
- data/lib/featurevisor/bucketer.rb +62 -8
- data/lib/featurevisor/child_instance.rb +43 -1
- data/lib/featurevisor/conditions.rb +63 -34
- data/lib/featurevisor/{logger.rb → diagnostics.rb} +19 -52
- data/lib/featurevisor/evaluate.rb +51 -50
- data/lib/featurevisor/{datafile_reader.rb → evaluation_data_provider.rb} +27 -16
- data/lib/featurevisor/events.rb +2 -2
- data/lib/featurevisor/instance.rb +74 -40
- data/lib/featurevisor/version.rb +1 -1
- data/lib/featurevisor.rb +4 -4
- metadata +3 -3
|
@@ -25,9 +25,9 @@ module Featurevisor
|
|
|
25
25
|
def initialize(options = {})
|
|
26
26
|
# from options
|
|
27
27
|
@context = options[:context] || {}
|
|
28
|
-
@
|
|
28
|
+
@diagnostics = DiagnosticReporter.new(
|
|
29
29
|
level: options[:log_level] || "info",
|
|
30
|
-
handler: method(:
|
|
30
|
+
handler: method(:handle_evaluation_diagnostic)
|
|
31
31
|
)
|
|
32
32
|
@on_diagnostic = options[:on_diagnostic] || options[:onDiagnostic]
|
|
33
33
|
@emitter = Featurevisor::Emitter.new
|
|
@@ -36,9 +36,9 @@ module Featurevisor
|
|
|
36
36
|
@module_diagnostic_subscriptions = []
|
|
37
37
|
|
|
38
38
|
# datafile
|
|
39
|
-
@
|
|
39
|
+
@datafile = InstanceEvaluationDataProvider.new(
|
|
40
40
|
datafile: EMPTY_DATAFILE,
|
|
41
|
-
|
|
41
|
+
diagnostics: @diagnostics
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
@modules_manager = Featurevisor::Modules::ModulesManager.new(
|
|
@@ -62,21 +62,35 @@ module Featurevisor
|
|
|
62
62
|
# Set the log level
|
|
63
63
|
# @param level [String] Log level
|
|
64
64
|
def set_log_level(level)
|
|
65
|
-
@
|
|
65
|
+
@diagnostics.set_level(level)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
def
|
|
68
|
+
def handle_evaluation_diagnostic(level, message, details = nil)
|
|
69
69
|
details = (details || {}).dup
|
|
70
|
-
code = details[:reason] || details["reason"] || message
|
|
70
|
+
code = details.delete(:code) || details.delete("code") || details[:reason] || details["reason"] || message
|
|
71
71
|
code = "deprecated_feature" if message == "feature is deprecated"
|
|
72
72
|
code = "deprecated_variable" if message == "variable is deprecated"
|
|
73
73
|
code = "feature_not_found" if message == "feature not found"
|
|
74
74
|
code = "variable_not_found" if message == "variable schema not found"
|
|
75
75
|
code = "no_variations" if message == "no variations"
|
|
76
76
|
code = "invalid_bucket_by" if message == "invalid bucketBy"
|
|
77
|
-
|
|
77
|
+
code = "evaluation_error" if message == "Error during evaluation"
|
|
78
|
+
code = "conditions_parse_error" if message == "Error parsing conditions"
|
|
79
|
+
original_error = details.delete(:error) || details.delete("error")
|
|
80
|
+
nested_details = details.delete(:details) || details.delete("details")
|
|
81
|
+
details.merge!(nested_details) if nested_details.is_a?(Hash)
|
|
82
|
+
if details.key?(:feature_key) && details.key?(:reason)
|
|
83
|
+
evaluation = details.dup
|
|
84
|
+
details = {
|
|
85
|
+
featureKey: evaluation[:feature_key],
|
|
86
|
+
variableKey: evaluation[:variable_key],
|
|
87
|
+
reason: evaluation[:reason],
|
|
88
|
+
evaluation: camelize_diagnostic_value(evaluation)
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
report_diagnostic(level: level, code: code.to_s, message: message, details: details, originalError: original_error)
|
|
78
92
|
end
|
|
79
|
-
private :
|
|
93
|
+
private :handle_evaluation_diagnostic
|
|
80
94
|
|
|
81
95
|
# Set the datafile
|
|
82
96
|
# @param datafile [Hash, String] Datafile content or JSON string
|
|
@@ -97,14 +111,14 @@ module Featurevisor
|
|
|
97
111
|
parsed_datafile[:features].is_a?(Hash)
|
|
98
112
|
raise ArgumentError, "Invalid datafile"
|
|
99
113
|
end
|
|
100
|
-
next_datafile = replace ? parsed_datafile : merge_datafiles(@
|
|
101
|
-
|
|
114
|
+
next_datafile = replace ? parsed_datafile : merge_datafiles(@datafile.get_datafile, parsed_datafile)
|
|
115
|
+
new_datafile = InstanceEvaluationDataProvider.new(
|
|
102
116
|
datafile: next_datafile,
|
|
103
|
-
|
|
117
|
+
diagnostics: @diagnostics
|
|
104
118
|
)
|
|
105
119
|
|
|
106
|
-
details = Featurevisor::Events.get_params_for_datafile_set_event(@
|
|
107
|
-
@
|
|
120
|
+
details = Featurevisor::Events.get_params_for_datafile_set_event(@datafile, new_datafile, replace)
|
|
121
|
+
@datafile = new_datafile
|
|
108
122
|
|
|
109
123
|
report_diagnostic(
|
|
110
124
|
level: "info",
|
|
@@ -152,34 +166,34 @@ module Featurevisor
|
|
|
152
166
|
# Get the revision
|
|
153
167
|
# @return [String] Revision string
|
|
154
168
|
def get_revision
|
|
155
|
-
@
|
|
169
|
+
@datafile.get_revision
|
|
156
170
|
end
|
|
157
171
|
|
|
158
172
|
def get_schema_version
|
|
159
|
-
@
|
|
173
|
+
@datafile.get_schema_version
|
|
160
174
|
end
|
|
161
175
|
|
|
162
176
|
def get_segment(segment_key)
|
|
163
|
-
@
|
|
177
|
+
@datafile.get_segment(segment_key)
|
|
164
178
|
end
|
|
165
179
|
|
|
166
180
|
def get_feature_keys
|
|
167
|
-
@
|
|
181
|
+
@datafile.get_feature_keys
|
|
168
182
|
end
|
|
169
183
|
|
|
170
184
|
def get_variable_keys(feature_key)
|
|
171
|
-
@
|
|
185
|
+
@datafile.get_variable_keys(feature_key)
|
|
172
186
|
end
|
|
173
187
|
|
|
174
188
|
def has_variations?(feature_key)
|
|
175
|
-
@
|
|
189
|
+
@datafile.has_variations?(feature_key)
|
|
176
190
|
end
|
|
177
191
|
|
|
178
192
|
# Get a feature by key
|
|
179
193
|
# @param feature_key [String] Feature key
|
|
180
194
|
# @return [Hash, nil] Feature data or nil if not found
|
|
181
195
|
def get_feature(feature_key)
|
|
182
|
-
@
|
|
196
|
+
@datafile.get_feature(feature_key)
|
|
183
197
|
end
|
|
184
198
|
|
|
185
199
|
# Add a module
|
|
@@ -189,8 +203,8 @@ module Featurevisor
|
|
|
189
203
|
@modules_manager.add(mod)
|
|
190
204
|
end
|
|
191
205
|
|
|
192
|
-
def remove_module(
|
|
193
|
-
@modules_manager.remove(
|
|
206
|
+
def remove_module(name)
|
|
207
|
+
@modules_manager.remove(name)
|
|
194
208
|
end
|
|
195
209
|
|
|
196
210
|
# Subscribe to an event
|
|
@@ -198,11 +212,15 @@ module Featurevisor
|
|
|
198
212
|
# @param callback [Proc] Callback function
|
|
199
213
|
# @return [Proc] Unsubscribe function
|
|
200
214
|
def on(event_name, callback)
|
|
215
|
+
return -> {} if @closed
|
|
216
|
+
|
|
201
217
|
@emitter.on(event_name, callback)
|
|
202
218
|
end
|
|
203
219
|
|
|
204
220
|
# Close the instance
|
|
205
221
|
def close
|
|
222
|
+
return if @closed
|
|
223
|
+
|
|
206
224
|
@closed = true
|
|
207
225
|
@modules_manager.close_all
|
|
208
226
|
@module_diagnostic_subscriptions = []
|
|
@@ -285,7 +303,7 @@ module Featurevisor
|
|
|
285
303
|
evaluation = evaluate_flag(feature_key, context, options)
|
|
286
304
|
evaluation[:enabled] == true
|
|
287
305
|
rescue => e
|
|
288
|
-
|
|
306
|
+
report_diagnostic(level: "error", code: "evaluation_error", message: "isEnabled failed", originalError: e, details: { featureKey: feature_key })
|
|
289
307
|
false
|
|
290
308
|
end
|
|
291
309
|
end
|
|
@@ -313,7 +331,7 @@ module Featurevisor
|
|
|
313
331
|
begin
|
|
314
332
|
evaluation = evaluate_variation(feature_key, context, options)
|
|
315
333
|
|
|
316
|
-
if evaluation
|
|
334
|
+
if evaluation.key?(:variation_value)
|
|
317
335
|
evaluation[:variation_value]
|
|
318
336
|
elsif evaluation[:variation]
|
|
319
337
|
evaluation[:variation][:value]
|
|
@@ -321,7 +339,7 @@ module Featurevisor
|
|
|
321
339
|
nil
|
|
322
340
|
end
|
|
323
341
|
rescue => e
|
|
324
|
-
|
|
342
|
+
report_diagnostic(level: "error", code: "evaluation_error", message: "getVariation failed", originalError: e, details: { featureKey: feature_key })
|
|
325
343
|
nil
|
|
326
344
|
end
|
|
327
345
|
end
|
|
@@ -352,7 +370,7 @@ module Featurevisor
|
|
|
352
370
|
begin
|
|
353
371
|
evaluation = evaluate_variable(feature_key, variable_key, context, options)
|
|
354
372
|
|
|
355
|
-
if
|
|
373
|
+
if evaluation.key?(:variable_value)
|
|
356
374
|
if evaluation[:variable_schema] &&
|
|
357
375
|
evaluation[:variable_schema][:type] == "json" &&
|
|
358
376
|
evaluation[:variable_value].is_a?(String)
|
|
@@ -364,7 +382,7 @@ module Featurevisor
|
|
|
364
382
|
nil
|
|
365
383
|
end
|
|
366
384
|
rescue => e
|
|
367
|
-
|
|
385
|
+
report_diagnostic(level: "error", code: "evaluation_error", message: "getVariable failed", originalError: e, details: { featureKey: feature_key, variableKey: variable_key })
|
|
368
386
|
nil
|
|
369
387
|
end
|
|
370
388
|
end
|
|
@@ -454,7 +472,7 @@ module Featurevisor
|
|
|
454
472
|
def get_all_evaluations(context = {}, feature_keys = [], options = {})
|
|
455
473
|
result = {}
|
|
456
474
|
|
|
457
|
-
|
|
475
|
+
keys = feature_keys.size > 0 ? feature_keys : @datafile.get_feature_keys
|
|
458
476
|
|
|
459
477
|
keys.each do |feature_key|
|
|
460
478
|
# Convert symbol keys to strings for evaluation functions
|
|
@@ -466,13 +484,13 @@ module Featurevisor
|
|
|
466
484
|
}
|
|
467
485
|
|
|
468
486
|
# variation
|
|
469
|
-
if @
|
|
487
|
+
if @datafile.has_variations?(feature_key_str)
|
|
470
488
|
variation = get_variation(feature_key_str, context, options)
|
|
471
|
-
evaluated_feature[:variation] = variation
|
|
489
|
+
evaluated_feature[:variation] = variation unless variation.nil?
|
|
472
490
|
end
|
|
473
491
|
|
|
474
492
|
# variables
|
|
475
|
-
variable_keys = @
|
|
493
|
+
variable_keys = @datafile.get_variable_keys(feature_key_str)
|
|
476
494
|
if variable_keys.size > 0
|
|
477
495
|
evaluated_feature[:variables] = {}
|
|
478
496
|
|
|
@@ -501,13 +519,14 @@ module Featurevisor
|
|
|
501
519
|
def get_evaluation_dependencies(context, options = {})
|
|
502
520
|
{
|
|
503
521
|
context: get_context(context),
|
|
504
|
-
|
|
522
|
+
diagnostics: @diagnostics,
|
|
505
523
|
modules_manager: @modules_manager,
|
|
506
|
-
|
|
524
|
+
datafile: @datafile,
|
|
507
525
|
sticky: options[:__featurevisor_child_sticky] || @sticky,
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
526
|
+
}.tap do |dependencies|
|
|
527
|
+
dependencies[:default_variation_value] = options[:default_variation_value] if options.key?(:default_variation_value)
|
|
528
|
+
dependencies[:default_variable_value] = options[:default_variable_value] if options.key?(:default_variable_value)
|
|
529
|
+
end
|
|
511
530
|
end
|
|
512
531
|
|
|
513
532
|
# Get value by type
|
|
@@ -584,7 +603,7 @@ module Featurevisor
|
|
|
584
603
|
diagnostic = (diagnostic || {}).dup
|
|
585
604
|
diagnostic[:level] ||= "info"
|
|
586
605
|
diagnostic[:module] = source_module.name if source_module && source_module.name
|
|
587
|
-
details = (diagnostic[:details] || {}).dup
|
|
606
|
+
details = camelize_diagnostic_value((diagnostic[:details] || {}).dup)
|
|
588
607
|
legacy_module_name = diagnostic.delete(:module_name)
|
|
589
608
|
legacy_original_error = diagnostic.delete(:original_error)
|
|
590
609
|
diagnostic[:moduleName] = legacy_module_name if !diagnostic.key?(:moduleName) && !legacy_module_name.nil?
|
|
@@ -609,7 +628,7 @@ module Featurevisor
|
|
|
609
628
|
end
|
|
610
629
|
|
|
611
630
|
if @on_diagnostic
|
|
612
|
-
if should_report_diagnostic?(diagnostic[:level], @
|
|
631
|
+
if should_report_diagnostic?(diagnostic[:level], @diagnostics.level)
|
|
613
632
|
begin
|
|
614
633
|
@on_diagnostic.call(diagnostic)
|
|
615
634
|
rescue => e
|
|
@@ -617,7 +636,7 @@ module Featurevisor
|
|
|
617
636
|
end
|
|
618
637
|
end
|
|
619
638
|
else
|
|
620
|
-
|
|
639
|
+
DiagnosticReporter.new(level: @diagnostics.level).log(
|
|
621
640
|
diagnostic[:level],
|
|
622
641
|
diagnostic[:message],
|
|
623
642
|
diagnostic
|
|
@@ -638,6 +657,21 @@ module Featurevisor
|
|
|
638
657
|
|
|
639
658
|
subscriber_index >= diagnostic_index
|
|
640
659
|
end
|
|
660
|
+
|
|
661
|
+
def camelize_diagnostic_value(value)
|
|
662
|
+
case value
|
|
663
|
+
when Hash
|
|
664
|
+
value.each_with_object({}) do |(key, item), result|
|
|
665
|
+
normalized_key = key.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }.to_sym
|
|
666
|
+
result[normalized_key] = camelize_diagnostic_value(item)
|
|
667
|
+
end
|
|
668
|
+
when Array
|
|
669
|
+
value.map { |item| camelize_diagnostic_value(item) }
|
|
670
|
+
else
|
|
671
|
+
value
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
private :camelize_diagnostic_value
|
|
641
675
|
end
|
|
642
676
|
|
|
643
677
|
# Create a new Featurevisor instance
|
data/lib/featurevisor/version.rb
CHANGED
data/lib/featurevisor.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
require_relative "featurevisor/version"
|
|
2
2
|
require_relative "featurevisor/murmurhash"
|
|
3
3
|
require_relative "featurevisor/compare_versions"
|
|
4
|
-
require_relative "featurevisor/
|
|
4
|
+
require_relative "featurevisor/diagnostics"
|
|
5
5
|
require_relative "featurevisor/emitter"
|
|
6
6
|
require_relative "featurevisor/conditions"
|
|
7
|
-
require_relative "featurevisor/
|
|
7
|
+
require_relative "featurevisor/evaluation_data_provider"
|
|
8
8
|
require_relative "featurevisor/bucketer"
|
|
9
9
|
require_relative "featurevisor/modules"
|
|
10
10
|
require_relative "featurevisor/evaluate"
|
|
@@ -15,6 +15,6 @@ require_relative "featurevisor/events"
|
|
|
15
15
|
module Featurevisor
|
|
16
16
|
class Error < StandardError; end
|
|
17
17
|
|
|
18
|
-
private_constant :
|
|
19
|
-
private_constant :
|
|
18
|
+
private_constant :InstanceEvaluationDataProvider
|
|
19
|
+
private_constant :DiagnosticReporter
|
|
20
20
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: featurevisor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Fahad Heylaal
|
|
@@ -76,12 +76,12 @@ files:
|
|
|
76
76
|
- lib/featurevisor/child_instance.rb
|
|
77
77
|
- lib/featurevisor/compare_versions.rb
|
|
78
78
|
- lib/featurevisor/conditions.rb
|
|
79
|
-
- lib/featurevisor/
|
|
79
|
+
- lib/featurevisor/diagnostics.rb
|
|
80
80
|
- lib/featurevisor/emitter.rb
|
|
81
81
|
- lib/featurevisor/evaluate.rb
|
|
82
|
+
- lib/featurevisor/evaluation_data_provider.rb
|
|
82
83
|
- lib/featurevisor/events.rb
|
|
83
84
|
- lib/featurevisor/instance.rb
|
|
84
|
-
- lib/featurevisor/logger.rb
|
|
85
85
|
- lib/featurevisor/modules.rb
|
|
86
86
|
- lib/featurevisor/murmurhash.rb
|
|
87
87
|
- lib/featurevisor/version.rb
|