xConnect: Rapid Interaction Spoofing and Contact Identification

Cover Image for xConnect: Rapid Interaction Spoofing and Contact Identification

When you're testing contact sessions and contact identification, you don't want to wait for sessions to end on their own; it's too slow. We are going to set up a handful of bookmarkable API GET endpoints so you can rapidly start a session, identify as a specific contact, viw the current session data, and end the session so that the data is written to the database immediately instead of having to wait.

Endpoints for Ending Sessions and Viewing Session Data

VisitController.cs

_25
using Sitecore.Analytics;
_25
using Sitecore.Diagnostics;
_25
using System.Web.Mvc;
_25
_25
namespace Client.Foundation.xConnectPoc.Controllers
_25
{
_25
public class VisitController : Controller
_25
{
_25
[HttpGet]
_25
public JsonResult Data()
_25
{
_25
var currentTracker = Tracker.Current;
_25
var data = currentTracker.Interaction.ToVisitData();
_25
return Json(data, JsonRequestBehavior.AllowGet);
_25
}
_25
_25
[HttpGet]
_25
public JsonResult End()
_25
{
_25
Session.Abandon();
_25
Log.Info("Closing xDB session", this);
_25
return Json(new { Message = "xDB Session Closed" }, JsonRequestBehavior.AllowGet);
_25
}
_25
}
_25
}

InitializeRoutes.cs

_24
using System.Web.Mvc;
_24
using System.Web.Routing;
_24
using Sitecore.Pipelines;
_24
_24
namespace Client.Foundation.xConnectPoc.Pipelines.Initialize
_24
{
_24
public class InitializeRoutes : Sitecore.Mvc.Pipelines.Loader.InitializeRoutes
_24
{
_24
public override void Process(PipelineArgs args)
_24
{
_24
RouteTable.Routes.MapRoute(
_24
"VisitData", // Route name
_24
"api/visit/data",
_24
new { controller = "Visit", action = "Data" },
_24
new[] { "Client.Foundation.xConnectPoc.Controllers" });
_24
_24
RouteTable.Routes.MapRoute(
_24
"VisitEnd",
_24
"api/visit/end",
_24
new { controller = "Visit", action = "End" },
_24
new[] { "Client.Foundation.xConnectPoc.Controllers" });
_24
}
_24
}
_24
}

Patch.config

_10
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
_10
<sitecore>
_10
<pipelines>
_10
<initialize>
_10
<!-- Only enable this in test environments -->
_10
<processor type="Client.Foundation.xConnectPoc.Pipelines.Initialize.InitializeRoutes, Client.Foundation.xConnectPoc" patch:before="processor[@type='Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc']"/>
_10
</initialize>
_10
</pipelines>
_10
</sitecore>
_10
</configuration>

Now we need to set up the logic to get or set the contact and populate its fields. Getting or setting the contact also implies that we need to support contact identification. Modify the code below to suit your needs.

SpoofVisit.cs

