vwo-fme-ruby-sdk 1.0.0 → 1.1.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/vwo/api/get_flag.rb +236 -0
  3. data/lib/vwo/api/set_attribute.rb +57 -0
  4. data/lib/vwo/api/track_event.rb +77 -0
  5. data/lib/vwo/constants/constants.rb +54 -0
  6. data/lib/vwo/decorators/storage_decorator.rb +86 -0
  7. data/lib/vwo/{utils/logger_helper.rb → enums/api_enum.rb} +5 -10
  8. data/lib/vwo/enums/campaign_type_enum.rb +19 -0
  9. data/lib/vwo/enums/decision_types_enum.rb +18 -0
  10. data/lib/vwo/enums/event_enum.rb +19 -0
  11. data/lib/vwo/enums/headers_enum.rb +20 -0
  12. data/lib/vwo/enums/hooks_enum.rb +17 -0
  13. data/lib/vwo/enums/http_method_enum.rb +18 -0
  14. data/lib/vwo/enums/log_level_enum.rb +21 -0
  15. data/lib/vwo/enums/status_enum.rb +19 -0
  16. data/lib/vwo/enums/storage_enum.rb +22 -0
  17. data/lib/vwo/enums/url_enum.rb +21 -0
  18. data/lib/vwo/models/campaign/campaign_model.rb +192 -0
  19. data/lib/vwo/models/campaign/feature_model.rb +111 -0
  20. data/lib/vwo/models/campaign/impact_campaign_model.rb +38 -0
  21. data/lib/vwo/models/campaign/metric_model.rb +44 -0
  22. data/lib/vwo/models/campaign/rule_model.rb +56 -0
  23. data/lib/vwo/models/campaign/variable_model.rb +51 -0
  24. data/lib/vwo/models/campaign/variation_model.rb +137 -0
  25. data/lib/vwo/models/gateway_service_model.rb +39 -0
  26. data/lib/vwo/models/schemas/settings_schema_validation.rb +102 -0
  27. data/lib/vwo/models/settings/settings_model.rb +85 -0
  28. data/lib/vwo/models/storage/storage_data_model.rb +44 -0
  29. data/lib/vwo/models/user/context_model.rb +100 -0
  30. data/lib/vwo/models/user/context_vwo_model.rb +38 -0
  31. data/lib/vwo/{utils/feature_flag_response.rb → models/user/get_flag_response.rb} +14 -14
  32. data/lib/vwo/models/vwo_options_model.rb +107 -0
  33. data/lib/vwo/packages/decision_maker/decision_maker.rb +60 -0
  34. data/lib/vwo/packages/logger/core/log_manager.rb +90 -0
  35. data/lib/vwo/packages/logger/core/transport_manager.rb +87 -0
  36. data/lib/vwo/packages/logger/log_message_builder.rb +70 -0
  37. data/lib/vwo/packages/logger/logger.rb +38 -0
  38. data/lib/vwo/packages/logger/transports/console_transport.rb +49 -0
  39. data/lib/vwo/packages/network_layer/client/network_client.rb +107 -0
  40. data/lib/vwo/packages/network_layer/handlers/request_handler.rb +37 -0
  41. data/lib/vwo/packages/network_layer/manager/network_manager.rb +78 -0
  42. data/lib/vwo/packages/network_layer/models/global_request_model.rb +105 -0
  43. data/lib/vwo/packages/network_layer/models/request_model.rb +145 -0
  44. data/lib/vwo/packages/network_layer/models/response_model.rb +45 -0
  45. data/lib/vwo/packages/segmentation_evaluator/core/segmentation_manager.rb +76 -0
  46. data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +29 -0
  47. data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_value_enum.rb +26 -0
  48. data/lib/vwo/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +30 -0
  49. data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +210 -0
  50. data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +198 -0
  51. data/lib/vwo/packages/segmentation_evaluator/utils/segment_util.rb +44 -0
  52. data/lib/vwo/{constants.rb → packages/storage/connector.rb} +12 -10
  53. data/lib/vwo/packages/storage/storage.rb +45 -0
  54. data/lib/vwo/services/campaign_decision_service.rb +153 -0
  55. data/lib/vwo/services/hooks_service.rb +51 -0
  56. data/lib/vwo/services/logger_service.rb +83 -0
  57. data/lib/vwo/services/settings_service.rb +120 -0
  58. data/lib/vwo/services/storage_service.rb +65 -0
  59. data/lib/vwo/utils/campaign_util.rb +249 -0
  60. data/lib/vwo/utils/data_type_util.rb +105 -0
  61. data/lib/vwo/utils/decision_util.rb +253 -0
  62. data/lib/vwo/utils/function_util.rb +123 -0
  63. data/lib/vwo/utils/gateway_service_util.rb +101 -0
  64. data/lib/vwo/utils/impression_util.rb +49 -0
  65. data/lib/vwo/utils/log_message_util.rb +42 -0
  66. data/lib/vwo/utils/meg_util.rb +350 -0
  67. data/lib/vwo/utils/network_util.rb +235 -0
  68. data/lib/vwo/utils/rule_evaluation_util.rb +57 -0
  69. data/lib/vwo/utils/settings_util.rb +38 -0
  70. data/lib/vwo/utils/url_util.rb +46 -0
  71. data/lib/vwo/utils/uuid_util.rb +55 -0
  72. data/lib/vwo/vwo_builder.rb +156 -11
  73. data/lib/vwo/vwo_client.rb +163 -113
  74. data/lib/vwo.rb +49 -31
  75. metadata +187 -9
  76. data/lib/vwo/utils/request.rb +0 -89
