private List <Variation> GetWhiteListedVariationsList(string apiName, string userId, BucketedCampaign campaign, string campaignKey, Dictionary <string, dynamic> customVariables, Dictionary <string, dynamic> variationTargetingVariables) { List <Variation> result = new List <Variation> { }; foreach (var variation in campaign.Variations.All()) { string status = Constants.SegmentationStatus.FAILED; string segmentationType = Constants.SegmentationType.WHITELISTING; if (variation.Segments.Count == 0) { LogDebugMessage.SkippingSegmentation(typeof(IVWOClient).FullName, userId, campaignKey, apiName, variation.Name); } else { if (this._segmentEvaluator.evaluate(userId, campaignKey, segmentationType, variation.Segments, variationTargetingVariables)) { status = Constants.SegmentationStatus.PASSED; result.Add(variation); } LogDebugMessage.SegmentationStatus(typeof(IVWOClient).FullName, userId, campaignKey, apiName, variation.Name, status); } } return(result); }
public double ComputeBucketValue(string userId, double maxVal, double multiplier, out double hashValue) { byte[] hash = this._murmur32.ComputeHash(Encoding.UTF8.GetBytes(userId)); hashValue = BitConverter.ToUInt32(hash, 0); var bucketValue = Compute(hashValue, maxVal, multiplier); LogDebugMessage.UserHashBucketValue(file, userId, hashValue, bucketValue); return(bucketValue); }
/// <summary> /// Allocate variation by checking UserStorageService, Campaign Traffic Allocation and compute UserHash to check variation allocation by bucketing. /// </summary> /// <param name="campaignKey"></param> /// <param name="userId"></param> /// <param name="apiName"></param> /// <param name="campaign"></param> /// <param name="customVariables"></param> /// <param name="variationTargetingVariables"></param> /// <returns> /// If Variation is allocated, returns UserAssignedInfo with valid details, else return Empty UserAssignedInfo. /// </returns> private UserAllocationInfo AllocateVariation(string campaignKey, string userId, BucketedCampaign campaign, Dictionary <string, dynamic> customVariables, Dictionary <string, dynamic> variationTargetingVariables, string apiName = null) { Variation TargettedVariation = this.FindTargetedVariation(apiName, campaign, campaignKey, userId, customVariables, variationTargetingVariables); if (TargettedVariation != null) { return(new UserAllocationInfo(TargettedVariation, campaign)); } UserStorageMap userStorageMap = this._userStorageService.GetUserMap(campaignKey, userId); BucketedCampaign selectedCampaign = this._campaignAllocator.Allocate(this._settings, userStorageMap, campaignKey, userId, apiName); if (userStorageMap != null && userStorageMap.VariationName != null) { Variation variation = this._variationAllocator.GetSavedVariation(campaign, userStorageMap.VariationName.ToString()); return(new UserAllocationInfo(variation, selectedCampaign)); } if (selectedCampaign != null) { Variation variation = this._variationAllocator.Allocate(userStorageMap, selectedCampaign, userId); if (variation != null) { if (campaign.Segments.Count > 0) { string segmentationType = Constants.SegmentationType.PRE_SEGMENTATION; if (customVariables == null) { LogInfoMessage.NoCustomVariables(typeof(IVWOClient).FullName, userId, campaignKey, apiName); customVariables = new Dictionary <string, dynamic>(); } if (variationTargetingVariables == null) { variationTargetingVariables = new Dictionary <string, dynamic>(); } if (!this._segmentEvaluator.evaluate(userId, campaignKey, segmentationType, campaign.Segments, customVariables)) { return(new UserAllocationInfo()); } } else { LogInfoMessage.SkippingPreSegmentation(typeof(IVWOClient).FullName, userId, campaignKey, apiName); } LogInfoMessage.VariationAllocated(file, userId, campaignKey, variation.Name); LogDebugMessage.GotVariationForUser(file, userId, campaignKey, variation.Name, nameof(AllocateVariation)); this._userStorageService.SetUserMap(userId, selectedCampaign.Key, variation.Name); return(new UserAllocationInfo(variation, selectedCampaign)); } } LogInfoMessage.NoVariationAllocated(file, userId, campaignKey); return(new UserAllocationInfo()); }
public static string Compute(long accountId, string userId) { var accountIdAsString = accountId.ToString(); var vwoNamespaceGuid = Identifiable.NamedGuid.Compute(NamedGuidAlgorithm.SHA1, UrlNamespace, VWO_NAMESPACE_URL); var accountIdGuid = NamedGuid.Compute(NamedGuidAlgorithm.SHA1, vwoNamespaceGuid, accountIdAsString); var userIdGuid = NamedGuid.Compute(NamedGuidAlgorithm.SHA1, accountIdGuid, userId); var uuid = userIdGuid.ToString(GUID_FORMAT).ToUpper(); LogDebugMessage.UuidForUser(file, userId, accountId, uuid); return(uuid); }
//Event Batching internal static ApiRequest EventBatching(long accountId, bool isDevelopmentMode, string sdkKey) { string queryParams = GetQueryParamertersForEventBatching(accountId); var trackUserRequest = new ApiRequest(Method.POST, isDevelopmentMode) { Uri = new Uri($"{Host}/{Verb}/{BatchEventVerb}?{queryParams}&{GetSdkKeyQuery(sdkKey)}"), logUri = new Uri($"{Host}/{Verb}/{BatchEventVerb}?{queryParams}"), }; LogDebugMessage.ImpressionForBatchEvent(file, queryParams); return(trackUserRequest); }
internal static ApiRequest TrackGoal(int accountId, int campaignId, int variationId, string userId, int goalId, string revenueValue, bool isDevelopmentMode) { string queryParams = GetQueryParamertersForTrackGoal(accountId, campaignId, variationId, userId, goalId, revenueValue); var trackUserRequest = new ApiRequest(Method.GET, isDevelopmentMode) { Uri = new Uri($"{Host}/{Verb}/{TrackGoalVerb}?{queryParams}"), }; trackUserRequest.WithCaller(AppContext.ApiCaller); LogDebugMessage.ImpressionForTrackGoal(file, queryParams); return(trackUserRequest); }
internal static ApiRequest PushTags(AccountSettings settings, string tagKey, string tagValue, string userId, bool isDevelopmentMode) { string queryParams = GetQueryParamertersForPushTag(settings, tagKey, tagValue, userId); var trackPushRequest = new ApiRequest(Method.GET, isDevelopmentMode) { Uri = new Uri($"{Host}/{Verb}/{PushTagsVerb}?{queryParams}"), }; trackPushRequest.WithCaller(AppContext.ApiCaller); LogDebugMessage.ImpressionForPushTag(file, queryParams); return(trackPushRequest); }
internal static ApiRequest TrackUser(long accountId, int campaignId, int variationId, string userId, bool isDevelopmentMode, string sdkKey) { string queryParams = GetQueryParamertersForTrackUser(accountId, campaignId, variationId, userId); var trackUserRequest = new ApiRequest(Method.GET, isDevelopmentMode) { Uri = new Uri($"{Host}/{Verb}/{TrackUserVerb}?{queryParams}&{GetSdkKeyQuery(sdkKey)}"), logUri = new Uri($"{Host}/{Verb}/{TrackUserVerb}?{queryParams}"), }; trackUserRequest.WithCaller(AppContext.ApiCaller); LogDebugMessage.ImpressionForTrackUser(file, queryParams); return(trackUserRequest); }
/// <summary> /// Allocate Variation by checking previously assigned variation if userStorageMap is provided, else by computing User Hash and matching it in bucket for eligible variation. /// </summary> /// <param name="userStorageMap"></param> /// <param name="campaign"></param> /// <param name="userId"></param> /// <returns></returns> public Variation Allocate(UserStorageMap userStorageMap, BucketedCampaign campaign, string userId) { if (campaign == null) { return(null); } if (userStorageMap == null) { double maxVal = Constants.Variation.MAX_TRAFFIC_VALUE; double multiplier = maxVal / campaign.PercentTraffic / 100; ///This is to evenly spread all user among variations. var bucketValue = this._userHasher.ComputeBucketValue(userId, maxVal, multiplier, out double hashValue); var selectedVariation = campaign.Variations.Find(bucketValue); LogDebugMessage.VariationHashBucketValue(file, userId, campaign.Key, campaign.PercentTraffic, hashValue, bucketValue); return(selectedVariation); } return(campaign.Variations.Find(userStorageMap.VariationName, GetVariationName)); }
internal void SetUserMap(string userId, string campaignKey, string variationName, string goalIdentifier = null) { if (this._userStorageService == null) { LogDebugMessage.NoUserStorageServiceSet(file); return; } try { this._userStorageService.Set(new UserStorageMap(userId, campaignKey, variationName, goalIdentifier)); LogInfoMessage.SavingDataUserStorageService(file, userId); return; } catch (Exception ex) { LogErrorMessage.SetUserStorageServiceFailed(file, userId); } }
/// <summary> /// Init variables in BatchEventQueue. /// </summary> /// <param name="batchEvents">BatchEventsData instance</param> /// <param name="apikey">VWO application apikey</param> /// <param name="accountId">VWO application accountId</param> /// <param name="isDevelopmentMode">isDevelopmentMode Boolean value specifying development mode is on ir off</param> internal BatchEventQueue(BatchEventData batchEvents, string apikey, int accountId, bool isDevelopmentMode) { if (batchEvents != null) { if (batchEvents.RequestTimeInterval != null) { if (batchEvents.RequestTimeInterval > 1) { this.requestTimeInterval = (int)batchEvents.RequestTimeInterval; } else { LogDebugMessage.RequestTimeIntervalOutOfBound(file, 1, requestTimeInterval); } } else { LogDebugMessage.RequestTimeIntervalOutOfBound(file, 1, requestTimeInterval); } if (batchEvents.EventsPerRequest != null) { if (batchEvents.EventsPerRequest > 0 && batchEvents.EventsPerRequest <= MAX_EVENTS_PER_REQUEST) { this.eventsPerRequest = Math.Min((int)batchEvents.EventsPerRequest, MAX_EVENTS_PER_REQUEST); } else { LogDebugMessage.EventsPerRequestOutOfBound(file, 1, MAX_EVENTS_PER_REQUEST, eventsPerRequest); } } else { LogDebugMessage.EventsPerRequestOutOfBound(file, 1, MAX_EVENTS_PER_REQUEST, eventsPerRequest); } if (batchEvents.FlushCallback != null) { this.flushCallback = batchEvents.FlushCallback; } } this.accountId = accountId; this.isDevelopmentMode = isDevelopmentMode; this.apikey = apikey; }
internal void SaveUserMap(string userId, string campaignTestKey, string variationName) { if (this._userProfileService == null) { LogDebugMessage.NoUserProfileServiceSave(file); return; } try { LogInfoMessage.SavingDataUserProfileService(file, userId); this._userProfileService.Save(new UserProfileMap(userId, campaignTestKey, variationName)); return; } catch (Exception ex) { LogErrorMessage.SaveUserProfileServiceFailed(file, userId); } }
/// <summary> /// If UserStorageService is provided, Calls Get for given UserId and validate the result. /// </summary> /// <param name="campaignKey"></param> /// <param name="userId"></param> /// <param name="userStorageData"></param> /// <returns> /// Returns userStorageMap if validation is success, else null. /// </returns> internal UserStorageMap GetUserMap(string campaignKey, string userId, Dictionary <string, dynamic> userStorageData = null) { if (this._userStorageService == null) { LogDebugMessage.NoUserStorageServiceGet(file); return(null); } UserStorageMap userMap = TryGetUserMap(userId, campaignKey, userStorageData); if (userMap == null || string.IsNullOrEmpty(userMap.CampaignKey) || string.IsNullOrEmpty(userMap.VariationName) || string.IsNullOrEmpty(userMap.UserId) || string.Equals(userMap.UserId, userId) == false || string.Equals(userMap.CampaignKey, campaignKey) == false) { LogDebugMessage.NoStoredVariation(file, userId, campaignKey); return(null); } LogInfoMessage.GotStoredVariation(file, userMap.VariationName, campaignKey, userId); return(userMap); }
/// <summary> /// If UserProfileService is provided, Calls Lookup for given UserId and validate the result. /// </summary> /// <param name="campaignTestKey"></param> /// <param name="userId"></param> /// <returns> /// Returns userProfileMap if validation is success, else null. /// </returns> internal UserProfileMap GetUserMap(string campaignTestKey, string userId) { if (this._userProfileService == null) { LogDebugMessage.NoUserProfileServiceLookup(file); return(null); } UserProfileMap userMap = TryGetUserMap(userId, campaignTestKey); if (userMap == null || string.IsNullOrEmpty(userMap.CampaignTestKey) || string.IsNullOrEmpty(userMap.VariationName) || string.IsNullOrEmpty(userMap.UserId) || string.Equals(userMap.UserId, userId) == false || string.Equals(userMap.CampaignTestKey, campaignTestKey) == false) { LogDebugMessage.NoStoredVariation(file, userId, campaignTestKey); return(null); } LogInfoMessage.GotStoredVariation(file, userMap.VariationName, campaignTestKey, userId); LogDebugMessage.GettingStoredVariation(file, userId, campaignTestKey, userMap.VariationName); return(userMap); }
/// <summary> /// Flush the queue, clear timer and send POST network call to VWO servers. /// </summary> /// <param name="manual">manual Boolean specifying flush is triggered manual or not.</param> /// <returns>Boolean value specifying flush was successful or not.</returns> public bool flush(bool manual) { var batchMetadata = HttpRequestBuilder.GetJsonString(this.batchQueue); if (batchQueue.Count == 0) { LogDebugMessage.EventQueueEmpty(file); } if (manual) { if (batchQueue.Count > 0) { LogDebugMessage.BeforeFlushing(file, "manually", batchQueue.Count.ToString(), accountId.ToString(), "Timer will be cleared and registered again", batchMetadata); Task <bool> response = sendPostCall(); LogDebugMessage.AfterFlushing(file, "manually", batchQueue.Count.ToString(), batchMetadata); disposeData(); return(true); } clearRequestTimer(); return(true); } else { if (batchQueue.Count > 0 && !isBatchProcessing) { isBatchProcessing = true; LogDebugMessage.BeforeFlushing(file, "", batchQueue.Count.ToString(), accountId.ToString(), "", batchMetadata); Task <bool> response = sendPostCall(); LogDebugMessage.AfterFlushing(file, "", batchQueue.Count.ToString(), batchMetadata); disposeData(); return(true); } clearRequestTimer(); return(true); } }
/// <summary> /// Send Post network call to VWO servers. /// </summary> /// <returns>Boolean value specifying flush was successful or not.</returns> private async Task <bool> sendPostCall() { string PayLoad = HttpRequestBuilder.GetJsonString(this.batchQueue); var ApiRequest = ServerSideVerb.EventBatching(this.accountId, this.isDevelopmentMode, this.apikey); HttpClient httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Accept.Clear(); httpClient.DefaultRequestHeaders.Add("Authorization", this.apikey); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var data = new StringContent(PayLoad, Encoding.UTF8, "application/json"); try { HttpResponseMessage response = await httpClient.PostAsync(ApiRequest.Uri, data); response.EnsureSuccessStatusCode(); if (response.StatusCode == System.Net.HttpStatusCode.OK && response.StatusCode < System.Net.HttpStatusCode.Ambiguous) { if (flushCallback != null) { flushCallback.onFlush(null, PayLoad); } LogInfoMessage.ImpressionSuccess(file, ApiRequest.logUri?.ToString()); return(true); } else if (response.StatusCode == System.Net.HttpStatusCode.RequestEntityTooLarge) { if (flushCallback != null) { flushCallback.onFlush("Payload size too large", PayLoad); } LogDebugMessage.BatchEventLimitExceeded(file, ApiRequest.logUri?.ToString(), this.accountId.ToString(), eventsPerRequest.ToString()); LogErrorMessage.ImpressionFailed(file, ApiRequest.logUri?.ToString()); return(false); } else if (response.StatusCode == System.Net.HttpStatusCode.BadRequest) { if (flushCallback != null) { flushCallback.onFlush("Account id not found, no request app id found, or invalid API key", PayLoad); } LogErrorMessage.BulkNotProcessed(file); LogErrorMessage.ImpressionFailed(file, ApiRequest.logUri?.ToString()); return(false); } else { LogErrorMessage.BulkNotProcessed(file); LogErrorMessage.ImpressionFailed(file, ApiRequest.logUri?.ToString()); if (flushCallback != null) { flushCallback.onFlush("Invalid call", PayLoad); } return(false); } } catch (HttpRequestException ex) { LogErrorMessage.BulkNotProcessed(file); LogErrorMessage.UnableToDisplayHttpRequest(file, ex.StackTrace); if (flushCallback != null) { flushCallback.onFlush("HttpRequest Exception", PayLoad); } return(false); } }