Public Relevance REST API
Introduction
Goal
Expose a visitor's personal data stored in the Relevance database by enabling the Public Relevance REST API.
Background
The Bloomreach Experience Manager Relevance Module provides an optional Public Relevance REST API which exposes a visitor's personal data stored in the Relevance database. This is one of the requirements to be GDPR-compliant (see GDPR Support more more information).
This page describes how to enable the Public Relevance REST API in your implemantion project and how to extend the API.
Enable Public Relevance REST API
After you have added the Relevance Module to your project, your project's site module will (through the pom dependency hippo-addon-targeting-dependencies-site) automatically pull in the following dependency:
<dependency> <groupId>com.onehippo.cms7</groupId> <artifactId>hippo-addon-targeting-site-rest</artifactId> </dependency>
This gives you everything you need to make the Public Relevance REST API available in your application / website. All you need to do is configure the hst:mount nodes below the hosts for which you want to expose the REST API. To be able to serve the personal data of a visitor to be able to be GDPR compliant, you have to configure the REST mounts below every host for which you target visitors: The reason for this is that the visitor cookie is tied to a host in your browser.
Example Relevance REST API Mount Configuration
Assume you have the following HST configuration and you want to add the Relevance REST API to expose the personal data:
/hst:hst: /hst:hosts: /prod: /com: /example: /www: /hst:root: jcr:primaryType: hst:mount /subsite: /org: /example: /www: /hst:root: jcr:primaryType: hst:mount /subsite:
Above is an example in which you have 2 hosts running 4 channels
- www.example.com
- www.example.com/subsite
- www.example.org
- www.example.org/subsite
Now, assume on all channels you are targeting visitors to serve a better experience. For the upcoming GDPR, this means that for all channels you need to be able to serve the personal data you have about a visitor on request of the visitor. The Relevance REST API enables this. You need to decide which URL you want to use to expose the Relevance REST API. Assume you want to expose the personal data of a visitor over
- www.example.com/gdpr
- www.example.org/gdpr
then the above HST configuration should become:
/hst:hst: /hst:hosts: /prod: /com: /example: /www: /hst:root: jcr:primaryType: hst:mount /subsite: jcr:primaryType: hst:mount /gdpr: jcr:primaryType: hst:mount hst:ismapped: false hst:types: rest hst:namedpipeline: TargetingRestApiPipeline /org: /example: /www: /hst:root: jcr:primaryType: hst:mount /subsite: jcr:primaryType: hst:mount /gdpr: jcr:primaryType: hst:mount hst:ismapped: false hst:types: rest hst:namedpipeline: TargetingRestApiPipeline
That is it. By default, the Public Relevance REST API ships with an endpoint /visitorinfo through which you can
- Request (GET) the Personal Data about you (the visitor), see Serve Personal Data
- Request (DELETE) to be forgotten, see Forget About Me
Extend Public Relevance REST API
In your end project you can add new endpoints or override defaults from the TargetingRestApiPipeline by storing Spring beans below
META-INF/hst-assembly/overrides/addon/com/onehippo/cms7/targeting/restapi
Example Adding an Extra Relevance REST API Endpoint
Below is documented how you can add an extra Relevance REST API endpoint. It is a bit contrived example but serves here just as an example. For a real use case scenarion, if instead of the default /visitorinfo you'd like to serialize the result in a different format or different application type, you can do this by adding an extra REST endpoint serializing the result differently.
The Example Requirement
Assume you have a third party integration that polls all new visitors of your website for offline data processing / analyzing. Assume you want a REST call to first get all the visitor ids that have a last access time before now and a limit that you can specify, and you want a rest call to get the VisitorInfo for a specific visitor id. This can be achieved as follows:
In your project's site module, below META-INF/hst-assembly/overrides/addon/com/onehippo/cms7/targeting/restapi add the file (name is free) targeting-site-restapi-extension.xml that contains:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"> <bean id="com.hap.targeting.rest.LatestVisitorsResource" class="com.hap.targeting.rest.LatestVisitorsResource"> <property name="visitorService" ref="com.onehippo.cms7.targeting.VisitorService"/> </bean> <bean id="customRestApiResourceProviders" class="org.springframework.beans.factory.config.ListFactoryBean"> <property name="sourceList"> <list> <bean class="org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider"> <constructor-arg> <ref bean="com.myproject.targeting.rest.LatestVisitorsResource"/> </constructor-arg> </bean> </list> </property> </bean> </beans>
And in the java package com.myproject.targeting.rest add:
package com.hap.targeting.rest; import java.util.ArrayList; import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import com.onehippo.cms7.targeting.VisitorInfo; import com.onehippo.cms7.targeting.VisitorService; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; @Path("/visitors") @Produces(APPLICATION_JSON) public class VisitorsResource { private VisitorService visitorService; private final int DEFAULT_LIMIT = 10; public void setVisitorService(final VisitorService visitorService) { this.visitorService = visitorService; } @GET @Path("/") public List<String> getLatestVisitorsIds(@QueryParam("limit") final int limit) { final List<String> latestVisitorsIds = new ArrayList(); visitorService .getLatestVisitors(System.currentTimeMillis(), limit == 0 ? DEFAULT_LIMIT : limit) .forEach(visitor -> latestVisitorsIds.add(visitor.getId())); return latestVisitorsIds; } @GET @Path("/{visitorId}/") @Produces(MediaType.APPLICATION_JSON) public VisitorInfo getVisitorDetails(@PathParam("visitorId") final String visitorId) { return visitorService.getVisitorInfo(visitorId); } }
With the above changes, you should be able to request (assuming the mount is called 'gdpr')
http://www.example.com/gdpr/visitors
Returning the last 10 visitor ids that accessed the site and then via
http://www.example.com/gdpr/visitor/{id}
where {id} is one of the visitor ids, you return the visitor info for that visitor (in this case the same data as shown in Serve Personal Data). This example does not take any security (RolesAllowed for example) into account which you most likely want to address in a real implementation.