featurevisor 3.0.0 → 4.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.
data/bin/commands.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require_relative "commands/node_cli"
1
2
  require_relative "commands/test"
2
3
  require_relative "commands/benchmark"
3
4
  require_relative "commands/assess_distribution"
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FeaturevisorCLI
4
+ module SegmentTests
5
+ module_function
6
+
7
+ def prepare(datafile, segments)
8
+ return datafile if segments.empty?
9
+
10
+ definitions = segments.transform_keys(&:to_sym).merge((datafile[:segments] || {}).transform_keys(&:to_sym))
11
+ features = (datafile[:features] || {}).dup
12
+ keys = {}
13
+ segments.each_key do |key|
14
+ helper = "__featurevisor_segment_test__#{key}"
15
+ helper += "_" while features.key?(helper.to_sym) || features.key?(helper)
16
+ keys[key.to_s] = helper
17
+ features[helper.to_sym] = { bucketBy: "__segment_test__", conditions: key.to_s,
18
+ traffic: [{ key: "segment", conditions: "*", percentage: 100_000 }] }
19
+ end
20
+ datafile.merge(features: features, segments: definitions, _segment_test_keys: keys)
21
+ end
22
+
23
+ def references!(key, datafile, segments)
24
+ pending = [[:segment, key, nil]]
25
+ seen = {}
26
+ expression = nil
27
+ expression = lambda do |value|
28
+ case value
29
+ when String
30
+ next if value == "*"
31
+ if value.lstrip.match?(/\A[\{\[\"]/)
32
+ begin
33
+ expression.call(JSON.parse(value, symbolize_names: true))
34
+ rescue JSON::ParserError
35
+ nil
36
+ end
37
+ else
38
+ pending << [:segment, value, nil]
39
+ end
40
+ when Array
41
+ value.each { |child| expression.call(child) }
42
+ when Hash
43
+ if value.key?(:variable)
44
+ pending << [value[:feature] ? :feature : :variable, value[:feature] || value[:variable], value[:feature] && value[:variable]]
45
+ elsif value.key?(:feature)
46
+ pending << [:feature, value[:feature], nil]
47
+ else
48
+ %i[and or not].each { |group| expression.call(value[group]) if value.key?(group) }
49
+ end
50
+ end
51
+ end
52
+ cursor = 0
53
+ while cursor < pending.length
54
+ kind, name, variable = pending[cursor]
55
+ cursor += 1
56
+ identity = [kind, name.to_s, variable]
57
+ next if seen[identity]
58
+ seen[identity] = true
59
+ definitions = datafile[{ feature: :features, segment: :segments, variable: :variables }.fetch(kind)] || {}
60
+ definition = kind == :segment ? TestChecks.keyed(segments, name) : nil
61
+ definition ||= TestChecks.keyed(definitions, name)
62
+ unless definition.is_a?(Hash)
63
+ raise TestChecks::CheckError, "#{kind} #{name.inspect} is absent from the selected datafile and test definitions"
64
+ end
65
+ if variable && !TestChecks.keyed(definition[:variablesSchema], variable)
66
+ raise TestChecks::CheckError, "variable #{variable.inspect} is absent from feature #{name.inspect} in the selected datafile"
67
+ end
68
+ normalized, = Featurevisor::Normalize.entity(kind, name, definition)
69
+ expression.call(normalized[:conditions])
70
+ if kind == :feature
71
+ (Array(normalized[:force]) + Array(normalized[:traffic])).each { |entry| expression.call(entry[:conditions]) }
72
+ if variable
73
+ (Array(normalized[:traffic]) + Array(normalized[:variations])).each do |entry|
74
+ Array(TestChecks.keyed(entry[:variableOverrides], variable)).each { |override| expression.call(override[:conditions]) }
75
+ end
76
+ end
77
+ elsif kind == :variable
78
+ (Array(normalized[:overrides]) + Array(normalized[:overrideProgram])).each { |entry| expression.call(entry[:conditions]) }
79
+ compose = normalized[:compose]
80
+ if compose.is_a?(String)
81
+ pending << [:variable, compose, nil]
82
+ elsif compose.is_a?(Array)
83
+ compose.each { |reference| expression.call(reference) }
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end