Skip to content

Commit d86ba90

Browse files
committed
feat: split-functions-and-module-for-linting
1 parent 0386836 commit d86ba90

4 files changed

Lines changed: 251 additions & 170 deletions

File tree

lib/flagsmith/engine/evaluation/mappers.rb

Lines changed: 15 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
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
@@ -13,12 +17,8 @@ module Mappers
1317
# @param override_traits [Array<Flagsmith::Engine::Identities::Trait>, nil] Optional override traits
1418
# @return [Hash] Evaluation context with environment, features, segments, and optionally identity
1519
def self.get_evaluation_context(environment, identity = nil, override_traits = nil)
16-
environment_context = map_environment_model_to_evaluation_context(environment)
17-
identity_context = identity ? map_identity_model_to_identity_context(identity, override_traits) : nil
18-
19-
context = environment_context.dup
20-
context[:identity] = identity_context if identity_context
21-
20+
context = map_environment_model_to_evaluation_context(environment)
21+
context[:identity] = map_identity_model_to_identity_context(identity, override_traits) if identity
2222
context
2323
end
2424

@@ -27,85 +27,15 @@ def self.get_evaluation_context(environment, identity = nil, override_traits = n
2727
# @param environment [Flagsmith::Engine::Environment] The environment model
2828
# @return [Hash] Context with :environment, :features, and :segments keys
2929
def self.map_environment_model_to_evaluation_context(environment)
30-
environment_context = {
31-
key: environment.api_key,
32-
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)
3334
}
3435

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

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

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

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

15658
# Maps identity overrides to segments
15759
#
15860
# @param identity_overrides [Array<Flagsmith::Engine::Identity>] Array of identity override models
15961
# @return [Hash] Segments hash for identity overrides
16062
def self.map_identity_overrides_to_segments(identity_overrides)
161-
segments = {}
162-
features_to_identifiers = {}
163-
164-
identity_overrides.each do |identity|
165-
next if identity.identity_features.nil? || identity.identity_features.none?
166-
167-
# Sort features by name for consistent hashing
168-
sorted_features = identity.identity_features.to_a.sort_by { |fs| fs.feature.name }
169-
170-
# Create override keys for hashing
171-
overrides_key = sorted_features.map do |fs|
172-
{
173-
feature_key: fs.feature.id.to_s,
174-
name: fs.feature.name,
175-
enabled: fs.enabled,
176-
value: fs.get_value,
177-
priority: STRONGEST_PRIORITY,
178-
metadata: {
179-
flagsmith_id: fs.feature.id
180-
}
181-
}
182-
end
183-
184-
# Create hash of the overrides to group identities with same overrides
185-
overrides_hash = overrides_key.hash
186-
187-
features_to_identifiers[overrides_hash] ||= { identifiers: [], overrides: overrides_key }
188-
features_to_identifiers[overrides_hash][:identifiers] << identity.identifier
189-
end
190-
191-
# Create segments for each unique set of overrides
192-
features_to_identifiers.each do |overrides_hash, data|
193-
segment_key = "identity_override_#{overrides_hash}"
194-
195-
segments[segment_key] = {
196-
key: segment_key,
197-
name: 'identity_override',
198-
rules: [
199-
{
200-
type: 'ALL',
201-
conditions: [
202-
{
203-
property: '$.identity.identifier',
204-
operator: 'IN',
205-
value: data[:identifiers]
206-
}
207-
],
208-
rules: []
209-
}
210-
],
211-
metadata: {
212-
source: 'identity_override'
213-
},
214-
overrides: data[:overrides]
215-
}
216-
end
217-
218-
segments
63+
Identity.map_overrides_to_segments(identity_overrides)
21964
end
22065
end
22166
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
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# frozen_string_literal: true
2+
3+
module Flagsmith
4+
module Engine
5+
module Evaluation
6+
module Mappers
7+
# Handles identity and override mapping
8+
module Identity
9+
def self.build_environment_context(identity, override_traits = nil)
10+
traits = override_traits || identity.identity_traits
11+
12+
{
13+
identifier: identity.identifier,
14+
key: identity.django_id&.to_s || identity.composite_key,
15+
traits: build_traits_hash(traits)
16+
}
17+
end
18+
19+
def self.build_traits_hash(traits)
20+
traits_hash = {}
21+
traits.each do |trait|
22+
traits_hash[trait.trait_key] = trait.trait_value
23+
end
24+
traits_hash
25+
end
26+
27+
def self.map_overrides_to_segments(identity_overrides)
28+
features_to_identifiers = group_by_overrides(identity_overrides)
29+
build_segments(features_to_identifiers)
30+
end
31+
32+
def self.group_by_overrides(identity_overrides)
33+
features_to_identifiers = {}
34+
35+
identity_overrides.each do |identity|
36+
next if identity.identity_features.nil? || identity.identity_features.none?
37+
38+
overrides_key = build_overrides_key(identity.identity_features)
39+
overrides_hash = overrides_key.hash
40+
41+
features_to_identifiers[overrides_hash] ||= { identifiers: [], overrides: overrides_key }
42+
features_to_identifiers[overrides_hash][:identifiers] << identity.identifier
43+
end
44+
45+
features_to_identifiers
46+
end
47+
48+
def self.build_overrides_key(identity_features) # rubocop:disable Metrics/MethodLength
49+
sorted_features = identity_features.to_a.sort_by { |fs| fs.feature.name }
50+
sorted_features.map do |feature_state|
51+
{
52+
feature_key: feature_state.feature.id.to_s,
53+
name: feature_state.feature.name,
54+
enabled: feature_state.enabled,
55+
value: feature_state.get_value,
56+
priority: Mappers::STRONGEST_PRIORITY,
57+
metadata: { flagsmith_id: feature_state.feature.id }
58+
}
59+
end
60+
end
61+
62+
def self.build_segments(features_to_identifiers)
63+
segments = {}
64+
65+
features_to_identifiers.each do |overrides_hash, data|
66+
segment_key = "identity_override_#{overrides_hash}"
67+
segments[segment_key] = build_segment(segment_key, data)
68+
end
69+
70+
segments
71+
end
72+
73+
def self.build_segment(segment_key, data)
74+
{
75+
key: segment_key,
76+
name: 'identity_override',
77+
rules: [build_rule(data[:identifiers])],
78+
metadata: { source: 'identity_override' },
79+
overrides: data[:overrides]
80+
}
81+
end
82+
83+
def self.build_rule(identifiers) # rubocop:disable Metrics/MethodLength
84+
{
85+
type: 'ALL',
86+
conditions: [
87+
{
88+
property: '$.identity.identifier',
89+
operator: 'IN',
90+
value: identifiers
91+
}
92+
],
93+
rules: []
94+
}
95+
end
96+
end
97+
end
98+
end
99+
end
100+
end

0 commit comments

Comments
 (0)