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.
- checksums.yaml +4 -4
- data/lib/vwo/api/get_flag.rb +236 -0
- data/lib/vwo/api/set_attribute.rb +57 -0
- data/lib/vwo/api/track_event.rb +77 -0
- data/lib/vwo/constants/constants.rb +54 -0
- data/lib/vwo/decorators/storage_decorator.rb +86 -0
- data/lib/vwo/{utils/logger_helper.rb → enums/api_enum.rb} +5 -10
- data/lib/vwo/enums/campaign_type_enum.rb +19 -0
- data/lib/vwo/enums/decision_types_enum.rb +18 -0
- data/lib/vwo/enums/event_enum.rb +19 -0
- data/lib/vwo/enums/headers_enum.rb +20 -0
- data/lib/vwo/enums/hooks_enum.rb +17 -0
- data/lib/vwo/enums/http_method_enum.rb +18 -0
- data/lib/vwo/enums/log_level_enum.rb +21 -0
- data/lib/vwo/enums/status_enum.rb +19 -0
- data/lib/vwo/enums/storage_enum.rb +22 -0
- data/lib/vwo/enums/url_enum.rb +21 -0
- data/lib/vwo/models/campaign/campaign_model.rb +192 -0
- data/lib/vwo/models/campaign/feature_model.rb +111 -0
- data/lib/vwo/models/campaign/impact_campaign_model.rb +38 -0
- data/lib/vwo/models/campaign/metric_model.rb +44 -0
- data/lib/vwo/models/campaign/rule_model.rb +56 -0
- data/lib/vwo/models/campaign/variable_model.rb +51 -0
- data/lib/vwo/models/campaign/variation_model.rb +137 -0
- data/lib/vwo/models/gateway_service_model.rb +39 -0
- data/lib/vwo/models/schemas/settings_schema_validation.rb +102 -0
- data/lib/vwo/models/settings/settings_model.rb +85 -0
- data/lib/vwo/models/storage/storage_data_model.rb +44 -0
- data/lib/vwo/models/user/context_model.rb +100 -0
- data/lib/vwo/models/user/context_vwo_model.rb +38 -0
- data/lib/vwo/{utils/feature_flag_response.rb → models/user/get_flag_response.rb} +14 -14
- data/lib/vwo/models/vwo_options_model.rb +107 -0
- data/lib/vwo/packages/decision_maker/decision_maker.rb +60 -0
- data/lib/vwo/packages/logger/core/log_manager.rb +90 -0
- data/lib/vwo/packages/logger/core/transport_manager.rb +87 -0
- data/lib/vwo/packages/logger/log_message_builder.rb +70 -0
- data/lib/vwo/packages/logger/logger.rb +38 -0
- data/lib/vwo/packages/logger/transports/console_transport.rb +49 -0
- data/lib/vwo/packages/network_layer/client/network_client.rb +107 -0
- data/lib/vwo/packages/network_layer/handlers/request_handler.rb +37 -0
- data/lib/vwo/packages/network_layer/manager/network_manager.rb +78 -0
- data/lib/vwo/packages/network_layer/models/global_request_model.rb +105 -0
- data/lib/vwo/packages/network_layer/models/request_model.rb +145 -0
- data/lib/vwo/packages/network_layer/models/response_model.rb +45 -0
- data/lib/vwo/packages/segmentation_evaluator/core/segmentation_manager.rb +76 -0
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +29 -0
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_value_enum.rb +26 -0
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +30 -0
- data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +210 -0
- data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +198 -0
- data/lib/vwo/packages/segmentation_evaluator/utils/segment_util.rb +44 -0
- data/lib/vwo/{constants.rb → packages/storage/connector.rb} +12 -10
- data/lib/vwo/packages/storage/storage.rb +45 -0
- data/lib/vwo/services/campaign_decision_service.rb +153 -0
- data/lib/vwo/services/hooks_service.rb +51 -0
- data/lib/vwo/services/logger_service.rb +83 -0
- data/lib/vwo/services/settings_service.rb +120 -0
- data/lib/vwo/services/storage_service.rb +65 -0
- data/lib/vwo/utils/campaign_util.rb +249 -0
- data/lib/vwo/utils/data_type_util.rb +105 -0
- data/lib/vwo/utils/decision_util.rb +253 -0
- data/lib/vwo/utils/function_util.rb +123 -0
- data/lib/vwo/utils/gateway_service_util.rb +101 -0
- data/lib/vwo/utils/impression_util.rb +49 -0
- data/lib/vwo/utils/log_message_util.rb +42 -0
- data/lib/vwo/utils/meg_util.rb +350 -0
- data/lib/vwo/utils/network_util.rb +235 -0
- data/lib/vwo/utils/rule_evaluation_util.rb +57 -0
- data/lib/vwo/utils/settings_util.rb +38 -0
- data/lib/vwo/utils/url_util.rb +46 -0
- data/lib/vwo/utils/uuid_util.rb +55 -0
- data/lib/vwo/vwo_builder.rb +156 -11
- data/lib/vwo/vwo_client.rb +163 -113
- data/lib/vwo.rb +49 -31
- metadata +187 -9
- 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
|