_156
using Sitecore.Analytics;
_156
using Sitecore.Analytics.Model;
_156
using Sitecore.Analytics.Model.Entities;
_156
using Sitecore.Analytics.Pipelines.CreateVisits;
_156
using Sitecore.XConnect;
_156
using Sitecore.XConnect.Client;
_156
using Sitecore.XConnect.Collection.Model;
_156
using System.Linq;
_156
using System.Net;
_156
_156
namespace Client.Foundation.xConnectPoc.Pipelines.CreateVisit
_156
{
_156
public class SpoofVisit : CreateVisitProcessor
_156
{
_156
public override void Process(CreateVisitArgs args)
_156
{
_156
// We're using an email in the query string as the primary identification mechanism
_156
var email = args.Request.Params["email"];
_156
if (string.IsNullOrWhitespace(email))
_156
{
_156
return;
_156
}
_156
_156
var firstName = args.Request.Params["firstName"];
_156
var lastName = args.Request.Params["lastName"];
_156
_156
// IP address info, if you want
_156
if (!string.IsNullOrWhitespace(args.Request.Params["geoIp"]))
_156
{
_156
var geoip = args.Request.Params["geoIp"];
_156
var ipAddress = IPAddress.Parse(geoIp);
_156
args.Interaction.Ip = ipAddress.GetAddressBytes();
_156
}
_156
_156
args.Session.IdentifyAs("Email", email);
_156
_156
var manager = Sitecore.Configuration.Factory.CreateObject("tracking/contactManager", true) as Sitecore.Analytics.Tracking.ContactManager;
_156
if (manager == null)
_156
{
_156
return;
_156
}
_156
_156
if (Tracker.Current.Contact.IsNew)
_156
{
_156
// Save contact to xConnect; at this point, a contact has an anonymous
_156
// TRACKER IDENTIFIER, which follows a specific format. Do not use the contactId overload
_156
// and make sure you set the ContactSaveMode as demonstrated
_156
Tracker.Current.Contact.ContactSaveMode = ContactSaveMode.AlwaysSave;
_156
manager.SaveContactToCollectionDb(Tracker.Current.Contact);
_156
_156
// Now that the contact is saved, you can retrieve it using the tracker identifier
_156
var trackerIdentifier = new IdentifiedContactReference(Sitecore.Analytics.XConnect.DataAccess.Constants.IdentifierSource, Tracker.Current.Contact.ContactId.ToString("N"));
_156
_156
// Get contact from xConnect, update and save the facet
_156
using (XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
_156
{
_156
try
_156
{
_156
var contact = client.Get(trackerIdentifier, new Sitecore.XConnect.ContactExpandOptions());
_156
_156
if (contact == null)
_156
{
_156
// Handle it
_156
}
_156
_156
client.SetFacet(contact, PersonalInformation.DefaultFacetKey, new PersonalInformation()
_156
{
_156
FirstName = firstName,
_156
LastName = lastName
_156
});
_156
_156
var emails = new EmailAddressList(new EmailAddress(email, true), "Home");
_156
client.SetFacet(contact, emails);
_156
_156
client.Submit();
_156
_156
// Remove contact data from shared session state - contact will be re-loaded
_156
// during subsequent request with updated facets
_156
manager.RemoveFromSession(Tracker.Current.Contact.ContactId);
_156
Tracker.Current.Session.Contact = manager.LoadContact(Tracker.Current.Contact.ContactId);
_156
}
_156
catch (XdbExecutionException ex)
_156
{
_156
// Manage conflicts / exceptions
_156
Sitecore.Diagnostics.Log.Error(ex.Message, ex, this);
_156
}
_156
}
_156
}
_156
else
_156
{
_156
var anyIdentifier = Tracker.Current.Contact.Identifiers.FirstOrDefault();
_156
_156
// Get contact from xConnect, update and save the facet
_156
using (XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
_156
{
_156
try
_156
{
_156
var contact = client.Get(new IdentifiedContactReference(anyIdentifier.Source, anyIdentifier.Identifier), new ContactExpandOptions(PersonalInformation.DefaultFacetKey));
_156
_156
if (contact == null)
_156
{
_156
// Handle it
_156
}
_156
_156
if (contact.Personal() != null)
_156
{
_156
if (!string.IsNullOrEmpty(firstName))
_156
contact.Personal().FirstName = firstName;
_156
if (!string.IsNullOrEmpty(lastName))
_156
contact.Personal().LastName = lastName;
_156
_156
client.SetFacet(contact, PersonalInformation.DefaultFacetKey, contact.Personal());
_156
}
_156
else
_156
{
_156
client.SetFacet(contact, PersonalInformation.DefaultFacetKey, new PersonalInformation()
_156
{
_156
FirstName = firstName,
_156
LastName = lastName
_156
});
_156
}
_156
_156
var emailAddressList = contact.GetFacet<EmailAddressList>(EmailAddressList.DefaultFacetKey);
_156
if (emailAddressList != null)
_156
{
_156
// Change facet properties
_156
emailAddressList.PreferredEmail = new EmailAddress(email, true);
_156
emailAddressList.PreferredKey = "Home";
_156
_156
// Set the updated facet
_156
client.SetFacet(contact, EmailAddressList.DefaultFacetKey, emailAddressList);
_156
}
_156
else
_156
{
_156
// Facet is new
_156
var emails = new EmailAddressList(new EmailAddress(email, true), "Home");
_156
client.SetFacet(contact, EmailAddressList.DefaultFacetKey, emails);
_156
}
_156
_156
client.Submit();
_156
_156
// Remove contact data from shared session state - contact will be re-loaded
_156
// during subsequent request with updated facets
_156
manager.RemoveFromSession(Tracker.Current.Contact.ContactId);
_156
Tracker.Current.Session.Contact = manager.LoadContact(Tracker.Current.Contact.ContactId);
_156
}
_156
catch (XdbExecutionException ex)
_156
{
_156
// Manage conflicts / exceptions
_156
Sitecore.Diagnostics.Log.Error(ex.Message, ex, this);
_156
}
_156
}
_156
}
_156
}
_156
}
_156
}

Patch.config

_10
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
_10
<sitecore>
_10
<pipelines>
_10
<createVisit>
_10
<!-- Make sure to disable this in PROD -->
_10
<processor type="Client.Foundation.xConnectPoc.Pipelines.CreateVisit.SpoofVisit, Client.Foundation.xConnectPoc" patch:after="processor[@type='Sitecore.Analytics.Pipelines.CreateVisits.XForwardedFor, Sitecore.Analytics']" />
_10
</createVisit>
_10
</pipelines>
_10
</sitecore>
_10
</configuration>

I also found a browser extension for analytics testing that may be worth trying out.

Keep on building,

Marcel


More Stories