From 217f3e021765e4ed23a5fb626630c054ec20d9af Mon Sep 17 00:00:00 2001 From: Abbas-khaliq Date: Thu, 23 Dec 2021 14:34:38 +0530 Subject: [PATCH] feat(sdk): send POST call for events-enabled acc and multiple custom dimensions push support --- CHANGELOG.md | 39 ++++++ lib/vwo.rb | 124 +++++++++++------ lib/vwo/constants.rb | 8 +- lib/vwo/enums.rb | 4 + lib/vwo/schemas/settings_file.rb | 3 + lib/vwo/services/event_dispatcher.rb | 34 +++++ lib/vwo/utils/function.rb | 5 + lib/vwo/utils/impression.rb | 180 +++++++++++++++++++++++++ lib/vwo/utils/request.rb | 14 ++ tests/test_impression.rb | 191 +++++++++++++++++++++++++++ tests/test_vwo.rb | 61 +++++++++ 11 files changed, 622 insertions(+), 41 deletions(-) create mode 100644 tests/test_impression.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index eaf6aae..8065cb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,45 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.25.0] - 2021-12-23 + +### Added + +- Support for pushing multiple custom dimensions at once. Earlier, you had to call push API multiple times for tracking multiple custom dimensions as follows: + + ```ruby + vwo_client_instance.push('browser', 'chrome', user_id) + vwo_client_instance.push('price', '20', user_id) + ``` + + Now, you can pass an hash + + ```ruby + custom_dimension_map = { + browser: 'chrome', + price: '20' + } + + vwo_client_instance.push(custom_dimension_map, user_id) + ``` + + Multiple asynchronous tracking calls would be initiated in this case. + +### Changed + +- If Events Architecture is enabled for your VWO account, all the tracking calls being initiated from SDK would now be `POST` instead of `GET` and there would be single endpoint i.e. `/events/t`. This is done in order to bring events support and building advanced capabilities in future. + +- For events architecture accounts, tracking same goal across multiple campaigns will not send multiple tracking calls. Instead, one single `POST` call would be made to track the same goal across multiple different campaigns running on the same environment. + +- Multiple custom dimension can be pushed via `push` API. For events architecture enabled account, only one single asynchronous call would be made to track multiple custom dimensions. +```ruby + custom_dimension_map = { + browser: 'chrome', + price: '20' + } + vwo_client_instance.push(custom_dimension_map, user_id) +``` + ## [1.24.1] - 2021-12-09 ### Changed diff --git a/lib/vwo.rb b/lib/vwo.rb index e01e448..209f2f9 100644 --- a/lib/vwo.rb +++ b/lib/vwo.rb @@ -370,6 +370,10 @@ def activate(campaign_key, user_id, options = {}) user_id ) @batch_events_queue.enqueue(impression) + elsif is_event_arch_enabled + properties = get_events_base_properties(@settings_file, EventEnum::VWO_VARIATION_SHOWN, @usage_stats.usage_stats) + payload = get_track_user_payload_data(@settings_file, user_id, EventEnum::VWO_VARIATION_SHOWN, campaign['id'], variation['id']) + @event_dispatcher.dispatch_event_arch_post(properties, payload) else # Variation found, dispatch it to server impression = create_impression( @@ -592,6 +596,8 @@ def track(campaign_key, user_id, goal_identifier, options = {}) return nil end + metric_map = {} + revenue_props = [] result = {} campaigns.each do |campaign| begin @@ -687,6 +693,11 @@ def track(campaign_key, user_id, goal_identifier, options = {}) revenue_value ) @batch_events_queue.enqueue(impression) + elsif is_event_arch_enabled + metric_map[campaign['id']] = goal['id'] + if goal['type'] == GoalTypes::REVENUE && !(revenue_props.include? goal['revenueProp']) + revenue_props << goal['revenueProp'] + end else impression = create_impression( @settings_file, @@ -737,6 +748,12 @@ def track(campaign_key, user_id, goal_identifier, options = {}) end end + if is_event_arch_enabled + properties = get_events_base_properties(@settings_file, goal_identifier) + payload = get_track_goal_payload_data(@settings_file, user_id, goal_identifier, revenue_value, metric_map, revenue_props) + @event_dispatcher.dispatch_event_arch_post(properties, payload) + end + if result.length() == 0 return nil end @@ -861,6 +878,10 @@ def feature_enabled?(campaign_key, user_id, options = {}) user_id ) @batch_events_queue.enqueue(impression) + elsif is_event_arch_enabled + properties = get_events_base_properties(@settings_file, EventEnum::VWO_VARIATION_SHOWN, @usage_stats.usage_stats) + payload = get_track_user_payload_data(@settings_file, user_id, EventEnum::VWO_VARIATION_SHOWN, campaign['id'], variation['id']) + @event_dispatcher.dispatch_event_arch_post(properties, payload) else impression = create_impression( @settings_file, @@ -1110,12 +1131,12 @@ def get_feature_variable_value(campaign_key, variable_key, user_id, options = {} # This API method: Makes a call to our server to store the tag_values # 1. Validates the arguments being passed # 2. Send a call to our server - # @param[String] :tag_key key name of the tag - # @param[String] :tag_value Value of the tag + # @param[String|Hash] :tag_key key name of the tag OR tagKey/tagValue pair(custom dimension map) + # @param[String] :tag_value Value of the tag OR userId if TagKey is hash # @param[String] :user_id ID of the user for which value should be stored # @return true if call is made successfully, else false - def push(tag_key, tag_value, user_id) + def push(tag_key, tag_value, user_id = nil) unless @is_instance_valid @logger.log( LogLevelEnum::ERROR, @@ -1128,7 +1149,16 @@ def push(tag_key, tag_value, user_id) return end - unless valid_string?(tag_key) && valid_string?(tag_value) && valid_string?(user_id) + # Argument reshuffling. + custom_dimension_map = {} + if user_id.nil? || tag_key.is_a?(Hash) + custom_dimension_map = convert_to_symbol_hash(tag_key) + user_id = tag_value + else + custom_dimension_map[tag_key.to_sym] = tag_value + end + + unless (valid_string?(tag_key) || valid_hash?(tag_key)) && valid_string?(tag_value) && valid_string?(user_id) @logger.log( LogLevelEnum::ERROR, format( @@ -1140,51 +1170,61 @@ def push(tag_key, tag_value, user_id) return false end - if tag_key.length > PushApi::TAG_KEY_LENGTH - @logger.log( - LogLevelEnum::ERROR, - format( - LogMessageEnum::ErrorMessages::TAG_KEY_LENGTH_EXCEEDED, - file: FILE, - user_id: user_id, - tag_key: tag_key, - api_name: ApiMethods::PUSH + custom_dimension_map.each do |tag_key, tag_value| + if tag_key.length > PushApi::TAG_KEY_LENGTH + @logger.log( + LogLevelEnum::ERROR, + format( + LogMessageEnum::ErrorMessages::TAG_KEY_LENGTH_EXCEEDED, + file: FILE, + user_id: user_id, + tag_key: tag_key, + api_name: ApiMethods::PUSH + ) ) - ) - return false - end + return false + end - if tag_value.length > PushApi::TAG_VALUE_LENGTH - @logger.log( - LogLevelEnum::ERROR, - format( - LogMessageEnum::ErrorMessages::TAG_VALUE_LENGTH_EXCEEDED, - file: FILE, - user_id: user_id, - tag_value: tag_value, - api_name: ApiMethods::PUSH + if tag_value.length > PushApi::TAG_VALUE_LENGTH + @logger.log( + LogLevelEnum::ERROR, + format( + LogMessageEnum::ErrorMessages::TAG_VALUE_LENGTH_EXCEEDED, + file: FILE, + user_id: user_id, + tag_value: tag_value, + api_name: ApiMethods::PUSH + ) ) - ) - return false + return false + end end if defined?(@batch_events) - impression = get_batch_event_url_params(@settings_file, tag_key, tag_value, user_id) - @batch_events_queue.enqueue(impression) + custom_dimension_map.each do |tag_key, tag_value| + impression = get_batch_event_url_params(@settings_file, tag_key, tag_value, user_id) + @batch_events_queue.enqueue(impression) + end + elsif is_event_arch_enabled + properties = get_events_base_properties(@settings_file, EventEnum::VWO_SYNC_VISITOR_PROP) + payload = get_push_payload_data(@settings_file, user_id, EventEnum::VWO_SYNC_VISITOR_PROP, custom_dimension_map) + @event_dispatcher.dispatch_event_arch_post(properties, payload) else - impression = get_url_params(@settings_file, tag_key, tag_value, user_id, @sdk_key) - @event_dispatcher.dispatch(impression) + custom_dimension_map.each do |tag_key, tag_value| + impression = get_url_params(@settings_file, tag_key, tag_value, user_id, @sdk_key) + @event_dispatcher.dispatch(impression) - @logger.log( - LogLevelEnum::INFO, - format( - LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_PUSH_API, - file: FILE, - u: impression['u'], - account_id: impression['account_id'], - tags: impression['tags'] + @logger.log( + LogLevelEnum::INFO, + format( + LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_PUSH_API, + file: FILE, + u: impression['u'], + account_id: impression['account_id'], + tags: impression['tags'] + ) ) - ) + end end true rescue StandardError => e @@ -1253,4 +1293,8 @@ def get_goal_type_to_track(options) end goal_type_to_track end + + def is_event_arch_enabled + return @settings_file.key?('isEventArchEnabled') && @settings_file['isEventArchEnabled'] + end end diff --git a/lib/vwo/constants.rb b/lib/vwo/constants.rb index 6f9cea6..b98a827 100644 --- a/lib/vwo/constants.rb +++ b/lib/vwo/constants.rb @@ -27,7 +27,7 @@ module CONSTANTS HTTP_PROTOCOL = 'http://' HTTPS_PROTOCOL = 'https://' URL_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8' - SDK_VERSION = '1.24.1' + SDK_VERSION = '1.25.0' SDK_NAME = 'ruby' VWO_DELIMITER = '_vwo_' MAX_EVENTS_PER_REQUEST = 5000 @@ -44,6 +44,7 @@ module ENDPOINTS TRACK_GOAL = '/server-side/track-goal' PUSH = '/server-side/push' BATCH_EVENTS = '/server-side/batch-events' + EVENTS = '/events/t' end module EVENTS @@ -113,5 +114,10 @@ module CampaignTypes FEATURE_TEST = 'FEATURE_TEST' FEATURE_ROLLOUT = 'FEATURE_ROLLOUT' end + + module EventEnum + VWO_VARIATION_SHOWN = 'vwo_variationShown' + VWO_SYNC_VISITOR_PROP = 'vwo_syncVisitorProp' + end end end diff --git a/lib/vwo/enums.rb b/lib/vwo/enums.rb index 0be1267..92721d2 100644 --- a/lib/vwo/enums.rb +++ b/lib/vwo/enums.rb @@ -114,6 +114,9 @@ module DebugMessages BEFORE_FLUSHING = '(%s): Flushing events queue %s having %s events %s queue summary: %s' EVENT_BATCHING_INSUFFICIENT = '(%s): %s not provided, assigning default value' GOT_ELIGIBLE_CAMPAIGNS = "(%s): Campaigns:%s are eligible, %s are ineligible from the Group:%s for the User ID:%s" + IMPRESSION_FOR_EVENT_ARCH_TRACK_USER = "(%s): impression built for vwo_variationShown event for account ID:%s, user ID:%s, and campaign ID:%s" + IMPRESSION_FOR_EVENT_ARCH_TRACK_GOAL = "(%s): impression built for %s event for account ID:%s, user ID:%s, and campaign ID:%s" + IMPRESSION_FOR_EVENT_ARCH_PUSH = "(%s): impression built for visitor property:%s for account ID:%s and user ID:%s" end # Info Messages @@ -128,6 +131,7 @@ module InfoMessages AUDIENCE_CONDITION_NOT_MET = '(%s): userId:%s does not become part of campaign because of not meeting audience conditions' GOT_VARIATION_FOR_USER = '(%s): userId:%s for campaign:%s got variationName:%s' USER_GOT_NO_VARIATION = '(%s): userId:%s for campaign:%s did not allot any variation' + IMPRESSION_SUCCESS_FOR_EVENT_ARCH = '(%s): Impression for %s - %s was successfully received by VWO for account ID:%s' IMPRESSION_SUCCESS = '(%s): Impression event - %s was successfully received by VWO having main keys: accountId:%s campaignId:%s and variationId:%s' MAIN_KEYS_FOR_IMPRESSION = '(%s): Having main keys: accountId:%s campaignId:%s and variationId:%s}' MAIN_KEYS_FOR_PUSH_API = '(%s): Having main keys: accountId:%s u:%s and tags:%s}' diff --git a/lib/vwo/schemas/settings_file.rb b/lib/vwo/schemas/settings_file.rb index 23b78d3..f2410b8 100644 --- a/lib/vwo/schemas/settings_file.rb +++ b/lib/vwo/schemas/settings_file.rb @@ -26,6 +26,9 @@ module Schema accountId: { type: %w[number string] }, + isEventArchEnabled: { + type: ['boolean'] + }, campaigns: { if: { type: 'array' diff --git a/lib/vwo/services/event_dispatcher.rb b/lib/vwo/services/event_dispatcher.rb index 95bec9e..a05f59b 100644 --- a/lib/vwo/services/event_dispatcher.rb +++ b/lib/vwo/services/event_dispatcher.rb @@ -15,11 +15,13 @@ require_relative '../logger' require_relative '../enums' require_relative '../utils/request' +require_relative '../constants' class VWO module Services class EventDispatcher include VWO::Enums + include VWO::CONSTANTS EXCLUDE_KEYS = ['url'].freeze @@ -63,6 +65,38 @@ def dispatch(impression) ) false end + + def dispatch_event_arch_post(params, post_data) + return true if @is_development_mode + + url = HTTPS_PROTOCOL + ENDPOINTS::BASE_URL + ENDPOINTS::EVENTS + resp = VWO::Utils::Request.event_post(url, params, post_data, SDK_NAME) + if resp.code == '200' + @logger.log( + LogLevelEnum::INFO, + format( + LogMessageEnum::InfoMessages::IMPRESSION_SUCCESS_FOR_EVENT_ARCH, + file: FileNameEnum::EventDispatcher, + event: 'visitor property:' + JSON.generate(post_data[:d][:visitor][:props]), + url: url, + a: params[:a] + ) + ) + true + else + @logger.log( + LogLevelEnum::ERROR, + format(LogMessageEnum::ErrorMessages::IMPRESSION_FAILED, file: FileNameEnum::EventDispatcher, end_point: url) + ) + false + end + rescue StandardError + @logger.log( + LogLevelEnum::ERROR, + format(LogMessageEnum::ErrorMessages::IMPRESSION_FAILED, file: FileNameEnum::EventDispatcher, end_point: url) + ) + false + end end end end diff --git a/lib/vwo/utils/function.rb b/lib/vwo/utils/function.rb index 0fc1735..f65ac97 100644 --- a/lib/vwo/utils/function.rb +++ b/lib/vwo/utils/function.rb @@ -33,6 +33,11 @@ def get_current_unix_timestamp Time.now.to_i end + # @return[Integer] + def get_current_unix_timestamp_in_millis + (Time.now.to_f * 1000).to_i + end + # @return[any, any] def get_key_value(obj) [obj.keys[0], obj.values[0]] diff --git a/lib/vwo/utils/impression.rb b/lib/vwo/utils/impression.rb index ba8f5c1..83b56b1 100644 --- a/lib/vwo/utils/impression.rb +++ b/lib/vwo/utils/impression.rb @@ -164,6 +164,186 @@ def create_bulk_event_impression(settings_file, campaign_id, variation_id, user_ end impression end + + # Builds generic properties for different tracking calls required by VWO servers. + # + # @param[Hash] :settings_file + # @param[String] :sdk_key + # @param[String] :event_name + # @param[Hash] :usage_stats + # @return[Hash] :properties + # + def get_events_base_properties(settings_file, event_name, usage_stats = {}) + properties = { + en: event_name, + a: settings_file['accountId'], + env: settings_file['sdkKey'], + eTime: get_current_unix_timestamp_in_millis, + random: get_random_number, + p: "FS" + } + + if event_name == EventEnum::VWO_VARIATION_SHOWN + properties = properties.merge(usage_stats) + end + properties + end + + # Builds generic payload required by all the different tracking calls. + # + # @param[Hash] :settings_file + # @param[String] :user_id + # @param[String] :event_name + # @param[Hash] :usage_stats + # @return[Hash] :properties + # + def get_event_base_payload(settings_file, user_id, event_name, usage_stats = {}) + uuid = generator_for(user_id, (settings_file['accountId'])) + sdk_key = settings_file['sdkKey'] + + props = { + sdkName: SDK_NAME, + sdkVersion: SDK_VERSION, + '$visitor': { + props: { + vwo_fs_environment: sdk_key + } + } + } + + # if usage_stats + # props = props.merge(usage_stats) + # end + + properties = { + d: { + msgId: uuid + '_' + Time.now.to_i.to_s, + visId: uuid, + sessionId: Time.now.to_i, + event: { + props: props, + name: event_name, + time: get_current_unix_timestamp_in_millis + }, + visitor: { + props: { + vwo_fs_environment: sdk_key + } + } + } + } + + properties + end + + # Builds payload to track the visitor. + # + # @param[Hash] :settings_file + # @param[String] :user_id + # @param[String] :event_name + # @param[Integer] :campaign_id + # @param[Integer] :variation_id + # @param[Hash] :usage_stats + # @return[Hash] :properties + # + def get_track_user_payload_data(settings_file, user_id, event_name, campaign_id, variation_id, usage_stats = {}) + properties = get_event_base_payload(settings_file, user_id, event_name) + properties[:d][:event][:props][:id] = campaign_id + properties[:d][:event][:props][:variation] = variation_id + + #this is currently required by data-layer team, we can make changes on DACDN and remove it from here + properties[:d][:event][:props][:isFirst] = 1 + + logger = VWO::Logger.get_instance + logger.log( + LogLevelEnum::DEBUG, + format( + LogMessageEnum::DebugMessages::IMPRESSION_FOR_EVENT_ARCH_TRACK_USER, + file: FileNameEnum::ImpressionUtil, + a: settings_file['accountId'], + u: user_id, + c: campaign_id.to_s + ) + ) + properties + end + + # Builds payload to track the Goal. + # + # @param[Hash] :settings_file + # @param[String] :user_id + # @param[String] :event_name + # @param[Integer] :revenue_value + # @param[Hash] :metric_map + # @param[Array] :revenue_props + # + # @return[Hash] :properties + # + def get_track_goal_payload_data(settings_file, user_id, event_name, revenue_value, metric_map, revenue_props = []) + properties = get_event_base_payload(settings_file, user_id, event_name) + + logger = VWO::Logger.get_instance + metric = {} + metric_map.each do |campaign_id, goal_id| + metric[('id_' + campaign_id.to_s).to_sym] = ['g_' + goal_id.to_s] + logger.log( + LogLevelEnum::DEBUG, + format( + LogMessageEnum::DebugMessages::IMPRESSION_FOR_EVENT_ARCH_TRACK_GOAL, + file: FileNameEnum::ImpressionUtil, + goal_identifier: event_name, + a: settings_file['accountId'], + u: user_id, + c: campaign_id + ) + ) + end + + properties[:d][:event][:props][:vwoMeta] = { + metric: metric + } + + if revenue_props.length() != 0 && revenue_value + revenue_props.each do |revenue_prop| + properties[:d][:event][:props][:vwoMeta][revenue_prop.to_sym] = revenue_value + end + end + + properties[:d][:event][:props][:isCustomEvent] = true + properties + end + + # Builds payload to appply post segmentation on VWO campaign reports. + # + # @param[Hash] :settings_file + # @param[String] :user_id + # @param[String] :event_name + # @param[Hash] :custom_dimension_map + # + # @return[Hash] :properties + # + def get_push_payload_data(settings_file, user_id, event_name, custom_dimension_map = {}) + properties = get_event_base_payload(settings_file, user_id, event_name) + properties[:d][:event][:props][:isCustomEvent] = true + + custom_dimension_map.each do |tag_key, tag_value| + properties[:d][:event][:props][('$visitor'.to_sym)][:props][tag_key] = tag_value + properties[:d][:visitor][:props][tag_key] = tag_value + end + + logger = VWO::Logger.get_instance + logger.log( + LogLevelEnum::DEBUG, + format( + LogMessageEnum::DebugMessages::IMPRESSION_FOR_EVENT_ARCH_PUSH, + file: FileNameEnum::ImpressionUtil, + a: settings_file['accountId'], + u: user_id, + property: JSON.generate(custom_dimension_map) + ) + ) + properties + end end end end diff --git a/lib/vwo/utils/request.rb b/lib/vwo/utils/request.rb index 566df76..49d7db3 100644 --- a/lib/vwo/utils/request.rb +++ b/lib/vwo/utils/request.rb @@ -38,6 +38,20 @@ def self.post(url, params, post_data) response = http.post(uri, post_data.to_json, headers) response end + + def self.event_post(url, params, post_data, user_agent_value) + uri = URI.parse(url) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + uri.query = URI.encode_www_form(params) + headers = { + 'User-Agent' => user_agent_value, + 'Content-Type' =>'application/json', + 'Accept'=>'application/json' + } + response = http.post(uri, post_data.to_json, headers) + response + end end end end diff --git a/tests/test_impression.rb b/tests/test_impression.rb new file mode 100644 index 0000000..507c10d --- /dev/null +++ b/tests/test_impression.rb @@ -0,0 +1,191 @@ +# Copyright 2019-2021 Wingify Software Pvt. Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'json' +require_relative '../lib/vwo' +require_relative '../lib/vwo/utils/impression' +require_relative '../lib/vwo/enums' +require 'test/unit' +require 'mocha/test_unit' + +class ImpressionTest < Test::Unit::TestCase + include VWO::Utils::Impression + include VWO::Enums + EVENT_ARCH_QUERY_PARAMS = ['a', 'en', 'eTime', 'random', 'env', 'p'] + + def test_build_event_arch_payload_for_visitor + account_id = 1 + sdk_key = '12345' + config = {"accountId" => account_id, "sdkKey" => sdk_key} + query_params = get_events_base_properties(config, EventEnum::VWO_VARIATION_SHOWN) + properties = get_track_user_payload_data(config, 'Ashley', EventEnum::VWO_VARIATION_SHOWN, 20, 3) + + expected_properties = { + d: { + msgId: "string", + visId: "string", + sessionId: 123, + event: { + props: { + sdkName: "string", + sdkVersion: "string", + id: 12, + isFirst: 1233, + variation: 2, + '$visitor': { + props: { + vwo_fs_environment: "string" + } + }, + }, + name: "string", + time: 12345 + }, + visitor: { + props: { + vwo_fs_environment: "string" + } + } + } + } + + is_valid = check_all_properties_present(query_params, EVENT_ARCH_QUERY_PARAMS) + assert_equal(true, is_valid) + is_valid = check_all_properties_present_and_their_types(properties, expected_properties) + assert_equal(true, is_valid) + end + + def test_build_event_arch_payload_for_goal + account_id = 1 + sdk_key = '12345' + config = {"accountId" => account_id, "sdkKey" => sdk_key} + goal_identifier = 'goalIdentifier' + metric_map = { + "1": 10, + "2": 20, + "5": 30 + } + dummy_revenue_property = ['dummyRevenueProperty1', 'dummyRevenueProperty2']; + query_params = get_events_base_properties(config, goal_identifier) + properties = get_track_goal_payload_data(config, 'Ashley', goal_identifier, 12, metric_map, dummy_revenue_property) + + expected_properties = { + d: { + msgId: "string", + visId: "string", + sessionId: 123, + event: { + props: { + sdkName: "string", + sdkVersion: "string", + vwoMeta: { + metric: { + id_1: ["g_10"], + id_2: ["g_20"], + id_5: ["g_30"] + }, + dummyRevenueProperty1: 12, + dummyRevenueProperty2: 12 + }, + isCustomEvent: true, + '$visitor': { + props: { + vwo_fs_environment: "string" + } + }, + }, + name: "string", + time: 12345 + }, + visitor: { + props: { + vwo_fs_environment: "string" + } + } + } + } + + is_valid = check_all_properties_present(query_params, EVENT_ARCH_QUERY_PARAMS) + assert_equal(true, is_valid) + is_valid = check_all_properties_present_and_their_types(properties, expected_properties) + assert_equal(true, is_valid) + end + + def test_build_event_arch_payload_for_push + account_id = 1 + sdk_key = '12345' + config = {"accountId" => account_id, "sdkKey" => sdk_key} + query_params = get_events_base_properties(config, EventEnum::VWO_SYNC_VISITOR_PROP) + properties = get_push_payload_data(config, 'Ashley', EventEnum::VWO_SYNC_VISITOR_PROP, { tagKey1: "tagValue1", tagKey2: 'tagValue2'}) + + expected_properties = { + d: { + msgId: "string", + visId: "string", + sessionId: 123, + event: { + props: { + sdkName: "string", + sdkVersion: "string", + isCustomEvent: true, + '$visitor': { + props: { + vwo_fs_environment: "string", + tagKey1: "tagValue1", + tagKey2: 'tagValue2' + } + }, + }, + name: "string", + time: 12345 + }, + visitor: { + props: { + vwo_fs_environment: "string", + tagKey1: "tagValue1", + tagKey2: 'tagValue2' + } + } + } + } + + is_valid = check_all_properties_present(query_params, EVENT_ARCH_QUERY_PARAMS) + assert_equal(true, is_valid) + is_valid = check_all_properties_present_and_their_types(properties, expected_properties) + assert_equal(true, is_valid) + end + + def check_all_properties_present(properties, expected_properties) + expected_properties.each do |field| + unless properties.key? (field.to_sym) + return false + end + end + true + end + + def check_all_properties_present_and_their_types(properties, expected_properties) + expected_properties.each do |key, value| + if !(properties.key? key) || (properties[key]).class != (expected_properties[key]).class + return false + elsif (properties[key]).is_a?(Hash) + is_valid = check_all_properties_present_and_their_types(properties[key], expected_properties[key]) + unless is_valid + return false + end + end + end + true + end +end diff --git a/tests/test_vwo.rb b/tests/test_vwo.rb index d4e3033..fff89bf 100644 --- a/tests/test_vwo.rb +++ b/tests/test_vwo.rb @@ -1531,6 +1531,67 @@ def test_fr_whitelisting_not_passed_and_Traffic_10 end end + def test_activate_with_event_arch + set_up('AB_T_100_W_50_50') + @vwo.get_settings['isEventArchEnabled'] = true + USER_EXPECTATIONS[@campaign_key].each do |test| + assert_equal(@vwo.activate(@campaign_key, test['user']), test['variation']) + end + end + + def test_feature_enabled_with_event_arch + set_up('FR_T_100_W_100') + @vwo.get_settings['isEventArchEnabled'] = true + + USER_EXPECTATIONS['T_100_W_10_20_30_40'].each do |test| + assert_equal(@vwo.feature_enabled?('FR_T_100_W_100', test['user']), !test['variation'].nil?) + end + end + + def test_track_with_event_arch + set_up('AB_T_100_W_50_50') + @vwo.get_settings['isEventArchEnabled'] = true + @vwo.get_settings['campaigns'][0]['goals'][0]['revenueProp'] = 'dummyRevenueProperty' + USER_EXPECTATIONS[@campaign_key].each do |test| + result = @vwo.track(@campaign_key, test['user'], @goal_identifier, {revenue_value: '23.3'}) + assert_equal(result, {@campaign_key => !test['variation'].nil?}) + end + end + + def test_push_true_with_event_arch + vwo_instance = VWO.new(60781, 'ea87170ad94079aa190bc7c9b85d26fb', nil, nil, true, JSON.generate(SETTINGS_FILE['DUMMY_SETTINGS_FILE']), { log_level: Logger::DEBUG }) + vwo_instance.get_settings['isEventArchEnabled'] = true + assert_equal(true, vwo_instance.push('browser', 'chrome', '12345')) + end + + def test_push_int_value_false_with_event_arch + vwo_instance = VWO.new(60781, 'ea87170ad94079aa190bc7c9b85d26fb', nil, nil, true, JSON.generate(SETTINGS_FILE['DUMMY_SETTINGS_FILE']), { log_level: Logger::DEBUG }) + vwo_instance.get_settings['isEventArchEnabled'] = true + assert_equal(false, vwo_instance.push('browser', 1, '12345')) + end + + def test_push_true_with_two_arg_and_with_event_arch + vwo_instance = VWO.new(60781, 'ea87170ad94079aa190bc7c9b85d26fb', nil, nil, true, JSON.generate(SETTINGS_FILE['DUMMY_SETTINGS_FILE']), { log_level: Logger::DEBUG }) + vwo_instance.get_settings['isEventArchEnabled'] = true + assert_equal(true, vwo_instance.push({'browser' => 'chrome'}, '12345')) + end + + def test_push_true_with_two_arg + vwo_instance = VWO.new(60781, 'ea87170ad94079aa190bc7c9b85d26fb', nil, nil, true, JSON.generate(SETTINGS_FILE['DUMMY_SETTINGS_FILE']), { log_level: Logger::DEBUG }) + assert_equal(true, vwo_instance.push({'browser' => 'chrome'}, '12345')) + end + + def test_push_int_value_false_with_two_arg_and_with_event_arch + vwo_instance = VWO.new(60781, 'ea87170ad94079aa190bc7c9b85d26fb', nil, nil, true, JSON.generate(SETTINGS_FILE['DUMMY_SETTINGS_FILE']), { log_level: Logger::DEBUG }) + vwo_instance.get_settings['isEventArchEnabled'] = true + assert_equal(false, vwo_instance.push({'browser' => 1}, '12345')) + end + + def test_push_int_value_false_with_two_arg + vwo_instance = VWO.new(60781, 'ea87170ad94079aa190bc7c9b85d26fb', nil, nil, true, JSON.generate(SETTINGS_FILE['DUMMY_SETTINGS_FILE']), { log_level: Logger::DEBUG }) + assert_equal(false, vwo_instance.push({'browser' => 1}, '12345')) + end + def test_get_variation_as_user_hash_passes_whitelisting vwo_instance = VWO.new(1, 'someuniquestuff1234567', nil, nil, true, JSON.generate(SETTINGS_FILE['AB_T_100_W_25_25_25_25']))