diff --git a/Source/Adobe.Target.Client/Model/TargetDeliveryRequest.cs b/Source/Adobe.Target.Client/Model/TargetDeliveryRequest.cs index 7e93f26..c0d067c 100644 --- a/Source/Adobe.Target.Client/Model/TargetDeliveryRequest.cs +++ b/Source/Adobe.Target.Client/Model/TargetDeliveryRequest.cs @@ -14,6 +14,7 @@ namespace Adobe.Target.Client.Model using System.Collections.Generic; using System.Linq; using System.Net; + using System.Text; using Adobe.ExperienceCloud.Ecid; using Adobe.Target.Client.Util; using Adobe.Target.Delivery.Model; @@ -447,6 +448,20 @@ public TargetDeliveryRequest Build() return new TargetDeliveryRequest(this); } + private static string GetMboxNames(IEnumerable mboxes) + { + return string.Join( + string.Empty, + mboxes?.Select(mbox => mbox.Name) ?? Array.Empty()); + } + + private static string GetViewNames(IEnumerable views) + { + return string.Join( + string.Empty, + views?.Select(view => view.Name) ?? Array.Empty()); + } + private void SetTargetValues() { var targetCookie = this.Cookies[TargetConstants.MboxCookieName]?.Value; @@ -524,12 +539,26 @@ private void CreateAndSetAnalyticsValues() TrackingServer = this.TrackingServer, TrackingServerSecure = this.TrackingServerSecure, Logging = LoggingType.ServerSide, - SupplementalDataId = this.Visitor.GetSupplementalDataId(TargetConstants.DefaultSdidConsumerId), + SupplementalDataId = this.Visitor.GetSupplementalDataId(this.GetVisitorConsumerId()), }; this.ExperienceCloud.Analytics = analyticsRequest; } + private string GetVisitorConsumerId() + { + var consumerId = new StringBuilder(TargetConstants.SdkNameValue); + _ = consumerId.Append(this.DeliveryRequest.Execute?.PageLoad != null || + this.DeliveryRequest.Prefetch?.PageLoad != null + ? TargetConstants.DefaultSdidConsumerId : string.Empty); + _ = consumerId.Append(GetMboxNames(this.DeliveryRequest.Execute?.Mboxes)); + _ = consumerId.Append(GetMboxNames(this.DeliveryRequest.Prefetch?.Mboxes)); + _ = consumerId.Append(GetViewNames(this.DeliveryRequest.Prefetch?.Views)); + var consumerIdHash = HashUtils.SimpleHash(consumerId.ToString()); + + return Convert.ToBase64String(BitConverter.GetBytes(consumerIdHash)); + } + private void CreateAndSetAudienceManager() { if (this.ExperienceCloud.AudienceManager != null) diff --git a/Source/Adobe.Target.Client/Model/TargetDeliveryResponse.cs b/Source/Adobe.Target.Client/Model/TargetDeliveryResponse.cs index ded0d90..1ae3ea0 100644 --- a/Source/Adobe.Target.Client/Model/TargetDeliveryResponse.cs +++ b/Source/Adobe.Target.Client/Model/TargetDeliveryResponse.cs @@ -84,6 +84,22 @@ public IDictionary VisitorState } } + /// + /// Visitor State JSON string + /// + public string VisitorStateAsJson + { + get + { + if (this.Request.Visitor == null) + { + return string.Empty; + } + + return SerializationUtils.SerializeVisitorState(this.Request.Visitor.GetState()); + } + } + /// /// Gets Target cookies /// diff --git a/Source/Adobe.Target.Client/Util/AllocationUtils.cs b/Source/Adobe.Target.Client/Util/AllocationUtils.cs index eba684d..e0e2e73 100644 --- a/Source/Adobe.Target.Client/Util/AllocationUtils.cs +++ b/Source/Adobe.Target.Client/Util/AllocationUtils.cs @@ -29,7 +29,7 @@ private static string GetDeviceId(string clientId, string activityId, string vis private static double CalculateAllocation(string deviceId) { - var hashValue = MurmurHash3.HashUnencodedChars(deviceId); + var hashValue = HashUtils.HashUnencodedChars(deviceId); var hashFixedBucket = Math.Abs(hashValue) % TotalBuckets; var allocationValue = (float)hashFixedBucket / TotalBuckets * MaxPercentage; diff --git a/Source/Adobe.Target.Client/Util/MurmurHash3.cs b/Source/Adobe.Target.Client/Util/HashUtils.cs similarity index 81% rename from Source/Adobe.Target.Client/Util/MurmurHash3.cs rename to Source/Adobe.Target.Client/Util/HashUtils.cs index 1654829..62596d6 100644 --- a/Source/Adobe.Target.Client/Util/MurmurHash3.cs +++ b/Source/Adobe.Target.Client/Util/HashUtils.cs @@ -10,7 +10,9 @@ */ namespace Adobe.Target.Client.Util { - internal static class MurmurHash3 + using System; + + internal static class HashUtils { private const int Seed = 0; private const int CharBytes = 2; @@ -22,7 +24,7 @@ internal static class MurmurHash3 private const uint N = 0xe6546b64; /// - /// hashUnencodedChars() implementation ported from Guava + /// MurmurHash3 hashUnencodedChars() implementation ported from Guava /// /// Input /// Hashed output @@ -31,13 +33,25 @@ public static int HashUnencodedChars(string input) return unchecked((int)HashStringUnsigned(input)); } + public static int SimpleHash(string input) + { + uint result = 0; + + for (var i = 0; i < input.Length; i++) + { + result = ((result << 5) - result + input[i]) & uint.MaxValue; + } + + return unchecked((int)result); + } + private static uint HashStringUnsigned(string input) { uint h1 = Seed; - for (int i = 1; i < input.Length; i += 2) + for (var i = 1; i < input.Length; i += 2) { - uint k1 = unchecked((uint)input[i - 1] | (((uint)input[i]) << 16)); + var k1 = unchecked(input[i - 1] | ((uint)input[i] << 16)); k1 = MixK1(k1); h1 = MixH1(h1, k1); } diff --git a/Source/Adobe.Target.Client/Util/SerializationUtils.cs b/Source/Adobe.Target.Client/Util/SerializationUtils.cs index bb902ee..4008fed 100644 --- a/Source/Adobe.Target.Client/Util/SerializationUtils.cs +++ b/Source/Adobe.Target.Client/Util/SerializationUtils.cs @@ -10,6 +10,8 @@ */ namespace Adobe.Target.Client.Util { + using System.Collections.Generic; + using Adobe.ExperienceCloud.Ecid; using Newtonsoft.Json; internal static class SerializationUtils @@ -24,5 +26,17 @@ public static T ConvertObject(object from) var serialized = JsonConvert.SerializeObject(from); return JsonConvert.DeserializeObject(serialized); } + + public static string SerializeVisitorState(IDictionary state) + { + var settings = new JsonSerializerSettings + { + ContractResolver = new VisitorStateContractResolver(), + Formatting = Formatting.Indented, + NullValueHandling = NullValueHandling.Ignore, + }; + + return JsonConvert.SerializeObject(state, settings); + } } } diff --git a/Source/Adobe.Target.Client/Util/VisitorStateContractResolver.cs b/Source/Adobe.Target.Client/Util/VisitorStateContractResolver.cs new file mode 100644 index 0000000..6fa7f76 --- /dev/null +++ b/Source/Adobe.Target.Client/Util/VisitorStateContractResolver.cs @@ -0,0 +1,36 @@ +/* + * Copyright 2021 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +namespace Adobe.Target.Client.Util +{ + using Newtonsoft.Json.Serialization; + + internal sealed class VisitorStateContractResolver : DefaultContractResolver + { + private const string Sdid = "SupplementalDataId"; + private const string SdidReplace = "supplementalDataID"; + private const string OrgPostfix = "@AdobeOrg"; + + protected override string ResolvePropertyName(string name) + { + if (string.IsNullOrEmpty(name) || name.EndsWith(OrgPostfix)) + { + return name; + } + + if (name.StartsWith(Sdid)) + { + return name.Replace(Sdid, SdidReplace); + } + + return char.ToLowerInvariant(name[0]) + name.Substring(1); + } + } +} diff --git a/Tests/Adobe.Target.Client.Test/AllocationProviderShould.cs b/Tests/Adobe.Target.Client.Test/AllocationProviderShould.cs index 7a822b2..b6dbdc6 100644 --- a/Tests/Adobe.Target.Client.Test/AllocationProviderShould.cs +++ b/Tests/Adobe.Target.Client.Test/AllocationProviderShould.cs @@ -18,10 +18,10 @@ public class AllocationProviderShould [Fact] public void MurmurHash3_HashUnencodedChars() { - var hashed = MurmurHash3.HashUnencodedChars("someClientId.123456.tntId123.salty"); + var hashed = HashUtils.HashUnencodedChars("someClientId.123456.tntId123.salty"); Assert.Equal(-1846592194, hashed); - hashed = MurmurHash3.HashUnencodedChars("targettesting.125880.4c038b35f1b1453d80a3e7da8208c617.campaign"); + hashed = HashUtils.HashUnencodedChars("targettesting.125880.4c038b35f1b1453d80a3e7da8208c617.campaign"); Assert.Equal(-683299703, hashed); }