Developing an LRS from scratch is not a trivial undertaking. The first step is to get an in-depth understanding of the complete Tin Can specification itself. Unlike an activity provider, which can just use the very basic parts of the specification, an LRS is responsible for implementing the entire specification along with other specifications that are required to be used alongside the Tin Can spec.
To build an LRS, you first have to decide if the LRS will be a component of an LMS or a standalone, enterprise LRS. If you decide to build your LRS as part of an LMS, there’s a guide that handles how Tin Can content should be imported, launched and how to deal with private access to content. That LMS/LRS guide lives alongside the Tin Can specification and is agreed upon by the community as the best practice for implementing an LRS in an LMS, you can find it here.
This is the bare minimum functionality you will need to implement, no matter which type of LRS you decide to build.
The Tin Can API is really a collection of four RESTful APIs. In talking about Tin Can, many focus on statements and transferring them between LRSs, this is the job of the statement API. The Document APIs: State, Activity, and Agent don’t deal with statements, they allow for richer information to be stored (a string, an image, a Word document, a video, etc — the document APIs are about these other pieces of data that don’t belong in a statement.) To build an LRS, you have to implement all four APIs.Statement API
Statements range in size. Today we often see fairly small statements, but there is potential for very large statements. Adding attachments in 1.0 increases the likelihood of seeing larger statements regularly. An LRS needs to determine how large is too large for a statement, this needs to be carefully decided so it doesn’t limit common use cases for the users. The LRS needs to effectively balance storing and working with large statements while efficiently handling smaller statements.
An example for using this API could be a person editing an image as part of a learning activity, that image could be saved (as an image) in the LRS with the state API. The different states of the image could be pulled back by the application at the end to put together a collection that shows the changes in the work over time.
For team simulations, an activity could store it’s state under it’s own profile instead of with the State API. The state API is specific to Agent, the Activity API is not agent specific. Screenshots or pictures of real life training could be stored with this API. It’s a way to store documents that represent people showing their work with the activity.
Another example, in our Tetris prototype you will see a call to store high scores across the users in the LRS as a document for the activity with the Activity API. This populates a leaderboard much more quickly than by deriving high scores across all of the statements for the Tetris activity each time the user requests to see the leaderboard.
An avatar that represents the person could be added to their profile in the LRS through the Agent API. An avatar image is not something that would be sent with a statement. When other permissed systems queried the LRS for the profile, they could pull in this image file to populate for the person in their system.
An LRS has to support two authentication mechanisms HTTP basic authentication and oAuth. Three legged oAuth, even. oAuth is a commonly used specification for authentication on the web. This image is an example of a music site and Facebook connecting with oAuth. In most of the Tin Can API world, the consumer would be an application and the provider would be the LRS. LRSs can also connect to each other. Once connected they can use the Statement API to share statements.
There are five scenarios:
In the authorization process, the Tin Can API spec has specific ways that tokens, authorizations, and temporary credentials need to work with endpoints.
If an oAuth connection is made, the scope of the data that can be shared through the statement API is defined. An LRS has to follow oAuth 2.0’s scope parameter, which is here. The scope allows the statement API to work with the data in the LRS on the users behalf.
The LRS is responsible for populating (or validating) the authority portion of the statement, based on the credentials used to send the statement. There are specific requirements for constructing an authority based on each type of authentication.
Retrieving a collection of statements from an LRS and storing them locally to support reports or visualizations is the best way to work with the data. Constantly querying the LRS is less effective because it’s a big load. Pulling down the data also allows you to do more in-depth queries of the data because the statement API doesn’t query on all statement parameters (extensions, for example, can not be queried with the statement API).
An LRS will need to carefully index data because efficiently serving the queries can be complex. One challenge is efficiently handling relationships between statements. An example of this would be if a search for certain conditions that match statement C must return statement A. Then if A targets B which targets C, all three must be returned, as described in filter conditions for statement refs. Statement references are ways to point to another statement as important to the referencing statement.
When a query for statements is made to the LRS, it returns a number of statement results and generates a URL, which has to remain active for 24 hours to allow for more statements to be pulled down. There are also guidelines about the length of URLs and storage of query data, that’s all in the spec too.
The LRS needs to store all ‘voiding’ statements, and when returning statement results the LRS must filter out any statements which have been voided. The LRS should check to see if that statement made any changes to the activity or agent definitions and roll the changes that the statement made back. Read more about handling voided statements here.
Read more about Data Concurrency Controls.
Learn more about Signed Statement Validation here.
Learn more about HTTP Head here.
Version headers are required in every response from an LRS to make it clear what versions of the Tin Can specification the LRS supports. The version headers use semantic versioning. An LRS needs to provide an ‘about’ resource that returns JSON that identifies the versions of the specification that the LRS supports.
There are three versions of the Tin Can API specification that are in use by different activity providers currently. The specification doesn’t require that an LRS support all of the versions, but it should. Supporting limited versions is a headache for the LRS administrators and users. An LRS needs to be able to receive and store statements that are 0.9, 0.95, and 1.0.0. The LRS also needs to up-convert older versions to the most recent version of the specification so that they are able to be understood by all clients. This means that the older statements must be preserved in their original state and an updated version should be stored according to this appendix to the spec, appendix F.
An LRS is not required by the specification to do user management and permission management, but in order to be usable it should. This includes building ways for applications to register as OAuth consumers, managing user credentials, and managing basic authorization combos. Managing who can talk with the LRS is one part, the next step is to give controls for how much they can access which comes with a much more complicated set of decisions to be made about security and permissions.
The specification enables cross domain tracking, but the method for doing this does not allow HTTP headers to be set. This is a problem in using IE8 and IE9. There is a specific workaround for this in the specification that LRSs must support.
Now, all of that said, if you want to build an LRS that is a standalone enterprise system it will need a lot more functionality. These are some of the things the LRS should do: