You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2022/10/05 15:25:37 UTC

[unomi] branch UNOMI-657-tracker-doc created (now 7d2c5c093)

This is an automated email from the ASF dual-hosted git repository.

shuber pushed a change to branch UNOMI-657-tracker-doc
in repository https://gitbox.apache.org/repos/asf/unomi.git


      at 7d2c5c093 UNOMI-657 New tracker tutorial & reference, profile aliases and more - Added profile aliases to what's new section (detailed documentation will be added later in UNOMI-686 - Add new web tracking tutorial that explains how to use the tracker in progressive steps - Add new tracker reference sections with custom events definition, cookie and session handling and more - Fix section titles and levels - Update tracker HTML to align with the new tutorial

This branch includes the following new commits:

     new 7d2c5c093 UNOMI-657 New tracker tutorial & reference, profile aliases and more - Added profile aliases to what's new section (detailed documentation will be added later in UNOMI-686 - Add new web tracking tutorial that explains how to use the tracker in progressive steps - Add new tracker reference sections with custom events definition, cookie and session handling and more - Fix section titles and levels - Update tracker HTML to align with the new tutorial

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[unomi] 01/01: UNOMI-657 New tracker tutorial & reference, profile aliases and more - Added profile aliases to what's new section (detailed documentation will be added later in UNOMI-686 - Add new web tracking tutorial that explains how to use the tracker in progressive steps - Add new tracker reference sections with custom events definition, cookie and session handling and more - Fix section titles and levels - Update tracker HTML to align with the new tutorial

Posted by sh...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

shuber pushed a commit to branch UNOMI-657-tracker-doc
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit 7d2c5c093379e018f639078849b6678f7b4360a3
Author: Serge Huber <sh...@jahia.com>
AuthorDate: Wed Oct 5 17:25:31 2022 +0200

    UNOMI-657 New tracker tutorial & reference, profile aliases and more
    - Added profile aliases to what's new section (detailed documentation will be added later in UNOMI-686
    - Add new web tracking tutorial that explains how to use the tracker in progressive steps
    - Add new tracker reference sections with custom events definition, cookie and session handling and more
    - Fix section titles and levels
    - Update tracker HTML to align with the new tutorial
---
 .../web-tracker/wab/src/main/webapp/index.html     |  57 +++-
 manual/src/main/asciidoc/configuration.adoc        |   2 -
 manual/src/main/asciidoc/graphql.adoc              |   8 +-
 manual/src/main/asciidoc/index.adoc                |  18 +-
 .../src/main/asciidoc/jsonSchema/json-schema.adoc  |   3 -
 .../src/main/asciidoc/migrations/migrations.adoc   |   1 -
 manual/src/main/asciidoc/tutorial.adoc             | 343 +++++++++++++++++++++
 manual/src/main/asciidoc/web-tracker.adoc          | 220 +++++++++++++
 manual/src/main/asciidoc/whats-new.adoc            |  16 +-
 9 files changed, 638 insertions(+), 30 deletions(-)

diff --git a/extensions/web-tracker/wab/src/main/webapp/index.html b/extensions/web-tracker/wab/src/main/webapp/index.html
index 7f7a12a8d..4e5fa453b 100644
--- a/extensions/web-tracker/wab/src/main/webapp/index.html
+++ b/extensions/web-tracker/wab/src/main/webapp/index.html
@@ -69,6 +69,39 @@
                 console.log("Unomi tracker test successfully loaded context", unomiWebTracker.getLoadedContext());
             }, 'Unomi tracker test callback example');
 
+            variants = {
+                "var1" : {
+                    content : "variant1",
+                },
+                "var2" : {
+                    content : "variant2",
+                }
+            }
+            unomiWebTracker.registerPersonalizationObject({
+                "id": "testPersonalization",
+                "strategy": "matching-first",
+                "strategyOptions": {"fallback": "var2"},
+                "contents": [{
+                    "id": "var1",
+                    "filters": [{
+                        "condition": {
+                            "type": "profilePropertyCondition",
+                            "parameterValues": {
+                                "propertyName" : "properties.pageViewCount",
+                                "comparisonOperator" : "greaterThan",
+                                "propertyValueInteger" : 5
+                            }
+                        }
+                    }]
+                }, {
+                    "id": "var2"
+                }]
+            }, variants, false, function (successfulFilters, selectedFilter) {
+                if (selectedFilter) {
+                    document.getElementById(selectedFilter.content).style.display = '';
+                }
+            });
+
             // start the tracker
             unomiWebTracker.startTracker();
         })();
