wingify-fme-ruby-sdk 1.50.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 +7 -0
- data/lib/resources/debug_messages.json +21 -0
- data/lib/resources/error_messages.json +63 -0
- data/lib/resources/info_messages.json +43 -0
- data/lib/resources/warn_messages.json +1 -0
- data/lib/vwo.rb +40 -0
- data/lib/wingify/api/get_flag.rb +244 -0
- data/lib/wingify/api/set_attribute.rb +57 -0
- data/lib/wingify/api/track_event.rb +80 -0
- data/lib/wingify/constants/constants.rb +106 -0
- data/lib/wingify/decorators/storage_decorator.rb +82 -0
- data/lib/wingify/enums/api_enum.rb +23 -0
- data/lib/wingify/enums/campaign_type_enum.rb +19 -0
- data/lib/wingify/enums/debug_category_enum.rb +20 -0
- data/lib/wingify/enums/decision_types_enum.rb +18 -0
- data/lib/wingify/enums/event_enum.rb +22 -0
- data/lib/wingify/enums/headers_enum.rb +20 -0
- data/lib/wingify/enums/hooks_enum.rb +17 -0
- data/lib/wingify/enums/http_method_enum.rb +18 -0
- data/lib/wingify/enums/log_level_enum.rb +21 -0
- data/lib/wingify/enums/log_level_to_number.rb +27 -0
- data/lib/wingify/enums/status_enum.rb +19 -0
- data/lib/wingify/enums/storage_enum.rb +22 -0
- data/lib/wingify/enums/url_enum.rb +22 -0
- data/lib/wingify/models/campaign/campaign_model.rb +192 -0
- data/lib/wingify/models/campaign/feature_model.rb +111 -0
- data/lib/wingify/models/campaign/impact_campaign_model.rb +38 -0
- data/lib/wingify/models/campaign/metric_model.rb +44 -0
- data/lib/wingify/models/campaign/rule_model.rb +56 -0
- data/lib/wingify/models/campaign/variable_model.rb +51 -0
- data/lib/wingify/models/campaign/variation_model.rb +137 -0
- data/lib/wingify/models/gateway_service_model.rb +39 -0
- data/lib/wingify/models/schemas/settings_schema_validation.rb +104 -0
- data/lib/wingify/models/settings/settings_model.rb +101 -0
- data/lib/wingify/models/storage/storage_data_model.rb +44 -0
- data/lib/wingify/models/user/context_model.rb +154 -0
- data/lib/wingify/models/user/context_vwo_model.rb +38 -0
- data/lib/wingify/models/user/get_flag_response.rb +50 -0
- data/lib/wingify/models/vwo_options_model.rb +129 -0
- data/lib/wingify/packages/decision_maker/decision_maker.rb +60 -0
- data/lib/wingify/packages/logger/core/log_manager.rb +92 -0
- data/lib/wingify/packages/logger/core/transport_manager.rb +76 -0
- data/lib/wingify/packages/logger/log_message_builder.rb +70 -0
- data/lib/wingify/packages/logger/logger.rb +38 -0
- data/lib/wingify/packages/logger/transports/console_transport.rb +49 -0
- data/lib/wingify/packages/network_layer/client/network_client.rb +276 -0
- data/lib/wingify/packages/network_layer/handlers/request_handler.rb +37 -0
- data/lib/wingify/packages/network_layer/manager/network_manager.rb +145 -0
- data/lib/wingify/packages/network_layer/models/global_request_model.rb +105 -0
- data/lib/wingify/packages/network_layer/models/request_model.rb +179 -0
- data/lib/wingify/packages/network_layer/models/response_model.rb +62 -0
- data/lib/wingify/packages/segmentation_evaluator/core/segmentation_manager.rb +77 -0
- data/lib/wingify/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +29 -0
- data/lib/wingify/packages/segmentation_evaluator/enums/segment_operand_value_enum.rb +26 -0
- data/lib/wingify/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +33 -0
- data/lib/wingify/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +218 -0
- data/lib/wingify/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +415 -0
- data/lib/wingify/packages/segmentation_evaluator/utils/segment_util.rb +44 -0
- data/lib/wingify/packages/storage/connector.rb +26 -0
- data/lib/wingify/packages/storage/storage.rb +47 -0
- data/lib/wingify/services/batch_event_queue.rb +179 -0
- data/lib/wingify/services/campaign_decision_service.rb +161 -0
- data/lib/wingify/services/hooks_service.rb +51 -0
- data/lib/wingify/services/logger_service.rb +114 -0
- data/lib/wingify/services/settings_service.rb +178 -0
- data/lib/wingify/services/storage_service.rb +66 -0
- data/lib/wingify/utils/batch_event_dispatcher_util.rb +178 -0
- data/lib/wingify/utils/brand_context.rb +33 -0
- data/lib/wingify/utils/brand_util.rb +53 -0
- data/lib/wingify/utils/campaign_util.rb +284 -0
- data/lib/wingify/utils/data_type_util.rb +105 -0
- data/lib/wingify/utils/debugger_service_util.rb +40 -0
- data/lib/wingify/utils/decision_util.rb +259 -0
- data/lib/wingify/utils/event_util.rb +55 -0
- data/lib/wingify/utils/function_util.rb +141 -0
- data/lib/wingify/utils/gateway_service_util.rb +101 -0
- data/lib/wingify/utils/impression_util.rb +66 -0
- data/lib/wingify/utils/log_message_util.rb +42 -0
- data/lib/wingify/utils/meg_util.rb +357 -0
- data/lib/wingify/utils/network_util.rb +503 -0
- data/lib/wingify/utils/rule_evaluation_util.rb +57 -0
- data/lib/wingify/utils/settings_util.rb +38 -0
- data/lib/wingify/utils/usage_stats_util.rb +119 -0
- data/lib/wingify/utils/uuid_util.rb +96 -0
- data/lib/wingify/wingify_builder.rb +261 -0
- data/lib/wingify/wingify_client.rb +227 -0
- data/lib/wingify.rb +117 -0
- metadata +327 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ae10f6a02b6a5ddb698aed1f7ac3eb5461678e016dbd0e7084668bca8bcd0f62
|
|
4
|
+
data.tar.gz: ff5cae6fb46441e45f5b50fa1a8ce0149a356b622b41c0da7fedde092c75f87e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 06aaf6ca460223d50c225308579926ce7349142606a7d9ff3fc3a758d3c2d43a79276dcc02e7bf5c16c2915e76368442049f4b1fed011bbe650b2d10ed1d8ba1
|
|
7
|
+
data.tar.gz: ea43e8123d418bba625bf55dffed678713bc1513c90895aa60cce177ca77959ce460f919da56cc1bb1ce58d2ee8287c9274ee7f15399ddb974eefdd9d4700677
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"API_CALLED": "API - {apiName} called",
|
|
3
|
+
"SERVICE_INITIALIZED": "{brand} {service} initialized while creating an instance of SDK",
|
|
4
|
+
"USING_POLL_INTERVAL_FROM_SETTINGS": "key: poll_interval not found or invalid. Using poll_interval from {source} ({pollInterval})",
|
|
5
|
+
|
|
6
|
+
"EXPERIMENTS_EVALUATION_WHEN_ROLLOUT_PASSED": "Rollout rule got passed for user {userId}. Hence, evaluating experiments",
|
|
7
|
+
"EXPERIMENTS_EVALUATION_WHEN_NO_ROLLOUT_PRESENT": "No Rollout rules present for the feature. Hence, checking experiment rules",
|
|
8
|
+
|
|
9
|
+
"USER_BUCKET_TO_VARIATION": "User ID:{userId} for experiment:{campaignKey} having percent traffic:{percentTraffic} got bucket-value:{bucketValue} and hash-value:{hashValue}",
|
|
10
|
+
|
|
11
|
+
"IMPRESSION_FOR_TRACK_USER": "Impression built for vwo_variationShown({brand} standard event for tracking user) event haivng Account ID:{accountId}, User ID:{userId}, and experiment ID:{campaignId}",
|
|
12
|
+
"IMPRESSION_FOR_TRACK_GOAL": "Impression built for event:{eventName} event having Account ID:{accountId}, and user ID:{userId}",
|
|
13
|
+
"IMPRESSION_FOR_SYNC_VISITOR_PROP": "Impression built for {eventName}({brand} internal event) event for Account ID:{accountId}, and user ID:{userId}",
|
|
14
|
+
|
|
15
|
+
"CONFIG_BATCH_EVENT_LIMIT_EXCEEDED": "Impression event - {endPoint} failed due to exceeding payload size. Parameter eventsPerRequest in batchEvents config in init API has value:{eventsPerRequest} for account ID:{accountId}. Please read the official documentation for knowing the size limits",
|
|
16
|
+
|
|
17
|
+
"EVENT_BATCH_BEFORE_FLUSHING": "Flushing event queue {manually} having {length} events for Account ID:{accountId}. {timer}",
|
|
18
|
+
"EVENT_BATCH_FLUSH": "Manually flushing batch events for Account ID:{accountId} having {queueLength} events",
|
|
19
|
+
"BATCH_QUEUE_EMPTY": "Batch queue is empty. Nothing to flush.",
|
|
20
|
+
"WEB_UUID_FOUND": "VWO Web Testing identified UUID {uuid} as the Context ID for API {apiName}"
|
|
21
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"EXECUTION_FAILED": "API - {apiName} failed to execute. Error:{err}",
|
|
3
|
+
"INVALID_OPTIONS": "[ERROR]: {log_prefix} {date} Options should be of type object",
|
|
4
|
+
"INVALID_SDK_KEY_IN_OPTIONS": "[ERROR]: {log_prefix} {date} Please provide the sdkKey in the options and should be a of type string",
|
|
5
|
+
"INVALID_ACCOUNT_ID_IN_OPTIONS": "[ERROR]: {log_prefix} {date} Please provide {brand} account ID in the options and should be a of type string|number",
|
|
6
|
+
|
|
7
|
+
"ERROR_ADDING_CAMPAIGNS_TO_RULES": "Error setting and adding campaigns to rules. Error: {err}",
|
|
8
|
+
|
|
9
|
+
"INVALID_POLLING_CONFIGURATION": "Invalid key:{key} passed in options. Should be of type:{correctType} and greater than or equal to 1000",
|
|
10
|
+
|
|
11
|
+
"ERROR_FETCHING_SETTINGS": "Settings could not be fetched. Error:{err}",
|
|
12
|
+
"INVALID_SETTINGS_SCHEMA": "Settings are not valid for account ID: {accountId} and SDK key: {sdkKey}. Got settings as {settings}",
|
|
13
|
+
|
|
14
|
+
"ERROR_UPDATING_SETTINGS": "Error while updating settings. Error: {err}",
|
|
15
|
+
"UPDATING_CLIENT_INSTANCE_FAILED_WHEN_WEBHOOK_TRIGGERED": "Failed to fetch settings and hence {brand} client instance couldn't be updated when API: {apiName} got called having isViaWebhook param as {isViaWebhook}. Error: {err}",
|
|
16
|
+
|
|
17
|
+
"INVALID_PARAM": "Key:{key} passed to API:{apiName} is not of valid type. Got type:{type}, should be:{correctType}",
|
|
18
|
+
"INVALID_CONTEXT": "Context should be an object and must contain a mandatory key - id, which is User ID",
|
|
19
|
+
"INVALID_BUCKETING_SEED": "bucketingSeed passed to API:{apiName} is invalid (got type:{type}). It must be a non-empty string. Falling back to userId for bucketing.",
|
|
20
|
+
|
|
21
|
+
"FEATURE_NOT_FOUND": "Feature not found for the key:{featureKey}",
|
|
22
|
+
"EVENT_NOT_FOUND": "Event:{eventName} not found in any of the features' metrics",
|
|
23
|
+
|
|
24
|
+
"ERROR_READING_DATA_FROM_STORAGE": "Error while reading data from storage. Error: {err}",
|
|
25
|
+
"ERROR_STORING_DATA_IN_STORAGE": "Error while storing data in storage. Error: {err}",
|
|
26
|
+
|
|
27
|
+
"INVALID_GATEWAY_URL": "Please provide a valid URL for {brand} Gateway Service while initializing the SDK",
|
|
28
|
+
"INVALID_RETRY_CONFIG": "Retry config is invalid. Should be of type:object",
|
|
29
|
+
|
|
30
|
+
"NETWORK_CALL_FAILED": "Error occurred for {method} request. Error:{err}",
|
|
31
|
+
"NETWORK_CALL_RETRY_ATTEMPT": "Request failed for {endPoint}, Error: {err}. Retrying in {delay} seconds, attempt {attempt} of {maxRetries}",
|
|
32
|
+
"NETWORK_CALL_RETRY_FAILED": "Max retries reached. Request failed for {endPoint}, Error: {err}",
|
|
33
|
+
"ATTEMPTING_RETRY_FOR_FAILED_NETWORK_CALL": "Request failed for {endPoint}, Error: {err}. Retrying in {delay} seconds, attempt {attempt} of {maxRetries}",
|
|
34
|
+
"NETWORK_CALL_FAILURE_AFTER_MAX_RETRIES": "Network request failed after {attempts} attempts for {extraData}. Error: {err}",
|
|
35
|
+
|
|
36
|
+
"BATCH_QUEUE_EMPTY": "No batch queue present for account:{accountId} when calling flushEvents API. Check batchEvents config in init API",
|
|
37
|
+
"INVALID_BATCH_EVENTS_CONFIG": "Invalid batch events config, should be a hash, events_per_request should be a number greater than 0 and request_time_interval should be a number greater than 0",
|
|
38
|
+
"FAILED_TO_INITIALIZE_SERVICE": "Failed to initialize {service}: {err}",
|
|
39
|
+
|
|
40
|
+
"ERROR_SETTING_SEGMENTATION_CONTEXT": "Error in setting contextual data for segmentation. Got error: {err}",
|
|
41
|
+
"ERROR_VALIDATING_SEGMENTATION": "Error in validating segmentation. Got error: {err}",
|
|
42
|
+
"USER_AGENT_VALIDATION_ERROR": "Failed to validate user agent. Error: {err}",
|
|
43
|
+
"INVALID_IP_ADDRESS_IN_CONTEXT_FOR_PRE_SEGMENTATION": "ipAddress is required in context to evaluate location pre-segmentation",
|
|
44
|
+
"INVALID_USER_AGENT_IN_CONTEXT_FOR_PRE_SEGMENTATION": "userAgent is required in context to evaluate user-agent pre-segmentation",
|
|
45
|
+
"INVALID_ATTRIBUTE_LIST_FORMAT": "Invalid inList operand format",
|
|
46
|
+
"ERROR_FETCHING_DATA_FROM_GATEWAY": "Error while fetching data from gateway. Error: {err}",
|
|
47
|
+
|
|
48
|
+
"ATTRIBUTES_MAP_ERROR": "Attributes map must contain atleast 1 key-value pair",
|
|
49
|
+
"ATTRIBUTES_MAP_INVALID": "Attributes should be an object containing key-value pairs",
|
|
50
|
+
|
|
51
|
+
"VALUES_MISMATCH_BATCHING_NOT_ENABLED": "Values mismatch from the expectation of both parameters. Batching not initialized.",
|
|
52
|
+
"INVALID_EVENTS_PER_REQUEST_VALUE": "Events_per_request values is invalid (should be greater than 0 and less than 5000). Using default value of events_per_request parameter : 100",
|
|
53
|
+
"INVALID_REQUEST_TIME_INTERVAL_VALUE": "Request_time_interval values is invalid (should be greater than 0). Using default value of request_time_interval parameter : 600",
|
|
54
|
+
"BATCHING_NOT_ENABLED": "Batching is not enabled. Pass batchEventData in the SDK configuration while invoking init API.",
|
|
55
|
+
|
|
56
|
+
"ERROR_CHECKING_FEATURE_IN_USER_STORAGE": "Error while checking feature in user storage. Got error: {err}",
|
|
57
|
+
|
|
58
|
+
"ERROR_FINDING_WINNER_CAMPAIGN": "Error while finding winner campaign inside {method}. Error: {err}",
|
|
59
|
+
|
|
60
|
+
"ERROR_EVALUATING_RULE": "Error occurred while evaluating rule. Error: {err}",
|
|
61
|
+
"BATCH_FLUSH_FAILED": "Failed to send batch events. Re-enqueuing events for retry.",
|
|
62
|
+
"ERROR_PARSING_GATEWAY_URL": "Error occurred while parsing gateway service URL: {err}"
|
|
63
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ON_INIT_ALREADY_RESOLVED": "[INFO]: {log_prefix} {date} {apiName} already resolved",
|
|
3
|
+
"ON_INIT_SETTINGS_FAILED": "[INFO]: {log_prefix} {date} {brand} settings could not be fetched",
|
|
4
|
+
|
|
5
|
+
"POLLING_SET_SETTINGS": "There's a change in settings from the last settings fetched. Hence, instantiating a new {brand} client internally",
|
|
6
|
+
"POLLING_NO_CHANGE_IN_SETTINGS": "No change in settings with the last settings fetched. Hence, not instantiating new {brand} client",
|
|
7
|
+
|
|
8
|
+
"SETTINGS_FETCH_SUCCESS": "Settings fetched successfully",
|
|
9
|
+
|
|
10
|
+
"CLIENT_INITIALIZED": "{brand} Client initialized",
|
|
11
|
+
|
|
12
|
+
"STORED_VARIATION_FOUND": "Variation {variationKey} found in storage for the user {userId} for the {experimentType} experiment:{experimentKey}",
|
|
13
|
+
|
|
14
|
+
"USER_PART_OF_CAMPAIGN": "User ID:{userId} is {notPart} part of experiment:{campaignKey}",
|
|
15
|
+
"SEGMENTATION_SKIP": "For userId:{userId} of experiment:{campaignKey}, segments was missing. Hence, skipping segmentation",
|
|
16
|
+
"SEGMENTATION_STATUS": "Segmentation {status} for userId:{userId} of experiment:{campaignKey}",
|
|
17
|
+
|
|
18
|
+
"USER_CAMPAIGN_BUCKET_INFO": "User ID:{userId} for experiment:{campaignKey} {status}",
|
|
19
|
+
|
|
20
|
+
"WHITELISTING_SKIP": "Whitelisting is not used for experiment:{campaignKey}, hence skipping evaluating whitelisting {variation} for User ID:{userId}",
|
|
21
|
+
"WHITELISTING_STATUS": "User ID:{userId} for experiment:{campaignKey} {status} whitelisting {variationString}",
|
|
22
|
+
|
|
23
|
+
"VARIATION_RANGE_ALLOCATION": "Variation:{variationKey} of experiment:{campaignKey} having weight:{variationWeight} got bucketing range: ({startRange} - {endRange})",
|
|
24
|
+
|
|
25
|
+
"IMPACT_ANALYSIS": "Tracking feature:{featureKey} being {status} for Impact Analysis Campaign for the user {userId}",
|
|
26
|
+
|
|
27
|
+
"MEG_SKIP_ROLLOUT_EVALUATE_EXPERIMENTS": "No rollout rule found for feature:{featureKey}. Hence, evaluating experiments",
|
|
28
|
+
"MEG_CAMPAIGN_FOUND_IN_STORAGE": "Campaign {campaignKey} found in storage for user ID:{userId}",
|
|
29
|
+
"MEG_CAMPAIGN_ELIGIBLE": "Campaign {campaignKey} is eligible for user ID:{userId}",
|
|
30
|
+
"MEG_WINNER_CAMPAIGN": "MEG: Campaign {campaignKey} is the winner for group {groupId} for user ID:{userId} {algo}",
|
|
31
|
+
"SETTINGS_UPDATED": "Settings fetched and updated successfully on the current {brand} client instance when API: {apiName} got called having isViaWebhook param as {isViaWebhook}",
|
|
32
|
+
"NETWORK_CALL_SUCCESS": "Impression for {event} - {endPoint} was successfully received by {brand} having Account ID:{accountId}, UUID: {uuid}",
|
|
33
|
+
|
|
34
|
+
"EVENT_BATCH_DEFAULTS": "{parameter} in SDK configuration is missing or invalid (should be greater than {minLimit}). Using default value: {defaultValue}",
|
|
35
|
+
"EVENT_QUEUE": "Event with payload:{event} pushed to the {queueType} queue",
|
|
36
|
+
"EVENT_BATCH_After_FLUSHING": "Event queue having {length} events has been flushed {manually}",
|
|
37
|
+
"IMPRESSION_BATCH_SUCCESS": "Impression event - {endPoint} was successfully received by {brand} having Account ID:{accountId}",
|
|
38
|
+
"IMPRESSION_BATCH_FAILED": "Batch events couldn\"t be received by {brand}. Calling Flush Callback with error and data",
|
|
39
|
+
"EVENT_BATCH_MAX_LIMIT": "{parameter} passed in SDK configuration is greater than the maximum limit of {maxLimit}. Setting it to the maximum limit",
|
|
40
|
+
"GATEWAY_AND_BATCH_EVENTS_CONFIG_MISMATCH": "Batch Events config passed in SDK configuration will not work as the gatewayService is already configured. Please check the documentation for more details",
|
|
41
|
+
"NETWORK_CALL_SUCCESS_WITH_RETRIES": "Network call for {extraData} succeeded after {attempts} retry attempt(s). Previous attempts failed with error: {err}",
|
|
42
|
+
"PROXY_AND_GATEWAY_SERVICE_PROVIDED": "Both proxy URL and gateway service are provided. Gateway service will be used and proxy URL will be ignored"
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
data/lib/vwo.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Copyright 2024-2026 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 'wingify'
|
|
16
|
+
require_relative 'wingify/wingify_client'
|
|
17
|
+
require_relative 'wingify/wingify_builder'
|
|
18
|
+
|
|
19
|
+
# Map existing VWO classes to Wingify aliases to maintain 100% backward compatibility
|
|
20
|
+
VWOClient = WingifyClient
|
|
21
|
+
VWOBuilder = WingifyBuilder
|
|
22
|
+
|
|
23
|
+
module VWO
|
|
24
|
+
# Backward compatible VWO facade
|
|
25
|
+
# It intercepts the init call, forces the is_via_vwo flag, and forwards to Wingify core.
|
|
26
|
+
def self.init(options)
|
|
27
|
+
options[:is_via_vwo] = true
|
|
28
|
+
Wingify.init(options)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.get_uuid(user_id, account_id)
|
|
32
|
+
Wingify.get_uuid(user_id, account_id)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Keep VWO.new backward compatible for any customers manually instantiating it,
|
|
36
|
+
# although standard docs recommend VWO.init
|
|
37
|
+
def self.new(options)
|
|
38
|
+
init(options)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Copyright 2024-2026 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 '../enums/api_enum'
|
|
16
|
+
require_relative '../enums/campaign_type_enum'
|
|
17
|
+
require_relative '../enums/decision_types_enum'
|
|
18
|
+
require_relative '../decorators/storage_decorator'
|
|
19
|
+
require_relative '../services/storage_service'
|
|
20
|
+
require_relative '../models/campaign/feature_model'
|
|
21
|
+
require_relative '../models/campaign/campaign_model'
|
|
22
|
+
require_relative '../packages/segmentation_evaluator/core/segmentation_manager'
|
|
23
|
+
require_relative '../services/logger_service'
|
|
24
|
+
require_relative '../utils/campaign_util'
|
|
25
|
+
require_relative '../utils/rule_evaluation_util'
|
|
26
|
+
require_relative '../utils/impression_util'
|
|
27
|
+
require_relative '../utils/decision_util'
|
|
28
|
+
require_relative '../models/user/get_flag_response'
|
|
29
|
+
require_relative '../packages/storage/storage'
|
|
30
|
+
|
|
31
|
+
class FlagApi
|
|
32
|
+
# Get the flag for a given feature key and context
|
|
33
|
+
# @param feature_key [String] The key of the feature to get the flag for
|
|
34
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
|
35
|
+
# @param context [ContextModel] The context for the evaluation
|
|
36
|
+
# @param hooks_service [HooksService] The hooks service for the VWO instance
|
|
37
|
+
# @return [GetFlagResponse] The flag for the given feature key and context
|
|
38
|
+
def get(feature_key, settings, context, hooks_service)
|
|
39
|
+
is_enabled = false
|
|
40
|
+
rollout_variation_to_return = nil
|
|
41
|
+
experiment_variation_to_return = nil
|
|
42
|
+
should_check_for_experiments_rules = false
|
|
43
|
+
|
|
44
|
+
passed_rules_information = {} # for storing integration callback
|
|
45
|
+
evaluated_feature_map = {}
|
|
46
|
+
|
|
47
|
+
# Fetch feature object using the feature key
|
|
48
|
+
feature = get_feature_from_key(settings, feature_key)
|
|
49
|
+
|
|
50
|
+
decision = {
|
|
51
|
+
feature_name: feature&.get_name,
|
|
52
|
+
feature_id: feature&.get_id,
|
|
53
|
+
feature_key: feature&.get_key,
|
|
54
|
+
user_id: context&.get_id,
|
|
55
|
+
api: ApiEnum::GET_FLAG
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
storage_service = StorageService.new
|
|
59
|
+
if Storage.instance.is_storage_enabled
|
|
60
|
+
stored_data = StorageDecorator.new.get_feature_from_storage(feature_key, context, storage_service)
|
|
61
|
+
|
|
62
|
+
if stored_data && stored_data[:experiment_variation_id]
|
|
63
|
+
if stored_data[:experiment_key]
|
|
64
|
+
variation = CampaignUtil.get_variation_from_campaign_key(settings, stored_data[:experiment_key], stored_data[:experiment_variation_id])
|
|
65
|
+
|
|
66
|
+
if variation
|
|
67
|
+
LoggerService.log(LogLevelEnum::INFO, "STORED_VARIATION_FOUND", {variationKey: variation.get_key, userId: context.get_id, experimentKey: stored_data[:experiment_key], experimentType: "experiment"})
|
|
68
|
+
return GetFlagResponse.new(true, variation.get_variables, context.get_uuid, context.get_session_id)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
elsif stored_data && stored_data[:rollout_key] && stored_data[:rollout_id]
|
|
72
|
+
variation = CampaignUtil.get_variation_from_campaign_key(settings, stored_data[:rollout_key], stored_data[:rollout_variation_id])
|
|
73
|
+
|
|
74
|
+
if variation
|
|
75
|
+
LoggerService.log(LogLevelEnum::INFO, "STORED_VARIATION_FOUND", {variationKey: variation.get_key, userId: context.get_id, experimentKey: stored_data[:rollout_key], experimentType: "rollout"})
|
|
76
|
+
LoggerService.log(LogLevelEnum::DEBUG, "EXPERIMENTS_EVALUATION_WHEN_ROLLOUT_PASSED", {userId: context.get_id})
|
|
77
|
+
|
|
78
|
+
is_enabled = true
|
|
79
|
+
should_check_for_experiments_rules = true
|
|
80
|
+
rollout_variation_to_return = variation
|
|
81
|
+
feature_info = {
|
|
82
|
+
rollout_id: stored_data[:rollout_id],
|
|
83
|
+
rollout_key: stored_data[:rollout_key],
|
|
84
|
+
rollout_variation_id: stored_data[:rollout_variation_id]
|
|
85
|
+
}
|
|
86
|
+
evaluated_feature_map[feature_key] = feature_info
|
|
87
|
+
passed_rules_information.merge!(feature_info)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if feature.nil?
|
|
93
|
+
LoggerService.log(LogLevelEnum::ERROR, "FEATURE_NOT_FOUND", {featureKey: feature_key, an: ApiEnum::GET_FLAG, sId: context.get_session_id, uuid: context.get_uuid})
|
|
94
|
+
return GetFlagResponse.new(false, [], context.get_uuid, context.get_session_id)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Segmentation evaluation
|
|
98
|
+
SegmentationManager.instance.set_contextual_data(settings, feature, context)
|
|
99
|
+
|
|
100
|
+
# Get all rollout rules
|
|
101
|
+
rollout_rules = get_specific_rules_based_on_type(feature, CampaignTypeEnum::ROLLOUT)
|
|
102
|
+
|
|
103
|
+
if rollout_rules.any? && !is_enabled
|
|
104
|
+
rollout_rules_to_evaluate = []
|
|
105
|
+
|
|
106
|
+
rollout_rules.each do |rule|
|
|
107
|
+
result = evaluate_rule(settings, feature, rule, context, evaluated_feature_map, nil, storage_service, decision)
|
|
108
|
+
pre_segmentation_result = result[:pre_segmentation_result]
|
|
109
|
+
updated_decision = result[:updated_decision]
|
|
110
|
+
decision.merge!(updated_decision) if updated_decision
|
|
111
|
+
|
|
112
|
+
if pre_segmentation_result
|
|
113
|
+
rollout_rules_to_evaluate << rule
|
|
114
|
+
evaluated_feature_map[feature_key] = {
|
|
115
|
+
rollout_id: rule.get_id,
|
|
116
|
+
rollout_key: rule.get_key,
|
|
117
|
+
rollout_variation_id: rule.get_variations.first&.get_id
|
|
118
|
+
}
|
|
119
|
+
break
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
if rollout_rules_to_evaluate.any?
|
|
124
|
+
passed_rollout_campaign = CampaignModel.new.model_from_dictionary(rollout_rules_to_evaluate.first)
|
|
125
|
+
variation = DecisionUtil.evaluate_traffic_and_get_variation(settings, passed_rollout_campaign, context)
|
|
126
|
+
|
|
127
|
+
if variation
|
|
128
|
+
is_enabled = true
|
|
129
|
+
should_check_for_experiments_rules = true
|
|
130
|
+
rollout_variation_to_return = variation
|
|
131
|
+
update_integrations_decision_object(passed_rollout_campaign, variation, passed_rules_information, decision)
|
|
132
|
+
|
|
133
|
+
create_and_send_impression_for_variation_shown(settings, passed_rollout_campaign.get_id, variation.get_id, context, feature_key)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
elsif rollout_rules.empty?
|
|
137
|
+
LoggerService.log(LogLevelEnum::DEBUG, "EXPERIMENTS_EVALUATION_WHEN_NO_ROLLOUT_PRESENT")
|
|
138
|
+
should_check_for_experiments_rules = true
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
if should_check_for_experiments_rules
|
|
142
|
+
experiment_rules = get_all_experiment_rules(feature)
|
|
143
|
+
experiment_rules_to_evaluate = []
|
|
144
|
+
meg_group_winner_campaigns = {}
|
|
145
|
+
|
|
146
|
+
experiment_rules.each do |rule|
|
|
147
|
+
result = evaluate_rule(settings, feature, rule, context, evaluated_feature_map, meg_group_winner_campaigns, storage_service, decision)
|
|
148
|
+
pre_segmentation_result = result[:pre_segmentation_result]
|
|
149
|
+
whitelisted_object = result[:whitelisted_object]
|
|
150
|
+
updated_decision = result[:updated_decision]
|
|
151
|
+
decision.merge!(updated_decision) if updated_decision
|
|
152
|
+
|
|
153
|
+
if pre_segmentation_result
|
|
154
|
+
if whitelisted_object.nil?
|
|
155
|
+
experiment_rules_to_evaluate << rule
|
|
156
|
+
else
|
|
157
|
+
is_enabled = true
|
|
158
|
+
experiment_variation_to_return = whitelisted_object[:variation]
|
|
159
|
+
passed_rules_information.merge!(
|
|
160
|
+
experiment_id: rule.get_id,
|
|
161
|
+
experiment_key: rule.get_key,
|
|
162
|
+
experiment_variation_id: whitelisted_object[:variation_id]
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
break
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
if experiment_rules_to_evaluate.any?
|
|
170
|
+
campaign = CampaignModel.new.model_from_dictionary(experiment_rules_to_evaluate.first)
|
|
171
|
+
variation = DecisionUtil.evaluate_traffic_and_get_variation(settings, campaign, context)
|
|
172
|
+
|
|
173
|
+
if variation
|
|
174
|
+
is_enabled = true
|
|
175
|
+
experiment_variation_to_return = variation
|
|
176
|
+
update_integrations_decision_object(campaign, variation, passed_rules_information, decision)
|
|
177
|
+
|
|
178
|
+
create_and_send_impression_for_variation_shown(settings, campaign.get_id, variation.get_id, context, feature_key)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Store evaluated feature in storage
|
|
184
|
+
if is_enabled
|
|
185
|
+
begin
|
|
186
|
+
StorageDecorator.new.set_data_in_storage(
|
|
187
|
+
{ feature_key: feature_key, context: context }.merge(passed_rules_information),
|
|
188
|
+
storage_service
|
|
189
|
+
)
|
|
190
|
+
rescue StandardError => e
|
|
191
|
+
LoggerService.log(LogLevelEnum::ERROR, "ERROR_STORING_DATA_IN_STORAGE", { err: e.message, an: ApiEnum::GET_FLAG, sId: context.get_session_id, uuid: context.get_uuid})
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Execute hooks
|
|
196
|
+
hooks_service.set(decision)
|
|
197
|
+
hooks_service.execute(hooks_service.get)
|
|
198
|
+
|
|
199
|
+
if feature&.get_impact_campaign&.get_campaign_id
|
|
200
|
+
LoggerService.log(
|
|
201
|
+
LogLevelEnum::INFO,
|
|
202
|
+
"IMPACT_ANALYSIS",
|
|
203
|
+
{
|
|
204
|
+
userId: context.get_id,
|
|
205
|
+
featureKey: feature_key,
|
|
206
|
+
status: is_enabled ? 'enabled' : 'disabled'
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
variation_id = is_enabled ? 2 : 1 # 2 for Variation(flag enabled), 1 for Control(flag disabled)
|
|
211
|
+
|
|
212
|
+
create_and_send_impression_for_variation_shown(
|
|
213
|
+
settings,
|
|
214
|
+
feature&.get_impact_campaign&.get_campaign_id,
|
|
215
|
+
variation_id,
|
|
216
|
+
context,
|
|
217
|
+
feature_key
|
|
218
|
+
)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Return final evaluated feature flag
|
|
222
|
+
return GetFlagResponse.new(is_enabled, experiment_variation_to_return&.get_variables || rollout_variation_to_return&.get_variables || [], context.get_uuid, context.get_session_id)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
private
|
|
226
|
+
|
|
227
|
+
def update_integrations_decision_object(campaign, variation, passed_rules_information, decision)
|
|
228
|
+
if campaign.get_type == CampaignTypeEnum::ROLLOUT
|
|
229
|
+
passed_rules_information.merge!(
|
|
230
|
+
rollout_id: campaign.get_id,
|
|
231
|
+
rollout_key: campaign.get_key,
|
|
232
|
+
rollout_variation_id: variation.get_id
|
|
233
|
+
)
|
|
234
|
+
else
|
|
235
|
+
passed_rules_information.merge!(
|
|
236
|
+
experiment_id: campaign.get_id,
|
|
237
|
+
experiment_key: campaign.get_key,
|
|
238
|
+
experiment_variation_id: variation.get_id
|
|
239
|
+
)
|
|
240
|
+
end
|
|
241
|
+
decision.merge!(passed_rules_information)
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Copyright 2024-2026 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 '../models/user/context_model'
|
|
16
|
+
require_relative '../enums/event_enum'
|
|
17
|
+
require_relative '../utils/network_util'
|
|
18
|
+
require_relative '../services/batch_event_queue'
|
|
19
|
+
|
|
20
|
+
class SetAttributeApi
|
|
21
|
+
# Sets multiple attributes for a user in a single network call.
|
|
22
|
+
# @param attributes [Hash] Key-value map of attributes.
|
|
23
|
+
# @param context [ContextModel] Context containing user information.
|
|
24
|
+
def set_attribute(attributes, context)
|
|
25
|
+
create_impression_for_attributes(attributes, context)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# Creates an impression for multiple user attributes and sends it to the server.
|
|
31
|
+
# @param attributes [Hash] Key-value map of attributes.
|
|
32
|
+
# @param context [ContextModel] Context containing user information.
|
|
33
|
+
def create_impression_for_attributes(attributes, context)
|
|
34
|
+
# Retrieve base properties for the event
|
|
35
|
+
properties = NetworkUtil.get_events_base_properties(
|
|
36
|
+
EventEnum::SYNC_VISITOR_PROP,
|
|
37
|
+
URI.encode_www_form_component(context.user_agent),
|
|
38
|
+
context.ip_address
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Construct payload data for multiple attributes
|
|
42
|
+
payload = NetworkUtil.get_attribute_payload_data(
|
|
43
|
+
EventEnum::SYNC_VISITOR_PROP,
|
|
44
|
+
attributes,
|
|
45
|
+
context
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# check if batching is enabled
|
|
49
|
+
if BatchEventsQueue.instance
|
|
50
|
+
# add the payload to the batch events queue
|
|
51
|
+
BatchEventsQueue.instance.enqueue(payload)
|
|
52
|
+
else
|
|
53
|
+
# Send the constructed payload via POST request
|
|
54
|
+
NetworkUtil.send_post_api_request(properties, payload)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Copyright 2024-2026 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 '../enums/api_enum'
|
|
16
|
+
require_relative '../models/settings/settings_model'
|
|
17
|
+
require_relative '../models/user/context_model'
|
|
18
|
+
require_relative '../services/hooks_service'
|
|
19
|
+
require_relative '../utils/function_util'
|
|
20
|
+
require_relative '../utils/network_util'
|
|
21
|
+
require_relative '../services/logger_service'
|
|
22
|
+
require_relative '../services/batch_event_queue'
|
|
23
|
+
|
|
24
|
+
class TrackApi
|
|
25
|
+
# Tracks an event with given properties and context.
|
|
26
|
+
# @param settings [SettingsModel] Configuration settings.
|
|
27
|
+
# @param event_name [String] Name of the event to track.
|
|
28
|
+
# @param context [ContextModel] Contextual information like user details.
|
|
29
|
+
# @param event_properties [Hash] Properties associated with the event.
|
|
30
|
+
# @param hooks_service [HooksService] Manager for handling hooks and callbacks.
|
|
31
|
+
# @return [Hash] A hash indicating success or failure of event tracking.
|
|
32
|
+
def track(settings, event_name, context, event_properties, hooks_service)
|
|
33
|
+
if does_event_belong_to_any_feature(event_name, settings)
|
|
34
|
+
# Create an impression for the track event
|
|
35
|
+
create_impression_for_track(event_name, context, event_properties)
|
|
36
|
+
|
|
37
|
+
# Set and execute integration callback for the track event
|
|
38
|
+
hooks_service.set({ event_name: event_name, api: ApiEnum::TRACK_EVENT })
|
|
39
|
+
hooks_service.execute(hooks_service.get)
|
|
40
|
+
|
|
41
|
+
return { event_name: true }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Log an error if the event does not exist
|
|
45
|
+
LoggerService.log(LogLevelEnum::ERROR, "EVENT_NOT_FOUND", { eventName: event_name, an: ApiEnum::TRACK_EVENT, sId: context.get_session_id, uuid: context.get_uuid})
|
|
46
|
+
|
|
47
|
+
{ event_name: false }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# Creates an impression for a track event and sends it via a POST API request.
|
|
53
|
+
# @param event_name [String] Name of the event to track.
|
|
54
|
+
# @param context [ContextModel] User details.
|
|
55
|
+
# @param event_properties [Hash] Properties associated with the event.
|
|
56
|
+
def create_impression_for_track(event_name, context, event_properties)
|
|
57
|
+
# Get base properties for the event
|
|
58
|
+
properties = NetworkUtil.get_events_base_properties(
|
|
59
|
+
event_name,
|
|
60
|
+
URI.encode_www_form_component(context.user_agent),
|
|
61
|
+
context.ip_address
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Prepare the payload for the track goal
|
|
65
|
+
payload = NetworkUtil.get_track_goal_payload_data(
|
|
66
|
+
event_name,
|
|
67
|
+
event_properties,
|
|
68
|
+
context
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# check if batching is enabled
|
|
72
|
+
if BatchEventsQueue.instance
|
|
73
|
+
# add the payload to the batch events queue
|
|
74
|
+
BatchEventsQueue.instance.enqueue(payload)
|
|
75
|
+
else
|
|
76
|
+
# Send the prepared payload via POST API request
|
|
77
|
+
NetworkUtil.send_post_api_request(properties, payload)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|