Skip to content

Commit 561cd71

Browse files
committed
feat: rebased
2 parents 771a1a1 + 7de46c2 commit 561cd71

File tree

5 files changed

+247
-171
lines changed

5 files changed

+247
-171
lines changed

lib/flagsmith/engine/evaluation/core.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
module Flagsmith
88
module Engine
99
module Evaluation
10-
# Core evaluation logic module
10+
# Core evaluation logic for feature flags
1111
module Core
1212
extend self
1313
include Flagsmith::Engine::Utils::HashFunc

lib/flagsmith/engine/evaluation/mappers.rb

Lines changed: 16 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
# frozen_string_literal: true
22

3+
require_relative 'mappers/environment'
4+
require_relative 'mappers/identity'
5+
require_relative 'mappers/segments'
6+
37
module Flagsmith
48
module Engine
59
module Evaluation
10+
# Mappers for converting between models and evaluation contexts
611
module Mappers
712
STRONGEST_PRIORITY = Float::INFINITY
813
WEAKEST_PRIORITY = -Float::INFINITY
@@ -12,12 +17,8 @@ module Mappers
1217
# @param override_traits [Array<Flagsmith::Engine::Identities::Trait>, nil] Optional override traits
1318
# @return [Hash] Evaluation context with environment, features, segments, and optionally identity
1419
def self.get_evaluation_context(environment, identity = nil, override_traits = nil)
15-
environment_context = map_environment_model_to_evaluation_context(environment)
16-
identity_context = identity ? map_identity_model_to_identity_context(identity, override_traits) : nil
17-
18-
context = environment_context.dup
19-
context[:identity] = identity_context if identity_context
20-
20+
context = map_environment_model_to_evaluation_context(environment)
21+
context[:identity] = map_identity_model_to_identity_context(identity, override_traits) if identity
2122
context
2223
end
2324