@@ -79,18 +112,20 @@
 
 <p>
     The current page is sending a page view event using the <strong>Unomi web tracker</strong>.<br/><br/>
-    <strong>(Prerequisite: create scope "unomi-tracker-test" for the event to be accepted by the backend)</strong>
+
+    <strong>See the <a href="https://unomi.apache.org/manual/latest/index.html#_unomi_web_tracking_tutorial">Unomi tutorial</a> for details on how to use
+        and setup the prerequisites for this code to work properly.</strong>
 </p>
-<pre>
-        POST /cxs/scopes
 
-        {
-          "itemId" : "unomi-tracker-test",
-          "metadata" : {
-            "id" : "unomi-tracker-test",
-            "name" : "Unomi tracker test"
-          }
-        }
-</pre>
+<p>The line below this one will be personalized depending on whether you have been on this page before.
+    Try reload the page to see the changes.</p>
+
+    <div id="variant1" style="display: none">
+        You have already seen this page 5 times
+    </div>
+    <div id="variant2" style="display: none">
+        Welcome. Please reload this page 5 times until it triggers the personalization change
+    </div>
+
 </body>
 </html>
diff --git a/manual/src/main/asciidoc/configuration.adoc b/manual/src/main/asciidoc/configuration.adoc
index 328c9866d..6594f5954 100644
--- a/manual/src/main/asciidoc/configuration.adoc
+++ b/manual/src/main/asciidoc/configuration.adoc
@@ -11,8 +11,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-== Configuration
-
 === Centralized configuration
 
 Apache Unomi uses a centralized configuration file that contains both system properties and configuration properties.
diff --git a/manual/src/main/asciidoc/graphql.adoc b/manual/src/main/asciidoc/graphql.adoc
index 418a5fd05..ed3ec9af6 100644
--- a/manual/src/main/asciidoc/graphql.adoc
+++ b/manual/src/main/asciidoc/graphql.adoc
@@ -11,7 +11,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-=== GraphQL API
+=== Introduction
 
 First introduced in Apache Unomi 2.0, a GraphQL API is available as an alternative to REST for interacting with the platform. 
 Disabled by default, the GraphQL API is currently considered a beta feature.
@@ -19,7 +19,7 @@ Disabled by default, the GraphQL API is currently considered a beta feature.
 We look forward for this new GraphQL API to be used, feel free to open discussion on 
 https://the-asf.slack.com/messages/CBP2Z98Q7/[Unomi Slack channel] or https://issues.apache.org/jira/projects/UNOMI/issues[create tickets on Jira]
 
-==== Enabling/ the API
+=== Enabling the API
 
 The GraphQL API must be enabled using a system property (or environment variable):
 
