Tuesday, July 31, 2018

Experience Profile Anonymous, Unknown and Known contacts

When you first get started with Sitecore's experience profile the reporting for contacts can cause a little confusion. There are 3 terms that are thrown around, 1) Anonymous 2) Unknown 3) Known. When you read the docs they can bleed into each other a little.

First, have a read through the Sitecore tracking documentation to get a feel for what Sitecore is trying to do.

There are a couple key things here to first understand:

  1. Unless you call "IdentifyAs()" for request the contact is always anonymous. 
  2. Tracking of anonymous contacts is off by default. 
  3. Even if you call "IdentifyAs()" if you don't set facet values for the contact (like first name and email) the contact will still show up in your experience profile as "unknown" (because it has no facet data to display). 

Enabled Anonymous contacts

Notice in the picture I have two contacts marked in a red box. Those are my "known" contacts that I called "IdentifyAs" on. I know they say "Unknown" but we will get back to that.

The other records show "Anonymous". By default, none of these records will show. Tracking these can lead to data bloat. So if you are going to enable this make sure you have a plan for how to make the data meaningful for you still.

To enable this there are a couple of things to do. The steps are defined in this stack exchange thread answer, but I will list them here again just in case (The thread is about creating a known contact with WFFM but you don't need to get caught up in that).

  • Update IndexerSettings.xml file within [site_prefix].xconnect\App_data\jobs\continuous\IndexWorker\App_data\Config\Sitecore\SearchIndexer to change IndexAnonymousContactData to true
  • Update IndexerSettings.xml within [site_prefix].xconnect\App_data\config\sitecore\SearchIndexer to change IndexAnonymousContactData to true
  • Open command prompt in admin mode and navigate to [site_prefix].xconnect\App_data\jobs\continuous\IndexWorker and execute XConnectSearchIndexer -rr or XConnectSearchIndexer -requestrebuild
  • Rebuild your indexes and rebuild link databases, both from Sitecore control panel

The key here is to execute all those steps. Change the config in both places and do all the rebuilds. Once you do that you can reload this page and see the anonymous records. I struggled with this at first because I was only changing one config location and doing just the XConnectSearchIndexer -rr command. Once I did all those, anonymous records showed up. There is no need to rebuild the reporting database as some threads out there kind of point to.

You can also set the configuration settings to false and do your rebuilds to remove the anonymous records.

Create "Known" contacts

The two records that are highlighted in the above image are my known contacts. "But they say unknown." Yes, that is true. This is because all I did for those contacts was call "IdentifyAs()". So they have a tracking identifier linked to them now but no other data. So the reporting screen has nothing to show in the name or email column.

When you click on a record that says "unknown", in the profiles details page the header says "Anonymous" which can be confusing. Even though the heading says Anonymous this is a known contact. 

I removed all the anonymous contacts and then visited the site again. This time though I ran the documented code for adding a facet to a contact. Then on my visit, I called "IdentifyAs" and identified the request using the identifier that was set up for the contact I created. When you do this you can see I now have a new contact record with real information in the name and email field. The facet I added is the PersonalInfo facet which is one of the OTB facets with Sitecore. You will notice now the name and email fields are populated and if you click on Luke Skywalker, the profiles detail screen no longer says "Anonymous"!
So while the profile search screen may be returning data that says "unknown" these are NOT unknown contacts. They are known contacts with some unknown data fields. There really are just two types of contacts in Sitecore. Known and Anonymous, but a known contact can sometimes display "known" and "anonymous" even though it is known. 

If you happen to be struggling with the "unknown" header in the picture below, read Sitecore xDB - Custom Eras in Experience Profile Timeline

Friday, July 13, 2018

Experience Profile Anonymize or Right to be Forgotten

Experience Profile Screen
Experience Profile
This threw me off a bit at first as the action that is being taken is not very clear. When you open a profile record in experience profile and click on actions, one of the options is "Anonymize". If you click on this action you are sent back to the search screen and that contact you were just on is gone.

At this point, you might be able to figure out what happened. At first when just playing around it is a little concern what happened. The red "X" helps here as well, because what you are doing is executing the "right to be forgotten" or "Anonymize" command on this contact. That means their data (their PII data) is removed from the database. Hence, when you go back to the profile search screen you can't find them anymore. Beware though, if you click on it, it is done. No warning here folks.

Thursday, July 12, 2018

Back in the Sitecore world again!!

I have recently been able to get back into the Sitecore world and I am excited! I have been living in the Tridion world for a few years and it has been painful. With that being said it is not like Sitecore does not have it's pain points as well. Here are some I have run into as I start playing with version 9.

Keep your license file name "license.xml"

Using SIF to install locally only works with a license file named "license.xml". I had a temp license for awhile so in the file name I put the expiration date. I updated the install powershell script for SIF with the new license file name (yes in both places) and run the install. The install was fine until it tried to start the indexer window service. That step failed as the service would not start. When I looked at the logs the service said it was still looking for a file name "license.xml" and it was missing. Sure enough the license file I referenced was not copied over. I have not found the root cause of this yet, I just know changing it back to "license.xml" resolved the issue.

The prefix must be unique for all installs

When you set up your install.ps1 script for SIF you have to set the prefix for the install. I have installed a couple different instances to play with different things. Some of these had the same "demo" prefix. This will cause somethings (mainly the installed Window Services) to overlap and screw things up. So always make sure you have a unique prefix for all your instances. 

Uninstalling, while manual, is not too painful

The above issue lead me into figuring the uninstall process out. While there is a nice script to install everything there is not a script to uninstall. There is a nice thread on StackExchange on doing this. How to Uninstall an instance created using the Sitecore Install Framework.

Access to the registry key 'Global' is denied.

This one was a bit hidden and you will not even know it is there unless you check your logs. Here is a nice blog with the fix. 

Saturday, January 7, 2017

Windows Workflow Unit Testing

I know people have very mixed opinions about Windows Workflow and, to be honest, so do I. Really I am not even sure if it has much of a future given the little attention Microsoft has given it. However, despite all that and rather your like it or not there are times when you may use it and want to unit test it. The question is how? Well there are not a lot of options but there is one, that for me, has proven valuable.

People tend to use Windows Workflow in a few different ways, so first let me explain how I have use it most. I have never really used it where I programmatically created and instantiate of my own workflow. For me it has pretty much all been using the Windows Workflow designer and using IIS as my workflow host. Then inside those XAML workflows I have custom activities I create and need to test. Do to this I have found one tool that does this pretty well and pretty easy.

Microsoft Activities Unit Testing

It is an old framework but it still gets the job done. There is not a real Dependency injection framework for Windows Workflow activities but with this you can basically use the service locator pattern to do unit testing. You could get fancy with and use the metadata model to store a DI container like Castle Windsor but so far, for me, I have found that activities normally have discreet enough unit of works that I don't need anymore. The CodePlex page has a good how to get setup page. It also has a good how to use the framework page.

What is nice about the framework is it comes with an easy way to get your activity hosted as a Windows Workflow activity with standard in and out arguments so it really behaves just like a Workflow Activity. There are a couple key aspects to getting this setup.

Getting the host setup is easy:
public void SumAddsTwoNumbers()
    // Arrange
    var activity = new Sum();
    var host = WorkflowInvokerTest.Create(activity);

    // InArguments is a dynamic object of type Microsoft.Activities.WorkflowArguments
    host.InArguments.Num1 = 1;
    host.InArguments.Num2 = 2;

        // Act

        // Assert
        // Note: The host automatically captures the out arguments
        host.AssertOutArgument.AreEqual("Result", 3);
        // Note: The host automatically captures tracking

Notice in the above code all you are doing is creating an instance of your normal activity, then using the framework to create a host for it. You can then just setup your InArgs (notice these are dynamic so don't expect intellisense here).

Ok that is great, but what about my dependencies? Here is the magic for that.

In your activity you need to setup a new method override.
protected override void CacheMetadata(CodeActivityMetadata metadata)
    metadata.AddDefaultExtensionProvider<YourInterface>(() => Your code to create the instance);

Adding the default extension provider allows your code later (by the below line of code) to get the instance it should use. If nothing else is provided it, well, uses the default that is setup.

this.unitOfWork = context.GetExtension<IUnitOfWork>();

For my code I am assigning whatever extension is setup to my unitOfWork field. This lets me use that object through the rest of my code.

Now you need to get it setup for your unit test.
this.unitOfWork = Mock.Of<IUnitOfWork>(m => m.MytRepository == Mock.Of<IMyRepository>());

this.workflowHost.Extensions.Add(() => this.unitOfWork);

All I need to do is create my mocked object (I used Moq). Then register that mock as a workflowhost extension. Now when I run this code as part of my unit test it will see my registered moq as the extension for that interface and use it instead of the default. But when the code runs in production it will not see any registered extension so it will use the registered default. That is it. I am sure there is even more you can do with this but that should get you up and running with Windows Workflow Activity unit testing.

Here is a look at how to do your assert on what is returned.
var outarg = this.workflowHost.OutArguments["Result"] as List<OrderFulfillment>;
outarg.Select(p => p).Count(p => p.CustomAttributes == null).Should().Be(0);

Here is another example of how someone used it.
Here is a Stackoverflow thread with some resources as well.

Saturday, December 3, 2016

Getting Started with .Net Core and Docker

I have been working to understand .Net Core and Docker as of late. It is cool technology but a bit of a paradigm shift. I have found a few good resources here and wanted to start recording them in case I needed to come back to them a long this journey.

First step to all of this is to install the .Net Core SDK.

Second step is to get Docker.

Once these are installed you have all the command line you need for awhile as you start moving down this road.

One of the first steps for me was getting my head around what .Net Core is and how it is different then the >net world I have been living in. Here is a great walk through that help me understand this.
First Steps Exploring NET Core and ASPNET Core

For me that finally connected the dots of what was happening now with the .net framework.

The next step was to understand what Docker is going to do for me. I had played and read about it but I needed to get hands-on. To do that I actually ran into another great article.

Deploy an ASP.NET Core Application on Linux with Docker

With those tutorials, I started to understand how to put .Net Core application together and how to take that application and put it into a Docker image and container.

Docker has some good docs to understand a little deeper. For me a little reading on containers and images went a long way. After that as I used the command line to look at images and containers I had locally for Docker as started wonder why and how to use repository names, image ids, and tags. Well a little reading through the Docker docs helped there too.

Friday, September 9, 2016

Powershell, XML and Visual Studio build event

Visual Studio provides a lot of capability, but sometimes you need a little Powershell. I needed to update an XML file in my solution based on the projects build configuration. If the configuration was "Release" setup a node in the XML to be value A. If the configuration was "Debug" setup a node in the XML to be value B. I actually did not realize how easy it is to work with XML within Powershell. I found a nice little start on StackOverflow.

My XML was more complex of course but it is still pretty easy to work with. Here is my post-build event.

"%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" -file ..\..\..\..\Scripts\ToggleXMLValues.ps1 $(ConfigurationName) $(TargetDir)

Then in a Powershell file I wrote some pretty simple code.

param ([string]$configName, [string]$outputDir)

$doc = [xml](Get-Content "$($outputDir)\config\myfile.xml")

if ($configName -eq "Release")
 $doc.Configuration.Global.Storages.Storage[0].Url = "URL 1"
 $doc.Configuration.Global.Storages.Storage[0].Url = "Url 2"


In my case I had an XML document that looked like this.

      <Storage Url="My URL"/>

With that little bit of code I can now toggle this value whenever I build.

Friday, July 1, 2016

Uniting Testing Expression Predicate with Moq

I recently was setting up a repository in a project with an interface on all repositories that took a predicate. As part of this I needed to mock out this call so I could unit test my code. The vast majority of samples out there for mocking an expression predicate just is It.IsAny<> which is not very helpful as it does not test anything other then verify it got a predicate. What if you actually want to test that you got a certain predicate though? It is actually pretty easy to do but not very straight forward.

Here is what you do for the It.IsAny<> approach in case someone is looking for that.

this.bindingRepository.Setup(c => c.Get(It.IsAny<Expression<Func<UserBinding, bool>>>())) 
.Returns(new List<UserBinding>() { defaultBinding }.AsQueryable());

This example just says to always return a collection of UserBindings that contain “defaultBinding” (which is an object I setup previously).

Here is what it looks like when you want to pass in an expression predicate that actually gets executed and only returns data if it matches something.

For my example I have a predicate that executes against an object collection of type “UserBinding.” The part to understand here is you are not passing to the Moq setup an expression. You are telling Moq to compile whatever expression it is given and see if that expression finds a match against the given object your provided. So in the example, does the expression passed in find a hit when executed against my “defaultBinding” object. If so return whatever data you want to return (in my case I am returning a collection of UserBindings as a queryable).

var defaultBinding = new UserBinding { Binding = new Binding { Name = "Default binding" }, Domain = "test.com" };
this.bindingRepository.Setup(c => c.Get(It.Is<Expression<Func<UserBinding, bool>>>(y => y.Compile()(defaultBinding))))
.Returns(new List<UserBinding>() { defaultBinding }.AsQueryable());

That is it. Now you can test expression predicates and return different out put based on the expression you are expecting to be passed in.