New xAPI plugin for Adapt

By jPablo Caballero | January 17, 2018

Yesterday I released a “new” xAPI plugin for Adapt. Its functionality is almost the same as the existing adapt-tkhub-xAPI, but the main change is that the dependency on adapt-trackingHub has been removed, so there is only one plugin to install and configure. This makes the new plugin much easier not only to install and set-up, but also to understand and to modify, should anyone need to customize it to fit his or her specific requirements.

So, the main reason behind this rewrite was to simplify things for those who want to add xAPI tracking to their Adapt courses.

Background on trackingHub

What is trackingHub, anyway? Why did the old xAPI plugin depend on it? A long while ago, when I started thinking about tracking and xAPI (particularly in Adapt), I ended up trying to find the core concepts and ideas about tracking in general. Those key elements that would always be present, regardless of the particular ‘type of tracking’. So, I was not really thinking about xAPI in particular. I was thinking about ‘something’, some general tracking infrastructure, of which xAPI tracking would just be a specific ‘implementation’. So, trackingHub was the implementation that embodied this general tracking infrastructure and its concepts(channels, channel handlers, message composers, launch phase, state representations, multi-channel tracking, etc.). Once the general tracking infrastructure was ready, I could implement a specific tracking solution, and the one I was most interested in was xAPI. So, adapt-tkhub-xAPI was just a specific case of tracking -albeit a very popular one. And that is why adapt-tkhub-xAPI required adapt-trackingHub.

People interested in creating specific tracking solutions for Adapt (and indeed some developers wanted to do that), can do so fairly easily just by leveraging adapt-trackingHub. For example, somebody wanted to send tracking data to a PHP script that would just store the data in a database (he did not want to use an LMS, or an LRS). That's a specific tracking situation that would be challenging to do from scratch. But with tracking-Hub, one can just clone the starter plugin and modify it to define how to represent the tracking data (json? text? etc.), the specific interaction with the backend (calls to a REST API, websockets…), and a specific representation of state, if needed, and that's it.

Furthermore, trackingHub was conceived to allow multichannel tracking. That is, tracking data can be sent -in different formats-, to different backends at the same time. So, one could have a course send xAPI statements normally to an LRS, but on course completion, or assesement completion, or whatever, messages can be sent to the Slack API, to display completion notifications on a slack channel. That was the idea behind multichannel tracking: the tracking infrastructure should facilitate great flexibility for a wide variety of tracking scenarios. Even SCORM can be implemented following the model set forth by trackingHub, since the main ideas are really the same (a ‘launch’ phase to find an API to communicate with, a state representation, etc.). But it would not be worth doing, because the core SCORM plugin for Adapt is very mature and stable.

Detaching xAPI from trackingHub

So, trackingHub -and the tracking plugins that it enables- is very flexible and powerful, and it indeed satisfied my deepest desires related to tracking (by the way, this was all implemented in Adapt, but the general ideas are transferrable to any other system). But with power and flexibility comes complexity. And the support requests I have received were mostly related to configuration misunderstandings, because it was necessary to install two plugins, and juggle the configuration of both so they worked together as expected.

Also, except for a couple of cases where developers wanted custom tracking solutions, most people who have used trackingHub did so because they just wanted to add xAPI tracking to their courses.

So, a while ago I started to feel uneasy about making people go through trackingHub to just do xAPI… and I thought that -at some point- I should just refactor code from adapt-trackingHub and adapt-tkhub-xAPI into just one plugin. I had all the ingredients, I just had to mix them differently. It took a little longer than expected, but I ended up with less code, less configuration options, less chances to misconfigure something, and -hopefully- code that is easier to understand and modify/customize.

The xAPI Learning Cohort

In reality, I had thought about doing this for a while, but the real trigger was that some folks that are participating in the Spring 2016 xAPI Learning Cohort (organized by Torrance Learning) showed interest in using Adapt. So, it was just the right time to improve the plugin.

In the previous xAPI Learning Cohort, the authoring tools team did a great job of evaluating and presenting the xAPI capabilities of various authoring tools. Adapt was not evaluated then, but it seems that some participants are interested in evaluating it this time around. Hopefully this new plugin will make it a little bit easier to get started with the xAPI part, for anybody who decides to use it.

Please note that this adapt-alt-xapi plugin is just an external contribution to the Adapt project. It is neither endorsed not supported by the core Adapt Learning team. There is another xAPI plugin (adapt-contrib-xapi) being developed as part of the core product. Having more choice is good for users.

So, in contrast with the other Authoring Tools that were evaluated in the previous Cohort, the thing with Adapt -for those who are inclined to use it- is that not only can they choose the specific xAPI plugin they want to use, but they can see how it is coded, they can talk to the developers, they can suggest or contribute changes to the plugin, or they can just take the code as a starting point and modify it to fit their needs.

I think this is a great opportunity for the Adapt community to learn about IDs’ and course developer's needs regarding xAPI.

Changes from the old adapt-tkhub-xAPI


The whole concept of multi-channel tracking has been dropped in adapt-alt-xapi in favor of simplicity. The most common case is to send the xAPI Statements to just one LRS. So, there is no concept of channels. Once you set up the plugin, it reports to one LRS.


There was a configuration option in the old plugin to control whether the statement id was generated in the browser before sending the statement or not. But, as I see in the source code of the xAPIWrapper library that I use, the library will check if the statement has an id or not, and if it doesn't, it will generate and assign the id right before sending the statement. Therefore, it seems that the _generateIds setting does not really do anything useful (unless you modify the xAPIWrapper library). So, in this version, this setting has been eliminated.

The background about this setting and why it was implemented in the first place, is that UUID generation in a browser is not very reliable. One developer ran into the problem of duplicate ids being created after a few thousand statements were generated in each device. So, statements were (correctly) being rejected by the LRS because they had an id that already existed. One solution to this problem is to leave the statement without id, and then the LRS will generate and ID for it (however, the xAPI spec actually recommends to send statements with id).

Here's an interesting (very technical) article about identifier generation and Pseudo Random Number Generators.


The same developer mentioned above (who, by the way, made great contributions to adapt-tkhub-xAPI and adapt-trackingHub) implemented client-side caching of statements and state. It made a lot of sense in his environment, since his solution involved Adapt courses running on mobile devices. So, this was added to adapt-tkhub-xAPI, but for a ‘normal’ Adapt course running in a browser, this caching would only really work with the ‘tincan’ launch, and sometimes not even so.

The more advanced launch methods (adl-xapi-launch, and CMI5 -although I have not implemented any CMI5 functionality in adapt-alt-xapi-, and maybe some custom launch method) use credentials that are valid only for one session (they are ephimeral, to increase security). So, it turns out that implementing client-side caching in this situations is either not possible or very difficult (really, the server-side component that creates the ephimeral credentials would need to be implemented in a way that accepts statements that come later -from the cache of an expired session-), so it's not trivial at all.

Even with the tincan launch, depending on the implementation (e.g. the tincan Moodle plugin running against Learning Locker v1), the plugin still creates ephimeral credentials in the LRS … so the problem might still exist: If a client sends cached statements using credentials that the LRS considers expired, then it will reject those statements.

In summary, caching can be a little more involved than it seems at first sight. So, it was removed in adapt-alt-xapi.