@@ -35,13 +35,13 @@ org.apache.unomi.graphql.feature.activated=${env:UNOMI_GRAPHQL_FEATURE_ACTIVATED
 You can either modify the `org.apache.unomi.graphql.feature.activated` property or specify the `UNOMI_GRAPHQL_FEATURE_ACTIVATED` 
 environment variable (if using Docker for example).
 
-==== Endpoints
+=== Endpoints
 
 Two endpoints were introduced for Apache Unomi 2 GraphQL API:
 * `/graphql` is the primary endpoint for interacting programatically with the API and aims at receiving POST requests.
 * `/graphql-ui` provides access to the GraphQL Playground UI and aims at being accessed by a Web Browser.
 
-==== GraphQL Schema
+=== GraphQL Schema
 
 Thanks to GraphQL introspection, there is no dedicated documentation per-se as the Schema itself serves as documentation. 
 
diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc
index dcebaf79c..f8bdab770 100644
--- a/manual/src/main/asciidoc/index.adoc
+++ b/manual/src/main/asciidoc/index.adoc
@@ -28,24 +28,34 @@ image::asf_logo_url.png[pdfwidth=35%,align=center]
 
 include::whats-new.adoc[]
 
-== Quick start
+== Discover Unomi
 
 include::5-min-quickstart.adoc[]
 
-== First steps with Apache Unomi
-
 include::getting-started.adoc[]
 
+include::tutorial.adoc[]
+
+== Apache Unomi Recipes and requests
+
 include::recipes.adoc[]
 
 include::request-examples.adoc[]
 
+== Configuration
+
 include::configuration.adoc[]
 
+== JSON schemas
+
 include::jsonSchema/json-schema.adoc[]
 
+== GraphQL API
+
 include::graphql.adoc[]
 
+== Migrations
+
 include::migrations/migrations.adoc[]
 
 == Queries and aggregations
@@ -86,6 +96,8 @@ include::builtin-action-types.adoc[]
 
 include::updating-events.adoc[]
 
+include::web-tracker.adoc[]
+
 == Integration samples
 
 include::samples/samples.adoc[]
diff --git a/manual/src/main/asciidoc/jsonSchema/json-schema.adoc b/manual/src/main/asciidoc/jsonSchema/json-schema.adoc
index a4586a6ae..352c27ebe 100644
--- a/manual/src/main/asciidoc/jsonSchema/json-schema.adoc
+++ b/manual/src/main/asciidoc/jsonSchema/json-schema.adoc
@@ -11,9 +11,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-
-== JSON schemas
-
 include::introduction.adoc[]
 
 include::json-schema-api.adoc[]
diff --git a/manual/src/main/asciidoc/migrations/migrations.adoc b/manual/src/main/asciidoc/migrations/migrations.adoc
index 5f1f3bd26..7507e2514 100644
--- a/manual/src/main/asciidoc/migrations/migrations.adoc
+++ b/manual/src/main/asciidoc/migrations/migrations.adoc
@@ -11,7 +11,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-== Migrations
 
 This section contains information and steps to migrate between major Unomi versions.
 
diff --git a/manual/src/main/asciidoc/tutorial.adoc b/manual/src/main/asciidoc/tutorial.adoc
new file mode 100644
index 000000000..ab7c36f63
--- /dev/null
+++ b/manual/src/main/asciidoc/tutorial.adoc
@@ -0,0 +1,343 @@
+//
+// Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+=== Unomi web tracking tutorial
+
+In this tutorial we will guide through the basic steps of getting started with a web tracking project. You will see how to integrate the built-in web tracker with an existing web site and what this enables.
+
+If you prefer to use existing HTML and Javascript rather than building your own, all the code we feature in this tutorial is extracted from our tracker sample which is available here: https://github.com/apache/unomi/blob/master/extensions/web-tracker/wab/src/main/webapp/index.html . However you will still need to use the REST API calls to create the scope and rule to make it all work.
+
+==== Installing the web tracker in a web page
+
+Using the built-in tracker is pretty simple, simply add the following code to your HTML page :
+
+[source,javascript]
+----
+    <script type="text/javascript" src="/tracker/unomi-web-tracker.min.js"></script>
+----
+
+or you can also use the non-minified version that is available here:
+
+[source,javascript]
+----
+    <script type="text/javascript" src="/tracker/unomi-web-tracker.js"></script>
+----
+
+This will only load the tracker. To initialize it use a snipper like the following code:
+
+[source,javascript]
+----
+    <script type="text/javascript">
+        (function () {
+            const unomiTrackerTestConf = {
+                "scope": "unomi-tracker-test",
+                "site": {
+                    "siteInfo": {
+                        "siteID": "unomi-tracker-test"
+                    }
+                },
+                "page": {
+                    "pageInfo": {
+                        "pageID": "unomi-tracker-test-page",
+                        "pageName": document.title,
+                        "pagePath": document.location.pathname,
+                        "destinationURL": document.location.origin + document.location.pathname,
+                        "language": "en",
+                        "categories": [],
+                        "tags": []
+                    },
+                    "attributes": {},
+                    "consentTypes": []
+                },
+                "events:": [],
+                "wemInitConfig": {
+                    "contextServerUrl": document.location.origin,
+                    "timeoutInMilliseconds": "1500",
+                    "contextServerCookieName": "context-profile-id",
+                    "activateWem": true,
+                    "trackerSessionIdCookieName": "unomi-tracker-test-session-id",
+                    "trackerProfileIdCookieName": "unomi-tracker-test-profile-id"
+                }
+            }
+
+            // generate a new session
+            if (unomiWebTracker.getCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName) == null) {
+                unomiWebTracker.setCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName, unomiWebTracker.generateGuid(), 1);
+            }
+
+            // init tracker with our conf
+            unomiWebTracker.initTracker(unomiTrackerTestConf);
+
+            unomiWebTracker._registerCallback(() => {
+                console.log("Unomi tracker test successfully loaded context", unomiWebTracker.getLoadedContext());
+            }, 'Unomi tracker test callback example');
+
+            // start the tracker
+            unomiWebTracker.startTracker();
+        })();
+    </script>
+----
+
+==== Creating a scope to collect the data
+
+You might notice the `scope` used in the snippet. All events sent to Unomi must be associated with a scope, that must have been created before events are accepted. So in order to make sure the events are collected with the above Javascript code, we must create a scope with the following request.
+
+[source,shell]
+----
+curl --location --request POST 'http://localhost:8181/cxs/scopes' \
+  --header 'Authorization: Basic a2FyYWY6a2FyYWY=' \
+  --header 'Content-Type: application/json' \
+  --data-raw '{
+    "itemId": "unomi-tracker-test",
+    "metadata": {
+      "id": "unomi-tracker-test",
+      "name": "Unomi tracker Test Scope"
+    }
+  }'
+----
+
+The authorization is the default username/password for the REST API, which is `karaf:karaf` and you that should definitely be changed as soon as possible by modifying the `etc/users.properties` file.
+
+==== Using tracker in your own JavaScript projects
+
+The tracker also exists as an NPM library that you can integrate with your own Javascript projects. You can find the library here:
+
+    https://www.npmjs.com/package/apache-unomi-tracker
+
+Here's an example on how to use it:
+
+[source,shell]
+----
+    yarn add apache-unomi-tracker
+----
+
+You can then simply use it in your JS code using something like this:
+
+[source,javascript]
+----
+import {useTracker} from "apache-unomi-tracker";
+
+(function () {
+    const unomiWebTracker = useTracker();
+    const unomiTrackerTestConf = {
+        "scope": "unomi-tracker-test",
+        "site": {
+            "siteInfo": {
+                "siteID": "unomi-tracker-test"
+            }
+        },
+        "page": {
+            "pageInfo": {
+                "pageID": "unomi-tracker-test-page",
+                "pageName": document.title,
+                "pagePath": document.location.pathname,
+                "destinationURL": document.location.origin + document.location.pathname,
+                "language": "en",
+                "categories": [],
+                "tags": []
+            },
+            "attributes": {},
+            "consentTypes": []
+        },
+        "events:": [],
+        "wemInitConfig": {
+            "contextServerUrl": document.location.origin,
+            "timeoutInMilliseconds": "1500",
+            "contextServerCookieName": "context-profile-id",
+            "activateWem": true,
+            "trackerSessionIdCookieName": "unomi-tracker-test-session-id",
+            "trackerProfileIdCookieName": "unomi-tracker-test-profile-id"
+        }
+    }
+
+    // generate a new session
+    if (unomiWebTracker.getCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName) == null) {
+        unomiWebTracker.setCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName, unomiWebTracker.generateGuid(), 1);
+    }
+
+    // init tracker with our conf
+    unomiWebTracker.initTracker(unomiTrackerTestConf);
+
+    unomiWebTracker._registerCallback(() => {
+        console.log("Unomi tracker test successfully loaded context", unomiWebTracker.getLoadedContext());
+    }, 'Unomi tracker test callback example');
+
+    // start the tracker
+    unomiWebTracker.startTracker();
+})();
+----
+
+==== Viewing collected events
+
+There are multiple ways to view the events that were received. For example, you could use the following cURL request:
+
+[source,shell]
+----
+curl --location --request POST 'http://localhost:8181/cxs/events/search' \
+  --header 'Authorization: Basic a2FyYWY6a2FyYWY=' \
+  --header 'Content-Type: application/json' \
+  --data-raw '{
+    "sortby" : "timeStamp:desc",
+    "condition" : {
+      "type" : "matchAllCondition"
+    }
+  }'
+----
+
+Another (powerful) way to look at events is to use the SSH Console. You can connect to it with the following shell command:
+
+[source,shell]
+----
+    ssh -p 8102 karaf@localhost
+----
+
+Using the same username password (karaf:karaf) and then you can use command such as :
+
+- `event-tail` to view in realtime the events as they come in (CTRL+C to stop)
+- `event-list` to view the latest events
+- `event-view EVENT_ID` to view the details of a specific event
+
+==== Viewing the current profile
+
+By default, Unomi uses a cookie called context-profile-id to keep track of the current profile. You can use this the value of this cookie which contains a UUID to lookup the details of the profile. For example with the SSH console you can simply to:
+
+    profile-view PROFILE_UUID
+
+Which will print out the details of the profile with the associated ID.
+Another interesting command is `profile-list` to list all the recently modified profiles
+
+You could also retrieve the profile details using the REST API by using a request such as this one:
+
+[source,shell]
+----
+curl --location --request GET 'http://localhost:8181/cxs/profiles/PROFILE_UUID' \
+--header 'Authorization: Basic a2FyYWY6a2FyYWY=' \
+----
+
+==== Adding a rule
+
+Rules are a powerful ways to react in real-time to incoming events. For example a rule could update a profile when a certain event comes in, either copying values from the event or performing some kind of computation when the event occurs, including accessing remote systems such as a Salesforce CRM (see the Salesforce connector sample).
+
+In this example we will simply setup a basic rule that will react to the `view` event and set a property in the current profile.
+
+[source,shell]
+----
+curl --location --request POST 'http://localhost:8181/cxs/rules' \
+--header 'Authorization: Basic a2FyYWY6a2FyYWY=' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+    "metadata": {
+        "id": "viewEventRule",
+        "name": "View event rule",
+        "description": "Increments a property on a profile to indicate that this rule executed successfully when a view event occurs"
+    },
+    "condition": {
+        "type": "eventTypeCondition",
+        "parameterValues": {
+            "eventTypeId": "view"
+        }
+    },
+    "actions": [
+        {
+            "type": "incrementPropertyAction",
+            "parameterValues": {
+                "propertyName": "pageViewCount"
+            }
+        }
+    ]
+}'
+----
+
+The above rule will execute when a view event is received (which is automatically sent by the tracker when a page is loaded) and increments a property called `pageViewCount` on the user's profile.
+
+You can then reload then page and check with the `profile-view PROFILE_UUID` SSH command that the profile was updated with the new property and that it is incremented on each page reload.
+
+You can also use the `rule-list` command to display all the rules in the system and the `rule-tail` to watch in real-time which rules are executed. The `rule-view RULE_ID` command will let you view the contents of a rule.
+
+==== Adding personalization
+
+The last step is to use the newly added property to the profile to perform some page personalization. In order to do that we will use the tracker's API to register a personalization that will be using a condition that checks if the `pageViewCount` is higher than 5. If it has, `variant1` will be displayed, otherwise the fallback variant `variant2` will be used instead.
+
+[source,javascript]
+----
+            variants = {
+                "var1" : {
+                    content : "variant1",
+                },
+                "var2" : {
+                    content : "variant2",
+                }
+            }
+            unomiWebTracker.registerPersonalizationObject({
+                "id": "testPersonalization",
+                "strategy": "matching-first",
+                "strategyOptions": {"fallback": "var2"},
+                "contents": [{
+                    "id": "var1",
+                    "filters": [{
+                        "condition": {
+                            "type": "profilePropertyCondition",
+                            "parameterValues": {
+                                "propertyName" : "properties.pageViewCount",
+                                "comparisonOperator" : "greaterThan",
+                                "propertyValueInteger" : 5
+                            }
+                        }
+                    }]
+                }, {
+                    "id": "var2"
+                }]
+            }, variants, false, function (successfulFilters, selectedFilter) {
+                if (selectedFilter) {
+                    document.getElementById(selectedFilter.content).style.display = '';
+                }
+            });
+
+----
+
+As you can see in the above code snippet, a `variants` array is created with two objects that associated personalization IDs with content IDs. Then we build the personalization object that contains the two IDs and their associated conditions (only a condition on `var1` is passed in this case) as well as an option to indicate which is the fallback variant in case no conditions are matched.
+
+The HTML part of this example looks like this:
+
+[source,html]
+----
+    <div id="variant1" style="display: none">
+        You have already seen this page 5 times
+    </div>
+    <div id="variant2" style="display: none">
+        Welcome. Please reload this page 5 times until it triggers the personalization change
+    </div>
+----
+
+As you can see we hide the variants by default so that there is no "flashing" effect and then use the callback function to display to variant resolve by Unomi's personalization engine.
+
+==== Conclusion
+
+What have we achieved so far ?
+
+- Installed a tracker in a web page
+- Created a scope in which to collect the data
+- Learned how to use the tracker as an NPM library
+- How to view the collected events
+- How to view the current visitor profile
+- How to add a rule to update a profile property
+- How to personalize a web page's content based on the property updated by the rule
+
+Of course this tutorial is just one example of what could be achieved, and hasn't even yet introduced more advanced notions such as profile segmentation or Groovy action scripting. The system is capable of much more, for example by directly using its actions to integrate with third-party systems (CRM, social networks, etc..)
+
+==== Next steps
+
+- Learn more about the <<_unomi_web_tracker_reference,web tracker, custom events, API, ...>>
+- Learn more about <<_segment,segmentation>>
+- View some more <<_integration_samples,samples>>
+- Continue reading Unomi's user manual to see all that is possible with this technology
\ No newline at end of file
diff --git a/manual/src/main/asciidoc/web-tracker.adoc b/manual/src/main/asciidoc/web-tracker.adoc
new file mode 100644
index 000000000..e88718c7d
--- /dev/null
+++ b/manual/src/main/asciidoc/web-tracker.adoc
@@ -0,0 +1,220 @@
+//
+// Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+=== Unomi Web Tracker reference
+
+In this section of the documentation, more details are provided about the web tracker provided by Unomi.
+
+==== Custom events
+
+In order to be able to use your own custom events with the web tracker, you must first declare them in Unomi so that they are properly recognized and validated by the `/context.json` or `/eventcollector` endpoints.
+
+===== Declaring JSON schema
+
+The first step is to declare a JSON schema for your custom event type. Here's an example of such a declaration:
+
+[source,json]
+----
+{
+  "$id": "https://unomi.apache.org/schemas/json/events/click/1-0-0",
+  "$schema": "https://json-schema.org/draft/2019-09/schema",
+  "self": {
+    "vendor": "org.apache.unomi",
+    "target": "events",
+    "name": "click",
+    "format": "jsonschema",
+    "version": "1-0-0"
+  },
+  "title": "ClickEvent",
+  "type": "object",
+  "allOf": [
+    {
+      "$ref": "https://unomi.apache.org/schemas/json/event/1-0-0"
+    }
+  ],
+  "properties": {
+    "source": {
+      "$ref": "https://unomi.apache.org/schemas/json/items/page/1-0-0"
+    },
+    "target": {
+      "$ref": "https://unomi.apache.org/schemas/json/item/1-0-0"
+    }
+  },
+  "unevaluatedProperties": false
+}
+----
+
+The above example comes from a built-in event type that is already declared in Unomi but that illustrates the structure of a JSON schema. It is not however the objective of this section of the documentation to go into the details of how to declare a JSON schema, instead, we recommend you go to the <<_json_schemas_2,corresponding section>> of the documentation.
+
+===== Sending event from tracker
+
+In the Unomi web tracker, you can use the following function to send an event to Unomi:
+
+[source,javascript]
+----
+        /**
+         * This function will send an event to Apache Unomi
+         * @param {object} event The event object to send, you can build it using wem.buildEvent(eventType, target, source)
+         * @param {function} successCallback optional, will be executed in case of success
+         * @param {function} errorCallback optional, will be executed in case of error
+         * @return {undefined}
+         */
+        collectEvent: function (event, successCallback = undefined, errorCallback = undefined)
+----
+
+As you can see this function is quite straight forward to use. There are also helper functions to build events, such as :
+
+[source,javascript]
+----
+        /**
+         * This function return the basic structure for an event, it must be adapted to your need
+         *
+         * @param {string} eventType The name of your event
+         * @param {object} [target] The target object for your event can be build with wem.buildTarget(targetId, targetType, targetProperties)
+         * @param {object} [source] The source object for your event can be build with wem.buildSource(sourceId, sourceType, sourceProperties)
+         * @returns {object} the event
+         */
+        buildEvent: function (eventType, target, source)
+
+        /**
+         * This function return an event of type form
+         *
+         * @param {string} formName The HTML name of id of the form to use in the target of the event
+         * @param {HTMLFormElement} form optional HTML form element, if provided will be used to extract the form fields and populate the form event
+         * @returns {object} the form event
+         */
+        buildFormEvent: function (formName, form = undefined)
+
+        /**
+         * This function return the source object for a source of type page
+         *
+         * @returns {object} the target page
+         */
+        buildTargetPage: function ()
+
+        /**
+         * This function return the source object for a source of type page
+         *
+         * @returns {object} the source page
+         */
+        buildSourcePage: function ()
+
+        /**
+         * This function return the basic structure for the target of your event
+         *
+         * @param {string} targetId The ID of the target
+         * @param {string} targetType The type of the target
+         * @param {object} [targetProperties] The optional properties of the target
+         * @returns {object} the target
+         */
+        buildTarget: function (targetId, targetType, targetProperties = undefined)
+
+        /**
+         * This function return the basic structure for the source of your event
+         *
+         * @param {string} sourceId The ID of the source
+         * @param {string} sourceType The type of the source
+         * @param {object} [sourceProperties] The optional properties of the source
+         * @returns {object} the source
+         */
+        buildSource: function (sourceId, sourceType, sourceProperties = undefined)
+
+----
+
+Here's an example of using these helper functions and the `collectEvent` function alltogether:
+
+[source,javascript]
+----
+    var clickEvent = wem.buildEvent('click',
+        wem.buildTarget('buttonId', 'button'),
+        wem.buildSourcePage());
+
+    wem.collectEvent(clickEvent, function (xhr) {
+        console.info('Click event successfully collected.');
+    }, function (xhr) {
+        console.error('Could not send click event.');
+    });
+----
+
+===== Sending multiple events
+
+In some cases, especially when multiple events must be sent fast and the order of the events is critical for rules to be properly executed, it is better to use another function called `collectEvents` that will batch the sending of events to Unomi in a single HTTP request. Here's the signature of this function:
+
+[source,javascript]
+----
+        /**
+         * This function will send the events to Apache Unomi
+         *
+         * @param {object} events Javascript object { events: [event1, event2] }
+         * @param {function} successCallback optional, will be executed in case of success
+         * @param {function} errorCallback optional, will be executed in case of error
+         * @return {undefined}
+         */
+        collectEvents: function (events, successCallback = undefined, errorCallback = undefined)
+----
+
+This function is almost exactly the same as the `collectEvent` method except that it takes an events array instead of a single one. The events in the array may be of any mixture of types.
+
+===== Extending existing events
+
+An alternative to defining custom event types is to extend existing event types. This, for example, can be used to add new properties to the built-in `view` event type.
+
+For more information about event type extensions, please read the <<_extend_an_existing_schema,JSON schema section>> of this documentation.
+
+==== Integrating with tag managers
+
+Integrating with tag managers such as Google Tag Manager is an important part of the way trackers can be added to an existing site. Unomi's web tracker should be pretty easy to integrate with such tools: you simply need to insert the script tag to load the script and then another tag to initialize it and map any tag manager variables you want.
+
+Personalization scripts should however be modified to check for the existence of the tracker object in the page because tag managers might deactivate scripts based on conditions such as GDPR approval, cookie preferences, ...
+
+==== Cookie/session handling
+
+In order to track profiles, an identifier must be stored in the browser so that subsequent requests can keep a reference to the visitor's profile. Also, a session identifier must be generated to group the current visitor interactions.
+
+Unomi's web tracker uses 3 cookies in the tracker by default:
+
+- server profile ID, called `context-profile-id` by default, that is sent from the Unomi server
+- web tracker profile ID, called `web-profile-id` by default (this is a copy of the server profile ID that can be managed by the tracker directly)
+- web tracker session ID, called `wem-session-id` by default
+
+It is possible to change the name of these cookie by passing the following properties to the start's initialization:
+
+[source,javascript]
+----
+    "wemInitConfig": {
+        ...
+        "contextServerCookieName": "context-profile-id",
+        "trackerSessionIdCookieName": "unomi-tracker-test-session-id",
+        "trackerProfileIdCookieName": "unomi-tracker-test-profile-id"
+    }
+----
+
+Please note however that the `contextServerCookieName` will also have to be changed in the server configuration in order for it to work. See the <<_configuration,Configuration>> section for details on how to do this.
+
+For session tracking, it is important to provide a value for the cookie otherwise the tracker will not initialize (a message is displayed in the console that explains that the session cookie is missing). Here is the code excerpt from the initialization code used in the tutorial that creates the initial cookie value.
+
+[source,javascript]
+----
+    // generate a new session
+    if (unomiWebTracker.getCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName) == null) {
+        unomiWebTracker.setCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName, unomiWebTracker.generateGuid(), 1);
+    }
+----
+
+Note that this is just an example, you could very well customize this code to create session IDs another way.
+
+==== JavaScript API
+
+The JavaScript API for the web tracker is directly provided in the source code of the web tracker. You can find it here: https://github.com/apache/unomi-tracker/blob/main/src/apache-unomi-tracker.js
+
+Please note that only the functions that do NOT start with an underscore should be used. The ones that start with an underscore are not considered part of the public API and could change or even be removed at any point in the future.
\ No newline at end of file
diff --git a/manual/src/main/asciidoc/whats-new.adoc b/manual/src/main/asciidoc/whats-new.adoc
index 97e5a1661..764309d59 100644
--- a/manual/src/main/asciidoc/whats-new.adoc
+++ b/manual/src/main/asciidoc/whats-new.adoc
@@ -14,10 +14,15 @@
 === What's new in Apache Unomi 2.0
 
 Apache Unomi 2 is a new release focused on improving core functionalities and robustness of the product.