@@ -0,0 +1,105 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ class GlobalRequestModel
16
+ attr_accessor :url, :timeout, :query, :body, :headers, :is_development_mode
17
+
18
+ # Constructs an instance of the GlobalRequestModel.
19
+ # @param url [String] The base URL of the HTTP request.
20
+ # @param query [Hash] Query parameters as a record of key-value pairs.
21
+ # @param body [Hash] Body of the request as a record of key-value pairs.
22
+ # @param headers [Hash] HTTP headers as a record of key-value pairs.
23
+ def initialize(url, query, body, headers)
24
+ @url = url
25
+ @query = query
26
+ @body = body
27
+ @headers = headers
28
+ @timeout = 3000 # Default timeout in milliseconds
29
+ @is_development_mode = nil # Flag to indicate if the request is in development mode
30
+ end
31
+
32
+ # Sets the query parameters for the HTTP request.
33
+ # @param query [Hash] A record of key-value pairs representing the query parameters.
34
+ def set_query(query)
35
+ @query = query
36
+ end
37
+
38
+ # Retrieves the query parameters of the HTTP request.
39
+ # @returns [Hash] A record of key-value pairs representing the query parameters.
40
+ def get_query
41
+ @query
42
+ end
43
+
44
+ # Sets the body of the HTTP request.
45
+ # @param body [Hash] A record of key-value pairs representing the body content.
46
+ def set_body(body)
47
+ @body = body
48
+ end
49
+
50
+ # Retrieves the body of the HTTP request.
51
+ # @returns [Hash] A record of key-value pairs representing the body content.
52
+ def get_body
53
+ @body
54
+ end
55
+
56
+ # Sets the base URL of the HTTP request.
57
+ # @param url [String] The base URL as a string.
58
+ def set_base_url(url)
59
+ @url = url
60
+ end
61
+
62
+ # Retrieves the base URL of the HTTP request.
63
+ # @returns [String] The base URL as a string.
64
+ def get_base_url
65
+ @url
66
+ end
67
+
68
+ # Sets the timeout duration for the HTTP request.
69
+ # @param timeout [Integer] Timeout in milliseconds.
70
+ def set_timeout(timeout)
71
+ @timeout = timeout
72
+ end
73
+
74
+ # Retrieves the timeout duration of the HTTP request.
75
+ # @returns [Integer] Timeout in milliseconds.
76
+ def get_timeout
77
+ @timeout
78
+ end
79
+
80
+ # Sets the HTTP headers for the request.
81
+ # @param headers [Hash] A record of key-value pairs representing the HTTP headers.
82
+ def set_headers(headers)
83
+ @headers = headers
84
+ end
85
+
86
+ # Retrieves the HTTP headers of the request.
87
+ # @returns [Hash] A record of key-value pairs representing the HTTP headers.
88
+ def get_headers
89
+ @headers
90
+ end
91
+
92
+ # Sets the development mode status for the request.
93
+ # @param is_development_mode [Boolean] Boolean flag indicating if the request is in development mode.
94
+ def set_development_mode(is_development_mode)
95
+ @is_development_mode = is_development_mode
96
+ end
97
+
98
+ # Retrieves the development mode status of the request.
99
+ # @returns [Boolean] Boolean indicating if the request is in development mode.
100
+ def get_development_mode
101
+ @is_development_mode
102
+ end
103
+ end
104
+
105
+
@@ -0,0 +1,145 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ class RequestModel
16
+ attr_accessor :url, :method, :path, :query, :timeout, :body, :headers, :scheme, :port
17
+
18
+ def initialize(url, method = 'GET', path = '', query = {}, body = {}, headers = {}, scheme = 'https', port = nil)
19
+ @url = scheme + '://' + url
20
+ @method = method
21
+ @path = path
22
+ @query = query
23
+ @timeout = 5000
24
+ @body = body
25
+ @headers = headers
26
+ @scheme = scheme
27
+ @port = port
28
+ if !@port.nil?
29
+ @url = @url + ':' + @port.to_s
30
+ end
31
+ parse_options
32
+ self
33
+ end
34
+
35
+ def parse_options
36
+ hostname, collection_prefix = @url.split('/')
37
+
38
+ # Process body if present
39
+ if !@body.nil?
40
+ @headers['Content-Type'] = 'application/json'
41
+ @headers['Content-Length'] = @body.to_json.bytesize.to_s
42
+ end
43
+
44
+ # Process path and query parameters
45
+ if !@path.nil?
46
+ query_string = @query.map { |k,v| "#{k}=#{v}" }.join('&')
47
+ @path = query_string.empty? ? @path : "#{@path}?#{query_string}"
48
+ end
49
+
50
+ # Add collection prefix if present
51
+ @path = "#{collection_prefix}#{@path}" if collection_prefix
52
+
53
+ # Add timeout
54
+ @timeout = @timeout if @timeout
55
+ end
56
+
57
+ def set_timeout(timeout)
58
+ @timeout = timeout
59
+ end
60
+
61
+ def get_timeout
62
+ @timeout
63
+ end
64
+
65
+ def set_body(body)
66
+ @body = body
67
+ end
68
+
69
+ def get_body
70
+ @body
71
+ end
72
+
73
+ def set_headers(headers)
74
+ @headers = headers
75
+ end
76
+
77
+ def get_headers
78
+ @headers
79
+ end
80
+
81
+ def set_scheme(scheme)
82
+ @scheme = scheme
83
+ end
84
+
85
+ def get_scheme
86
+ @scheme
87
+ end
88
+
89
+ def set_port(port)
90
+ @port = port
91
+ end
92
+
93
+ def get_port
94
+ @port
95
+ end
96
+
97
+ def set_path(path)
98
+ @path = path
99
+ end
100
+
101
+ def get_path
102
+ @path
103
+ end
104
+
105
+ def set_query(query)
106
+ @query = query
107
+ end
108
+
109
+ def get_query
110
+ @query
111
+ end
112
+
113
+ def set_url(url)
114
+ @url = url
115
+ end
116
+
117
+ def get_url
118
+ @url
119
+ end
120
+
121
+ def set_method(method)
122
+ @method = method
123
+ end
124
+
125
+ def get_method
126
+ @method
127
+ end
128
+
129
+ def set_query(query)
130
+ @query = query
131
+ end
132
+
133
+ def get_query
134
+ @query
135
+ end
136
+
137
+ def set_url(url)
138
+ @url = url
139
+ end
140
+
141
+ def get_url
142
+ @url
143
+ end
144
+ end
145
+
@@ -0,0 +1,45 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ class ResponseModel
16
+ attr_accessor :status_code, :error, :headers, :data
17
+
18
+ def initialize
19
+ @status_code = nil
20
+ @error = nil
21
+ @headers = {}
22
+ @data = nil
23
+ end
24
+
25
+ def set_status_code(code)
26
+ @status_code = code
27
+ end
28
+
29
+ def set_headers(headers)
30
+ @headers = headers
31
+ end
32
+
33
+ def set_data(data)
34
+ @data = data
35
+ end
36
+
37
+ def get_data
38
+ @data
39
+ end
40
+
41
+ def set_error(error)
42
+ @error = error
43
+ end
44
+ end
45
+
@@ -0,0 +1,76 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative '../evaluators/segment_evaluator'
16
+ require_relative '../../../models/settings/settings_model'
17
+ require_relative '../../../utils/gateway_service_util'
18
+ require_relative '../../../enums/url_enum'
19
+ require_relative '../../../services/logger_service'
20
+ require_relative '../../../models/user/context_model'
21
+ require_relative '../../../models/campaign/feature_model'
22
+ require_relative '../../../models/user/context_vwo_model'
23
+ require_relative '../../../services/settings_service'
24
+ require_relative '../../../utils/data_type_util'
25
+ require_relative '../../../enums/log_level_enum'
26
+ class SegmentationManager
27
+ @@instance = nil # Singleton instance
28
+
29
+ attr_accessor :evaluator
30
+
31
+ def self.instance
32
+ @@instance ||= SegmentationManager.new
33
+ end
34
+
35
+ def initialize
36
+ @evaluator = SegmentEvaluator.new
37
+ end
38
+
39
+ def attach_evaluator(evaluator = nil)
40
+ @evaluator = evaluator || SegmentEvaluator.new
41
+ end
42
+
43
+ # Set the context for the segmentation evaluator
44
+ # @param settings [SettingsModel] The settings model
45
+ # @param feature [FeatureModel] The feature model
46
+ # @param context [ContextModel] The context model
47
+ def set_contextual_data(settings, feature, context)
48
+ attach_evaluator
49
+ @evaluator.settings = settings
50
+ @evaluator.context = context
51
+ @evaluator.feature = feature
52
+
53
+ # If both user agent and IP address are null, avoid making a gateway service call
54
+ return if context&.get_user_agent.nil? && context&.get_ip_address.nil?
55
+
56
+ if feature.get_is_gateway_service_required
57
+ if SettingsService.instance.is_gateway_service_provided && (context.get_vwo.nil?)
58
+ query_params = {}
59
+ query_params['userAgent'] = context.get_user_agent if context&.get_user_agent
60
+ query_params['ipAddress'] = context.get_ip_address if context&.get_ip_address
61
+
62
+ begin
63
+ params = get_query_params(query_params)
64
+ vwo_data = get_from_gateway_service(params, UrlEnum::GET_USER_DATA)
65
+ context.set_vwo(ContextVWOModel.new.model_from_dictionary(vwo_data))
66
+ rescue StandardError => e
67
+ LoggerService.log(LogLevelEnum::ERROR, "Error in setting contextual data for segmentation. Got error: #{e.message}", nil)
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def validate_segmentation(dsl, properties)
74
+ @evaluator.is_segmentation_valid(dsl, properties)
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module SegmentOperandRegexEnum
16
+ LOWER = '^lower'
17
+ LOWER_MATCH = '^lower\\((.*)\\)'
18
+ WILDCARD = '^wildcard'
19
+ WILDCARD_MATCH = '^wildcard\\((.*)\\)'
20
+ REGEX = '^regex'
21
+ REGEX_MATCH = '^regex\\((.*)\\)'
22
+ STARTING_STAR = '^\\*'
23
+ ENDING_STAR = '\\*$'
24
+ GREATER_THAN_MATCH = '^gt\\((\\d+\\.?\\d*|\\.\\d+)\\)'
25
+ GREATER_THAN_EQUAL_TO_MATCH = '^gte\\((\\d+\\.?\\d*|\\.\\d+)\\)'
26
+ LESS_THAN_MATCH = '^lt\\((\\d+\\.?\\d*|\\.\\d+)\\)'
27
+ LESS_THAN_EQUAL_TO_MATCH = '^lte\\((\\d+\\.?\\d*|\\.\\d+)\\)'
28
+ end
29
+
@@ -0,0 +1,26 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module SegmentOperandValueEnum
16
+ LOWER_VALUE = 1
17
+ STARTING_ENDING_STAR_VALUE = 2
18
+ STARTING_STAR_VALUE = 3
19
+ ENDING_STAR_VALUE = 4
20
+ REGEX_VALUE = 5
21
+ EQUAL_VALUE = 6
22
+ GREATER_THAN_VALUE = 7
23
+ GREATER_THAN_EQUAL_TO_VALUE = 8
24
+ LESS_THAN_VALUE = 9
25
+ LESS_THAN_EQUAL_TO_VALUE = 10
26
+ end
@@ -0,0 +1,30 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module SegmentOperatorValueEnum
16
+ AND = 'and'
17
+ NOT = 'not'
18
+ OR = 'or'
19
+ CUSTOM_VARIABLE = 'custom_variable'
20
+ USER = 'user'
21
+ COUNTRY = 'country'
22
+ REGION = 'region'
23
+ CITY = 'city'
24
+ OPERATING_SYSTEM = 'os'
25
+ DEVICE_TYPE = 'device_type'
26
+ DEVICE = 'device'
27
+ BROWSER_AGENT = 'browser_string'
28
+ UA = 'ua'
29
+ FEATURE_ID = 'featureId'
30
+ end
@@ -0,0 +1,210 @@
1
+ # Copyright 2025 Wingify Software Pvt. Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'json'
16
+ require_relative '../../../decorators/storage_decorator'
17
+ require_relative '../../../models/settings/settings_model'
18
+ require_relative '../../../models/user/context_model'
19
+ require_relative '../../../models/campaign/feature_model'
20
+ require_relative '../../../services/storage_service'
21
+ require_relative '../../../services/logger_service'
22
+ require_relative '../enums/segment_operator_value_enum'
23
+ require_relative '../core/segmentation_manager'
24
+ require_relative '../utils/segment_util'
25
+ require_relative './segment_operand_evaluator'
26
+ require_relative '../../../enums/log_level_enum'
27
+ class SegmentEvaluator
28
+ attr_accessor :context, :settings, :feature
29
+
30
+ def initialize(context = nil, settings = nil, feature = nil)
31
+ @context = context
32
+ @settings = settings
33
+ @feature = feature
34
+ end
35
+
36
+ # Validates if the segmentation defined in the DSL is applicable based on the provided properties.
37
+ # @param dsl [Hash] The DSL node to evaluate
38
+ # @param properties [Hash] The properties to evaluate the DSL against
39
+ # @return [Boolean] True if the segmentation is valid, false otherwise
40
+ def is_segmentation_valid(dsl, properties)
41
+ key_value = get_key_value(dsl)
42
+ return false unless key_value
43
+
44
+ operator = key_value[:key]
45
+ sub_dsl = key_value[:value]
46
+
47
+ case operator
48
+ when SegmentOperatorValueEnum::NOT
49
+ !is_segmentation_valid(sub_dsl, properties)
50
+ when SegmentOperatorValueEnum::AND
51
+ every(sub_dsl, properties)
52
+ when SegmentOperatorValueEnum::OR
53
+ some(sub_dsl, properties)
54
+ when SegmentOperatorValueEnum::CUSTOM_VARIABLE
55
+ SegmentOperandEvaluator.new.evaluate_custom_variable_dsl(sub_dsl, properties)
56
+ when SegmentOperatorValueEnum::USER
57
+ SegmentOperandEvaluator.new.evaluate_user_dsl(sub_dsl, properties)
58
+ when SegmentOperatorValueEnum::UA
59
+ SegmentOperandEvaluator.new.evaluate_user_agent_dsl(sub_dsl, @context)
60
+ else
61
+ false
62
+ end
63
+ end
64
+
65
+ # Evaluates if any of the DSL nodes are valid using the OR logic.
66
+ # @param dsl_nodes [Array<Hash>] The DSL nodes to evaluate
67
+ # @param custom_variables [Hash] The custom variables
68
+ # @return [Boolean] True if any of the DSL nodes are valid, false otherwise
69
+ def some(dsl_nodes, custom_variables)
70
+ ua_parser_map = {}
71
+ key_count = 0
72
+ is_ua_parser = false
73
+
74
+ dsl_nodes.each do |dsl|
75
+ dsl.each do |key, value|
76
+ if [SegmentOperatorValueEnum::OPERATING_SYSTEM, SegmentOperatorValueEnum::BROWSER_AGENT,
77
+ SegmentOperatorValueEnum::DEVICE_TYPE, SegmentOperatorValueEnum::DEVICE].include?(key)
78
+ is_ua_parser = true
79
+ ua_parser_map[key] ||= []
80
+ ua_parser_map[key] += Array(value).map(&:to_s)
81
+ key_count += 1
82
+ end
83
+
84
+ if key == SegmentOperatorValueEnum::FEATURE_ID
85
+ feature_id_object = dsl[key]
86
+ feature_id_key = feature_id_object.keys.first
87
+ feature_id_value = feature_id_object[feature_id_key]
88
+
89
+ if %w[on off].include?(feature_id_value)
90
+ feature = @settings.get_features.find { |f| f.get_id == feature_id_key.to_i }
91
+ if feature
92
+ feature_key = feature.get_key
93
+ result = check_in_user_storage(@settings, feature_key, @context)
94
+ return !result if feature_id_value == 'off'
95
+ return result
96
+ else
97
+ LoggerService.log(LogLevelEnum::ERROR, "Feature not found with featureIdKey: #{feature_id_key}", nil)
98
+ return nil
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ return check_user_agent_parser(ua_parser_map) if is_ua_parser && key_count == dsl_nodes.length
105
+ return true if is_segmentation_valid(dsl, custom_variables)
106
+ end
107
+
108
+ false
109
+ end
110
+
111
+ # Evaluates all DSL nodes using the AND logic.
112
+ # @param dsl_nodes [Array<Hash>] The DSL nodes to evaluate
113
+ # @param custom_variables [Hash] The custom variables
114
+ # @return [Boolean] True if all DSL nodes are valid, false otherwise
115
+ def every(dsl_nodes, custom_variables)
116
+ location_map = {}
117
+ dsl_nodes.each do |dsl|
118
+ if dsl.keys.any? { |key| [SegmentOperatorValueEnum::COUNTRY, SegmentOperatorValueEnum::REGION,
119
+ SegmentOperatorValueEnum::CITY].include?(key) }
120
+ add_location_values_to_map(dsl, location_map)
121
+ return check_location_pre_segmentation(location_map) if location_map.keys.length == dsl_nodes.length
122
+ next
123
+ end
124
+ return false unless is_segmentation_valid(dsl, custom_variables)
125
+ end
126
+ true
127
+ end
128
+
129
+ # Adds the location values to the map
130
+ # @param dsl [Hash] The DSL node
131
+ # @param location_map [Hash] The location map
132
+ def add_location_values_to_map(dsl, location_map)
133
+ location_map[SegmentOperatorValueEnum::COUNTRY] = dsl[SegmentOperatorValueEnum::COUNTRY] if dsl.key?(SegmentOperatorValueEnum::COUNTRY)
134
+ location_map[SegmentOperatorValueEnum::REGION] = dsl[SegmentOperatorValueEnum::REGION] if dsl.key?(SegmentOperatorValueEnum::REGION)
135
+ location_map[SegmentOperatorValueEnum::CITY] = dsl[SegmentOperatorValueEnum::CITY] if dsl.key?(SegmentOperatorValueEnum::CITY)
136
+ end
137
+
138
+ # Checks if the location pre-segmentation is valid
139
+ # @param location_map [Hash] The location map
140
+ # @return [Boolean] True if the location pre-segmentation is valid, false otherwise
141
+ def check_location_pre_segmentation(location_map)
142
+ unless @context&.get_ip_address
143
+ LoggerService.log(LogLevelEnum::ERROR, 'To evaluate location pre Segment, please pass ipAddress in context object', nil)
144
+ return false
145
+ end
146
+
147
+ unless @context&.get_vwo&.get_location
148
+ return false
149
+ end
150
+
151
+ values_match(location_map, @context.get_vwo.get_location)
152
+ end
153
+
154
+ # Checks if the user agent parser is valid
155
+ # @param ua_parser_map [Hash] The user agent parser map
156
+ # @return [Boolean] True if the user agent parser is valid, false otherwise
157
+ def check_user_agent_parser(ua_parser_map)
158
+ unless @context&.get_user_agent
159
+ LoggerService.log(LogLevelEnum::ERROR, 'To evaluate user agent related segments, please pass userAgent in context object', nil)
160
+ return false
161
+ end
162
+
163
+ unless @context&.get_vwo&.get_ua_info
164
+ return false
165
+ end
166
+
167
+ check_value_present(ua_parser_map, @context.get_vwo.get_ua_info)
168
+ end
169
+
170
+ # Checks if the feature key is present in the user's storage
171
+ # @param settings [SettingsModel] The settings for the VWO instance
172
+ # @param feature_key [String] The key of the feature to check
173
+ # @param context [ContextModel] The context for the evaluation
174
+ # @return [Boolean] True if the feature key is present in the user's storage, false otherwise
175
+ def check_in_user_storage(settings, feature_key, context)
176
+ storage_service = StorageService.new
177
+ stored_data = StorageDecorator.new.get_feature_from_storage(feature_key, context, storage_service)
178
+ stored_data.is_a?(Hash) && !stored_data.empty?
179
+ end
180
+
181
+ # Checks if the expected values are present in the actual values
182
+ # @param expected_map [Hash] The expected values
183
+ # @param actual_map [Hash] The actual values
184
+ # @return [Boolean] True if the expected values are present in the actual values, false otherwise
185
+ def check_value_present(expected_map, actual_map)
186
+ actual_map.each do |key, actual_value|
187
+ next unless expected_map.key?(key)
188
+
189
+ expected_values = expected_map[key].map(&:downcase)
190
+
191
+ return true if expected_values.any? { |val| val.start_with?('wildcard(') && val.end_with?(')') && actual_value.match?(Regexp.new(val[9..-2].gsub('*', '.*'), Regexp::IGNORECASE)) }
192
+ return true if expected_values.include?(actual_value.downcase)
193
+ end
194
+ false
195
+ end
196
+
197
+ # Checks if the expected location values match the user's location
198
+ # @param expected_location_map [Hash] The expected location values
199
+ # @param user_location [Hash] The user's location values
200
+ # @return [Boolean] True if the expected location values match the user's location, false otherwise
201
+ def values_match(expected_location_map, user_location)
202
+ expected_location_map.all? { |key, value| normalize_value(value) == normalize_value(user_location[key]) }
203
+ end
204
+
205
+ def normalize_value(value)
206
+ return nil if value.nil?
207
+
208
+ value.to_s.gsub(/^"|"$/, '').strip
209
+ end
210
+ end