@@ -26,85 +27,15 @@ def self.get_evaluation_context(environment, identity = nil, override_traits = n
2627
# @param environment [Flagsmith::Engine::Environment] The environment model
2728
# @return [Hash] Context with :environment, :features, and :segments keys
2829
def self.map_environment_model_to_evaluation_context(environment)
29-
environment_context = {
30-
key: environment.api_key,
31-
name: environment.project.name
30+
context = {
31+
environment: Environment.build_environment_context(environment),
32+
features: Environment.build_features_context(environment.feature_states),
33+
segments: Segments.build_segments_context(environment.project.segments)
3234
}
3335

34-
# Map feature states to features hash
35-
features = {}
36-
environment.feature_states.each do |fs|
37-
# Map multivariate values if present
38-
variants = nil
39-
if fs.multivariate_feature_state_values&.any?
40-
variants = fs.multivariate_feature_state_values.map do |mv|
41-
{
42-
value: mv.multivariate_feature_option.value,
43-
weight: mv.percentage_allocation,
44-
priority: mv.id || uuid_to_big_int(mv.mv_fs_value_uuid)
45-
}
46-
end
47-
end
48-
49-
feature_hash = {
50-
key: fs.django_id&.to_s || fs.uuid,
51-
feature_key: fs.feature.id.to_s,
52-
name: fs.feature.name,
53-
enabled: fs.enabled,
54-
value: fs.get_value
55-
}
56-
57-
feature_hash[:variants] = variants if variants
58-
priority = fs.feature_segment&.priority
59-
feature_hash[:priority] = priority unless priority.nil?
60-
feature_hash[:metadata] = { flagsmith_id: fs.feature.id }
61-
62-
features[fs.feature.name] = feature_hash
63-
end
64-
65-
# Map segments from project
66-
segments = {}
67-
environment.project.segments.each do |segment|
68-
overrides = segment.feature_states.map do |fs|
69-
override_hash = {
70-
key: fs.django_id&.to_s || fs.uuid,
71-
feature_key: fs.feature.id.to_s,
72-
name: fs.feature.name,
73-
enabled: fs.enabled,
74-
value: fs.get_value
75-
}
76-
override_hash[:priority] = fs.feature_segment.priority if fs.feature_segment&.priority
77-
override_hash[:metadata] = { flagsmith_id: fs.feature.id }
78-
override_hash
79-
end
36+
context[:segments].merge!(Identity.map_overrides_to_segments(environment.identity_overrides)) if environment.identity_overrides&.any?
8037

81-
segments[segment.id.to_s] = {
82-
key: segment.id.to_s,
83-
name: segment.name,
84-
rules: segment.rules.map { |rule| map_segment_rule_model_to_rule(rule) },
85-
overrides: overrides,
86-
metadata: {
87-
source: 'API',
88-
flagsmith_id: segment.id
89-
}
90-
}
91-
end
92-
93-
# Map identity overrides to segments
94-
if environment.identity_overrides&.any?
95-
identity_override_segments = map_identity_overrides_to_segments(environment.identity_overrides)
96-
segments.merge!(identity_override_segments)
97-
end
98-
99-
{
100-
environment: environment_context,
101-
features: features,
102-
segments: segments
103-
}
104-
end
105-
106-
def self.uuid_to_big_int(uuid)
107-
uuid.gsub('-', '').to_i(16)
38+
context
10839
end
10940

11041
# Maps identity model to identity context
@@ -113,108 +44,23 @@ def self.uuid_to_big_int(uuid)
11344
# @param override_traits [Array<Flagsmith::Engine::Identities::Trait>, nil] Optional override traits
11445
# @return [Hash] Identity context with :identifier, :key, and :traits
11546
def self.map_identity_model_to_identity_context(identity, override_traits = nil)
116-
# Use override traits if provided, otherwise use identity's traits
117-
traits = override_traits || identity.identity_traits
118-
119-
# Map traits to a hash with trait key => trait value
120-
traits_hash = {}
121-
traits.each do |trait|
122-
traits_hash[trait.trait_key] = trait.trait_value
123-
end
124-
125-
{
126-
identifier: identity.identifier,
127-
key: identity.django_id&.to_s || identity.composite_key,
128-
traits: traits_hash
129-
}
47+
Identity.build_environment_context(identity, override_traits)
13048
end
13149

13250
# Maps segment rule model to rule hash
13351
#
13452
# @param rule [Flagsmith::Engine::Segments::Rule] The segment rule model
13553
# @return [Hash] Mapped rule with :type, :conditions, and :rules
13654
def self.map_segment_rule_model_to_rule(rule)
137-
result = {
138-
type: rule.type
139-
}
140-
141-
# Map conditions if present
142-
result[:conditions] = (rule.conditions || []).map do |condition|
143-
{ property: condition.property, operator: condition.operator, value: condition.value }
144-
end
145-
146-
result[:rules] = if rule.rules&.any?
147-
rule.rules.map { |nested_rule| map_segment_rule_model_to_rule(nested_rule) }
148-
else
149-
[]
150-
end
151-
152-
result
55+
Segments.map_rule(rule)
15356
end
15457

15558
# Maps identity overrides to segments
15659
#
15760
# @param identity_overrides [Array<Flagsmith::Engine::Identity>] Array of identity override models
15861
# @return [Hash] Segments hash for identity overrides
15962
def self.map_identity_overrides_to_segments(identity_overrides)
160-
segments = {}
161-
features_to_identifiers = {}
162-
163-
identity_overrides.each do |identity|
164-
next if identity.identity_features.nil? || identity.identity_features.none?
165-
166-
# Sort features by name for consistent hashing
167-
sorted_features = identity.identity_features.to_a.sort_by { |fs| fs.feature.name }
168-
169-
# Create override keys for hashing
170-
overrides_key = sorted_features.map do |fs|
171-
{
172-
feature_key: fs.feature.id.to_s,
173-
name: fs.feature.name,
174-
enabled: fs.enabled,
175-
value: fs.get_value,
176-
priority: STRONGEST_PRIORITY,
177-
metadata: {
178-
flagsmith_id: fs.feature.id
179-
}
180-
}
181-
end
182-
183-
# Create hash of the overrides to group identities with same overrides
184-
overrides_hash = overrides_key.hash
185-
186-
features_to_identifiers[overrides_hash] ||= { identifiers: [], overrides: overrides_key }
187-
features_to_identifiers[overrides_hash][:identifiers] << identity.identifier
188-
end
189-
190-
# Create segments for each unique set of overrides
191-
features_to_identifiers.each do |overrides_hash, data|
192-
segment_key = "identity_override_#{overrides_hash}"
193-
194-
segments[segment_key] = {
195-
key: segment_key,
196-
name: 'identity_override',
197-
rules: [
198-
{
199-
type: 'ALL',
200-
conditions: [
201-
{
202-
property: '$.identity.identifier',
203-
operator: 'IN',
204-
value: data[:identifiers]
205-
}
206-
],
207-
rules: []
208-
}
209-
],
210-
metadata: {
211-
source: 'identity_override'
212-
},
213-
overrides: data[:overrides]
214-
}
215-
end
216-
217-
segments
63+
Identity.map_overrides_to_segments(identity_overrides)
21864
end
21965
end
22066
end
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# frozen_string_literal: true
2+
3+
module Flagsmith
4+
module Engine
5+
module Evaluation
6+
module Mappers
7+
# Handles environment and feature mapping
8+
module Environment
9+
def self.build_environment_context(environment)
10+
{
11+
key: environment.api_key,
12+
name: environment.project.name
13+
}
14+
end
15+
16+
def self.build_features_context(feature_states)
17+
features = {}
18+
feature_states.each do |feature_state|
19+
features[feature_state.feature.name] = build_feature_hash(feature_state)
20+
end
21+
features
22+
end
23+
24+
def self.build_feature_hash(feature_state) # rubocop:disable Metrics/MethodLength
25+
feature_hash = {
26+
key: feature_state.django_id&.to_s || feature_state.uuid,
27+
feature_key: feature_state.feature.id.to_s,
28+
name: feature_state.feature.name,
29+
enabled: feature_state.enabled,
30+
value: feature_state.get_value,
31+
metadata: { flagsmith_id: feature_state.feature.id }
32+
}
33+
add_variants_to_feature(feature_hash, feature_state)
34+
add_priority_to_feature(feature_hash, feature_state)
35+
feature_hash
36+
end
37+
38+
def self.add_variants_to_feature(feature_hash, feature_state)
39+
return unless feature_state.multivariate_feature_state_values&.any?
40+
41+
feature_hash[:variants] = feature_state.multivariate_feature_state_values.map do |mv|
42+
{
43+
value: mv.multivariate_feature_option.value,
44+
weight: mv.percentage_allocation,
45+
priority: mv.id || uuid_to_big_int(mv.mv_fs_value_uuid)
46+
}
47+
end
48+
end
49+
50+
def self.uuid_to_big_int(uuid)
51+
uuid.gsub('-', '').to_i(16)
52+
end
53+
54+
def self.add_priority_to_feature(feature_hash, feature_state)
55+
priority = feature_state.feature_segment&.priority
56+
feature_hash[:priority] = priority unless priority.nil?
57+
end
58+
end
59+
end
60+
end
61+
end
62+
end

0 commit comments

Comments
 (0)