Openactive Session Exchange Standard v0.1 (draft)

Document Purpose

This document represents a minimal early draft of a standard for data exchange for physical activity data. It is highly likely to change completely between now and the first published version, so this document should only be used to guide swift implementations where the quick win of data sharing and shared learning are high priorities.

Scope

This standard is tightly defined to cover data exchange itself.

Goals:

Non-Goals:

Objectives

Contents

  1. Simple REST Endpoint

The most basic implementation of this standard, which requires polling.

  1. Data Streaming

A real-time, high volume alternative to the basic implementation.

1. Simple REST Endpoint

A single REST endpoint that returns a JSON page of results given two query parameters:

The data returned can be defined as follows:

var query;

if (queryParams.from) {

query = Session.query().filter((Session.modified == queryParams.from && Session.id > queryParams.after) || (Session.modified > queryParams.from));

} else {

query = Session.query()

}

return query.sort([Session.modified, Session.id])

The above can be run for each relevant entity type (e.g. club, courses, sessions), with separate endpoints for each.

Note that deleted items are included in the response with a deleted state, but no <data> associated.

Response grammar / example:

<response> => {

items: [<item>,<item>,<item>,...],

next: '/getAfterTimestamp?from=<date>&after=<id>'

   }

<item> => {

state: 'Updated' | 'Deleted',

kind: "session",

id: "{21EC2020-3AEA-4069-A2DD-08002B303123}",

modified: Date(a),

data: <data>

   }

<data> => {

lat: 51.5072,

lng: -0.1275,

name: 'Acrobatics with Dave',

                groupId: "{0657FD6D-A4AB-43C4-84E5-0933C84B4F4F}"

clubId: "{38A52BE4-9352-453E-AF97-5C3B448652F0}"

   }

A full example response:

/getSessionsAfterTimestamp?from=Date(a)

-> { items: [{

   state: 'Updated',

   kind: “session”,

   id: “{c15814e5-8931-470c-8a16-ef45afedaece}”,

   modified: Date(a),

   data: {

       lat: 51.5072,

       lng: -0.1275,

       name: 'Acrobatics with Dave',

       clubId: "{fc1f0f87-0538-4b05-96a0-cee88b9c3377}"

   }roast

},{

   state: 'Deleted',

   kind: “session”,

   id: “{d97f73fb-4718-48ee-a6a9-9c7d717ebd85}”,

   modified: Date(b)

}],

next: '/getSessionsAfterTimestamp?from=Date(b)&after={d97f73fb-4718-48ee-a6a9-9c7d717ebd85}'

}

This paging allows ongoing data synchronisation to synchronize all data, that can be replayed arbitrarily by the client.


2. Data Streaming (ideas stage)

There are a variety of technologies that should allow us to stream data instead of polling, allowing near-real-time updates for high volume/load scenarios. This is worth considering in the second iteration of the standard, so the below are a collection of ideas of how this could be implemented.

AMQP

https://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol

RabbitMQ et. al. would provide a two way channel for events, but we’d need to think about certificates etc. as it’s not as simple as HTTPS.

Server-Sent Events

It’s potentially much more simple to use Server-Sent Events (over HTTP) (https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)

Example session:

/stream?from=Date(a)

->

event: create

data: {"id": 1, "timestamp": Date(a), "data": {"name": "Juniors' Netball", "lat": 51.5072, "lng": -0.1275}}

event: create

data: {"id": 2, "timestamp": Date(a), "data": {"name": "Afternoon Rounders", "lat": 51.5072, "lng": -0.1275}}

/stream?from=Date(a)&after=2

->

event: create

data: {"id": 3, "timestamp": Date(a), "data": {"name": "Adults' British Bulldog", "lat": 51.5072, "lng": -0.1275}}

event: update

data: {"id": 1, "timestamp": Date(b), "data": {"lat": 51.51, "lng": -0.1266}}

/stream?from=Date(b)&after=1

->

event: create

data: {"id": 4, "timestamp": Date(b), "data": {"name": "Seniors Disco Cycling", "lat": 51.5072, "lng": -0.1275}}

event: delete

data: {"id": 2, "timestamp": Date(c)}

(Date(c) > Date(b) > Date(a))

Version 0.1

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.

To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/