Five New Optimizely Certifications are Here! Validate your expertise and advance your career with our latest certification exams. Click here to find out more
Five New Optimizely Certifications are Here! Validate your expertise and advance your career with our latest certification exams. Click here to find out more
May I ask why you want to do this? Criterierias not already using IStateStorage, would not benefit from storing their state.
I managed to solve this now in a slightly different way:
So the requirement was to persist an audience match in a cookie. The configuration of the audience and the match-lifetime(how long the cookie persists the match) is configureable in the CMS. So in theory every existing audience is persistable. If it is a custom VisitorGroupRole checks in IsInVirtualRole if it's contained in the cookie and returns true then, otherwise the default implementation is used(so the optimizely logic to evaluate visitor-group matches). If the auzdience matches, the match is persisted in the cookie. For this i needed to implement a custom CookieBasedVisitorGroupRole class which will replace optimizely`s VisitorGroupRole(some boilerplate code above in my question).
Note: the implementation of IsInVirtualRole must be fully thread-safe since only one instance of the class is created and any role checks are made against the same instance.
Hello everyone,
the customer has the requirement to persist audience(visitorgroup) memberships of users in a cookie. I found out that it's better to just save in a cookie that an audience criterion matches, so not the whole audience. You can easily support that in your custom criteria's ICriterion.IsMatch-logic. But the challenge here is to extend the builtin criteria to (optionally) support persisting the match in a cookie.
Some of the builtin criteria already support that out of the box like the "Url criteria"-group, so basically all inheriting from UriSessionStartCriterionBase. Otherwise it would not be possible to for example evaluate the "Landing url" on the user's journey(saved in an "EPiStartUrlKey"-cookie). So my question is:
Is it possible to extend the ICriterionModel of the builtin audience criteria so that i can add a checkbox “Persist match“(or a "Cookie name" Textbox) and if checked(or given) the IsMatch result is saved in a cookie(or other IStateStorage) and evaluated first, so even if the criterion would not match anymore it returns true because it's in the cookie? The whole cookie logic is clear, the challenge here is just how to modify the cms behavior of the builtin criteria.
A workaround i would like to avoid is to replace all builtin- with custom criteria doing the same but can be persisted in a cookie.
I was playing around with using custom IVisitorGroupCriterionRepository and IVisitorGroupRepository but to no avail yet:
public static IServiceCollection AddCookieBasedVisitorGroups(this IServiceCollection services) { services.Intercept<IVisitorGroupCriterionRepository>((a, b) => new CookieBasedVisitorGroupCriteriaRepository(a, b)); services.Intercept<IVisitorGroupRepository>((a, b) => new CookieBasedVisitorGroupRepository(a, b)); return services; }
I cannot replace the existing criterion model with my new one and i doubt that this is the right approach anyway.
Edit:
I have now an idea how to solve this. I will configure if an audience(not the criterion) is persisted in a cookie in a file which contains these:
I will replace these optimizely services with my own, a lot of boilerplate:
public static IServiceCollection AddCookieBasedVisitorGroups(this IServiceCollection services) { services.Intercept<IVisitorGroupRoleRepository>((a, b) => { IVirtualRoleReplication virtualRoleReplication = a.GetRequiredService<IVirtualRoleReplication>(); ICriterionEvents criterionEvents = a.GetRequiredService<ICriterionEvents>(); ISynchronizedObjectInstanceCache cache = a.GetRequiredService<ISynchronizedObjectInstanceCache>(); IVisitorGroupRoleFactory visitorGroupRoleFactory = a.GetRequiredService<IVisitorGroupRoleFactory>(); IVisitorGroupStatisticsLogger visitorGroupStatisticsLogger = a.GetRequiredService<IVisitorGroupStatisticsLogger>(); IAggregatedPersonalizationEvaluator personalizationEvaluator = a.GetRequiredService<IAggregatedPersonalizationEvaluator>(); ICriterionFactory criterionFactory = a.GetRequiredService<ICriterionFactory>(); IHttpContextAccessor httpContextAccessor = a.GetRequiredService<IHttpContextAccessor>(); IVisitorGroupRepository visitorGroupRepository = a.GetRequiredService<IVisitorGroupRepository>(); return new CookieBasedVisitorGroupRoleRepository(virtualRoleReplication, criterionEvents, cache, visitorGroupRoleFactory, visitorGroupStatisticsLogger, personalizationEvaluator, criterionFactory, httpContextAccessor, visitorGroupRepository); }); services.Intercept<IVisitorGroupRoleFactory>((a, b) => { IVisitorGroupRepository visitorGroupRepository = a.GetRequiredService<IVisitorGroupRepository>(); IVisitorGroupStatisticsLogger visitorGroupStatisticsLogger = a.GetRequiredService<IVisitorGroupStatisticsLogger>(); ICriterionFactory criterionFactory = a.GetRequiredService<ICriterionFactory>(); IAggregatedPersonalizationEvaluator personalizationEvaluator = a.GetRequiredService<IAggregatedPersonalizationEvaluator>(); IHttpContextAccessor httpContextAccessor = a.GetRequiredService<IHttpContextAccessor>(); return new CookieBasedVisitorGroupRoleFactory(visitorGroupRepository, visitorGroupStatisticsLogger, criterionFactory, personalizationEvaluator, httpContextAccessor); }); return services; }
CookieBasedVisitorGroupRoleRepository overrides just TryGetRole and inherits from the default VisitorGroupRoleRepository:
public class CookieBasedVisitorGroupRoleRepository( IVirtualRoleReplication virtualRoleReplication, ICriterionEvents criterionEvents, ISynchronizedObjectInstanceCache cache, IVisitorGroupRoleFactory roleFactory, IVisitorGroupStatisticsLogger visitorGroupStatisticsLogger, IAggregatedPersonalizationEvaluator personalizationEvaluator, ICriterionFactory criterionFactory, IHttpContextAccessor httpContextAccessor, IVisitorGroupRepository visitorGroupRepository) : VisitorGroupRoleRepository(virtualRoleReplication, criterionEvents, cache, roleFactory) { public override bool TryGetRole(string name, out VisitorGroupRole visitorGroupRole) { if (base.TryGetRole(name, out visitorGroupRole) && visitorGroupRole is not null) { visitorGroupRole = GetCookieBasedVisitorGroupRole(visitorGroupRole); return true; } return false; } private CookieBasedVisitorGroupRole GetCookieBasedVisitorGroupRole(VisitorGroupRole visitorGroupRole) { VisitorGroup? vg = visitorGroupRepository.Load(visitorGroupRole.ID); return new CookieBasedVisitorGroupRole(vg, visitorGroupRepository, visitorGroupStatisticsLogger, criterionFactory, personalizationEvaluator, httpContextAccessor); } }
The CookieBasedVisitorGroupRoleFactory is also necessary to return the same CookieBasedVisitorGroupRole in Create:
public class CookieBasedVisitorGroupRoleFactory( IVisitorGroupRepository visitorGroupRepository, IVisitorGroupStatisticsLogger visitorGroupStatisticsLogger, ICriterionFactory criterionFactory, IAggregatedPersonalizationEvaluator personalizationEvaluator, IHttpContextAccessor httpContextAccessor) : IVisitorGroupRoleFactory { public VisitorGroupRole Create(VisitorGroup visitorGroup) { return new CookieBasedVisitorGroupRole(visitorGroup, visitorGroupRepository, visitorGroupStatisticsLogger, criterionFactory, personalizationEvaluator, httpContextAccessor); } }
The VisitorGroupRole finally is where all the visitor-group magic of optimizely happens, so i need to replace it with CookieBasedVisitorGroupRole and override IsInVirtualRole:
public class CookieBasedVisitorGroupRole( VisitorGroup visitorGroup, IVisitorGroupRepository visitorGroupRepository, IVisitorGroupStatisticsLogger visitorGroupStatisticsLogger, ICriterionFactory criterionFactory, IAggregatedPersonalizationEvaluator personalizationEvaluator, IHttpContextAccessor httpContextAccessor) : VisitorGroupRole(visitorGroup, visitorGroupRepository, visitorGroupStatisticsLogger, criterionFactory, personalizationEvaluator, httpContextAccessor) { public override bool IsInVirtualRole(IPrincipal principal, object context) { if (IsVisitorGroupContainedInCookie()) { return true; } bool visitorGroupMatches = base.IsInVirtualRole(principal, context); if (visitorGroupMatches) { PersistInCookie(); } return visitorGroupMatches; } private bool IsVisitorGroupContainedInCookie() { VisitorGroup currentVisitorGroup = visitorGroup; // TODO return false; } private void PersistInCookie() { VisitorGroup currentVisitorGroup = visitorGroup; // TODO } }