Skip to content

Commit

Permalink
Fix TNT-41586 VisitorState contains unexpected values (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
XDex authored Aug 2, 2021
1 parent 252f59d commit 8f7a0c3
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 8 deletions.
31 changes: 30 additions & 1 deletion Source/Adobe.Target.Client/Model/TargetDeliveryRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -447,6 +448,20 @@ public TargetDeliveryRequest Build()
return new TargetDeliveryRequest(this);
}

private static string GetMboxNames(IEnumerable<MboxRequest> mboxes)
{
return string.Join(
string.Empty,
mboxes?.Select(mbox => mbox.Name) ?? Array.Empty<string>());
}

private static string GetViewNames(IEnumerable<ViewRequest> views)
{
return string.Join(
string.Empty,
views?.Select(view => view.Name) ?? Array.Empty<string>());
}

private void SetTargetValues()
{
var targetCookie = this.Cookies[TargetConstants.MboxCookieName]?.Value;
Expand Down Expand Up @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions Source/Adobe.Target.Client/Model/TargetDeliveryResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ public IDictionary<string, VisitorState> VisitorState
}
}

/// <summary>
/// Visitor State JSON string
/// </summary>
public string VisitorStateAsJson
{
get
{
if (this.Request.Visitor == null)
{
return string.Empty;
}

return SerializationUtils.SerializeVisitorState(this.Request.Visitor.GetState());
}
}

/// <summary>
/// Gets Target cookies
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion Source/Adobe.Target.Client/Util/AllocationUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -22,7 +24,7 @@ internal static class MurmurHash3
private const uint N = 0xe6546b64;

/// <summary>
/// hashUnencodedChars() implementation ported from Guava
/// MurmurHash3 hashUnencodedChars() implementation ported from Guava
/// </summary>
/// <param name="input">Input</param>
/// <returns>Hashed output</returns>
Expand All @@ -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);
}
Expand Down
14 changes: 14 additions & 0 deletions Source/Adobe.Target.Client/Util/SerializationUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*/
namespace Adobe.Target.Client.Util
{
using System.Collections.Generic;
using Adobe.ExperienceCloud.Ecid;
using Newtonsoft.Json;

internal static class SerializationUtils
Expand All @@ -24,5 +26,17 @@ public static T ConvertObject<T>(object from)
var serialized = JsonConvert.SerializeObject(from);
return JsonConvert.DeserializeObject<T>(serialized);
}

public static string SerializeVisitorState(IDictionary<string, VisitorState> state)
{
var settings = new JsonSerializerSettings
{
ContractResolver = new VisitorStateContractResolver(),
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
};

return JsonConvert.SerializeObject(state, settings);
}
}
}
36 changes: 36 additions & 0 deletions Source/Adobe.Target.Client/Util/VisitorStateContractResolver.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
4 changes: 2 additions & 2 deletions Tests/Adobe.Target.Client.Test/AllocationProviderShould.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down

0 comments on commit 8f7a0c3

Please sign in to comment.