-
Notifications
You must be signed in to change notification settings - Fork 4
API Proposal
OK, so this is the way I see a new web API for OSPy designed. I want it to be a proper, modern web-API built on the CRUD principle using JSON as data-container format. In HTML terms that means using the POST/GET/PUT/DELETE methods that mostly resemble Create, Read, Update and Delete. For more - [RFC-2616](HTTP Method definitions http://tools.ietf.org/html/rfc2616.html#section-9). These are the verbs (actions) that can be executed on nouns -- the objects of interest for us.
Method | Mapping |
---|---|
GET | Get / List object/s |
PUT | Update / modify object/s |
POST | Create new object/s |
DELETE | Delete object/s |
Before getting into detailing the meaty part some notes :
- Authentication -- I think we should stick to http basic (base64 encoded) over TLS (once known as SSL). Assuming that this is going to be an API, used from other software it's easy to add the auth headers always. We can also do cookie auth, though I'm note rally sure of it's usability in our case.
- Versioning -- It may be an overkill, but it's good design anyway. So API URLs should be in the format :
http(s)://[ip|name]:[port]/api/v1/......
This way we keep the API constant and stable for a certain version of the API and clients "speaking" that level should be able to work ok, even after there is a second, third and so on releases of the API. As I said, in this particular case this seems to me a bit of an overkill.
We have the following (coarsely defined) groups of "objects" currently :
- Programs
- Stations
- Options
- Logs
- System
I've heard people also call these 'collections' in the API world. So the general URL format becomes :
http(s)://[ip|name]:[port]/api/v1/[collection]/[object_id]/
Regarding object_id
s - we have these even now, though they are more of an implicit type as a result from (I think) array indexing in both OSPy and OS side. They should be explicit.
Since CRUD specifies how one acts on objects' definitions we need a way to implement actions with these objects. Meaning that stuff like "start this program now", "manual station control" and such, need a way to be cleanly implemented in the API. I propose the /?do=[action]
notation. See below for more info
TODO
{
"id": integer, Read-Only
"enabled": bool,
"name": string,
"type": string, one of ["days of week", "interval"]
"start" : string, HH:MM:SS format
"duration" : string, HH:MM:SS format
"scheduling: : string, one of ["single", "recurring"]
"repeat": string, HH:MM:SS format, recurring scheduling only
"end" : string, HH:MM:SS format, recurring scheduling only
"day_of_week": list of integers, days of the week the program needs to run on, week cycle programs only
"recur": string, "days:after", recur [days] days apart, staring on the [after]th day. [days]<30. interval programs only
"stations": list of integers, station_ids that will be triggered by the program
}
Returns a single station_info entry. Example :
GET
/programs/1
Returns (a weekly cycle, every week day, single pass program)
{
"id": 1,
"enabled": true,
"name": "Weekly demo program",
"type": "days of week",
"start" : "06:45:00",
"duration" : "00:20:00",
"scheduling: : "single",
"day_of_week": [1, 2, 3, 4, 5, 6, 7],
"stations": [1, 3, 6, 7]
}
GET
/programs/2
Returns (Interval cycle, repeat every 3 days,recurring every hour and 40 minutes until 20:30)
{
"id": 2,
"enabled": true,
"name": "Interval demo program",
"type": "interval",
"start" : "06:45:00",
"duration" : "00:20:00",
"scheduling: : "recurring",
"repeat" : "01:40:00",
"end" : "20:30:00",
"recur": "3:2",
"stations": [2, 4, 5, 11]
}
TODO
TODO
TODO
TODO
TODO
TODO
TODO
TODO
TODO
TODO
Single station is represented like :
{
"id": integer, Read-Only
"name": string,
"ignore_rain": bool,
"enabled": bool,
"is_master": bool,
"state": string, Read-Only
"reason": string Read-Only
}
Returns a single station_info entry. Example :
GET
/station/1
Returns
{
"id": 1,
"name": "Station One",
"ignore_rain": "false",
"enabled": "true",
"is_master": "true",
"state": "Running",
"reason": "Manual start"
}
This is a bit tricky since stations are "created" only multiples of 8 currently, due to the default shift register output "sub-system" and needs more thought, but in general a POST
takes a station_info
object as a parameter and adds it to the stations
collection
This is the "edit station" method. Takes a "station_info" object and updates station with id
station_id
. Example :
PUT
/station/1
{
"id": 1,
"name": "Station One With a Nice New Name",
"ignore_rain": "false",
"enabled": "true",
"is_master": "true"
}
Renames station 1 to "Station One With a Nice New Name".
See note for POST
.
Example:
DELETE
/station/1
Returns nothing.
Returns a list of station_info entries, e.g. :
{
"stations":[
{
"id": 1,
"name": "Station One",
"ignore_rain": "false",
"enabled": "true",
"is_master": "true"
},
{
"id": 2,
"name": "Station Two",
"ignore_rain": "false",
"enabled": "true",
"is_master": "false"
},
...
]
}
See note for /stations/id.
Not implemented
Not implemented, or maybe "reset to default" for all stations ?
Manually start station with id 1. Duration is specified :
POST
/stations/1/?do=start
{
"duration" : string, HH:MM:SS format
}
Example:
Manually stop station with id 1
POST
/stations/1/?do=start
{
"duration" : "00:15:00"
}
POST
/stations/1/?do=stop
OSPy options
{
"system_name": string,
"location": string,
"timezone": string,
"extension_boards": integer,
"http_port": integer,
"logs_enabled": bool,
...
}
Returns the current system options :
GET
/options
{
"system_name": "OSPy System",
"location": "Paris/France",
"timezone": "Europe/Paris",
"extension_boards": 1,
"http_port": 80,
...
}
Not implemented
PUT
/options
{
"system_name": "OSPy System",
"location": "Sahara Desert/Egypt",
"timezone": "Egypt/Cairo",
"extension_boards": 4,
"http_port": 8080,
...
}
Modify options
Not implemented
None
OSPy logs
Log entry
{
"date": ISO8601 formatted timestamp, Read-Only
"message": string, Read-Only
...
}
Returns the current system options. Example :
{
"logs": [
{
"date": "2014-09-20T01:35",
"message": "Station 3 started"
},
{
"date": "2014-09-20T02:10",
"message": "Station 3 stopped"
},
{
"date": "2014-09-20T01:40",
"message": "Very intense log message"
},
...
]
}
Not implemented
Not implemented
DELETE
`/logs'
Clears logs
None
OSPy System Data
System data
{
"version": string, Read-Only
"CPU_temp": string, Read-Only
...
}
These are system level data and options, that do not belong to /options
. Also system level actions are implemented on this resource.
Returns the current system options. Example
{
"version": "2.1.999 (2014-09-10)",
"cpu_temp": "1000 deg C",
...
}
Not implemented
Not implemented
Not implemented
Restart System :
POST
/system/?do=restart
Pull latest from git and restart :
POST
/system/?do=git_upgrade
Access point to everything plug-in related.
TODO: ? plugin repository, format, deployment structure...
plugin specific API calls go under
/plugins/plugin_id/<whatever the plugin adds>
{
"id": integer, Read-Only. A name maybe a better id in this particular case
"enabled": bool,
"name": string,
"description": string,
"status": string, plugin status as reported by the plugin's get_status() method
....
}
Returns a single plugin data. Example :
GET
/plugins/1
Returns
{
"id": 1
"enabled": true,
"name": "A mega cool plugin for OSPy",
"description": "World's most famous OSPy plugin"
"status": "Working, of course"
....
}
TODO
TODO
TODO
TODO
TODO
List of plugin_info as defined in GET /plugin/plugin_id
TODO
TODO
TODO
TODO