+
 The introduction of tighter data validation with JSON Schemas required some changes in the product data model, which presented an opportunity for noticeable improvements in the overall performance.
 
 This new release also introduces a first (beta) version of the Unomi GraphQL API.
 
+==== Introducing profiles aliases
+
+Profiles may now have alias IDs, which is a new way to reference profiles using multiple IDs. The Unomi ID still exists, but a new index with aliases can reference a single Unomi profile. This enables more flexible integrations with external systems, as well as provide more flexible and reliable merging mechanisms. A new REST API makes it easy to define, update and remove aliases for profiles.
+
 ==== Scopes declarations are now required
 
 Scopes declarations are now required in Unomi 2. When submitting an event and specifying a scope,
@@ -133,15 +138,14 @@ Is replaced by the following in Apache Unomi 2.x:
 ----
 
 If using the default Apache 1.x data model, our Unomi 2 migration process will handle the data model changes for you.
-If you are using custom events/objects, please refer to the detailed migration guide for more details.
 
-==== Removal of the Web Tracker
+If you are using custom events/objects, please refer to the detailed <<_migration_overview,migration guide>> for more details.
+
+==== New Web Tracker
 
-[TODO: Add more details about the web tracker]
+Apache Unomi 2.0 Web Tracker, located in `extensions/web-tracker/` has been completely rewritten. It is no longer based on an external library and is fully self-sufficient. It is based on an external contribution that has been used in production on many sites.
 
-Apache Unomi 2.0 Web Tracker, previously located in `extensions/web-tracker/` has been removed.
-We considered it as outdated and instead recommend implementing your own tracker logic based on your project
-use case.
+You can find more information about the <<_unomi_web_tracking_tutorial,new web tracker here>>.
 
 ==== GraphQL API - beta