spaceapi-server/README.md

119 lines
No EOL
3.8 KiB
Markdown

# SpaceAPI Server
[![pipeline status](https://gitlab.com/s3lph/spaceapi-server/badges/master/pipeline.svg)][master]
[![coverage report](https://gitlab.com/s3lph/spaceapi-server/badges/master/coverage.svg)][master]
A lightweight server for [SpaceAPI][spaceapi] endpoints. Includes support for pluggable templating, so dynamic content,
like sensor values, can be added.
## Dependencies
- Python 3 (>=3.6)
- [Bottle][pypi-bottle]
- [Jinja2][pypi-jinja2]
## License
[MIT License][mit]
## Usage
```bash
python -m spaceapi_server config.json
```
## Configuration
```json
{
"address": "::1", # The address to listen on.
"port": 8000, # The TCP port to listen on.
"server": "wsgiref", # The Bottle backend server to use.
"template": "template.json", # Path to the SpaceAPI response template file.
"plugins_dir": "plugins", # Path to the directory containing your plugins.
"plugins": {
# Plugin-specific configuration should go in here, e.g. `.plugins.sqlite.database`
}
}
```
## Serve a Static SpaceAPI Endpoint
Have a look at [SpaceAPI: Getting Started][spaceapi-getting-started]. If you only want to serve static content, your
`template.json` may consist of regular JSON data, which is served almost-as-is (once parsed and re-serialized).
## Add Dynamic Content
This example guides you through adding a dynamic `.state` property to your SpaceAPI endpoint. We'll use the following
(rather simple, and probably not too useful) data source: Check a certain file, and mark the space as open depending on
its existence.
1. Create a plugin to fetch the data. Let's name it `mybackend.py` and put in in our plugins directory:
```python
import os
from spaceapi_server import config, plugins
@plugins.template_function
def space_state():
# Get the plugin config dict
conf = config.get_plugin_config('mybackend')
# Get the filename
filename = conf.get('filename', '/var/space_state')
try:
# Get the file's properties
stat = os.stat(filename)
except FileNotFoundError:
# File doesn't exist, aka. space is closed
return {
'open': False
}
# File exists, aka. space is open. Also report the mtime as "last changed" timestamp
return {
'open': True,
'lastchange': int(stat.st_mtime)
}
```
The `@template_function` decorator registers the function as a callable in Jinja's globals. There's also
`@template_filter`, which registers a Jinja2 filter, and `@template_test`, which registers a test. For more
information on the Jinja2 templating engine, see [Jinja2][jinja].
2. Call the template function in your template:
```json
{
# ...
"state": "{{ space_state() }}"
# ...
}
```
Although the value for the `state` key is a string containing the Jinja2 template, the return value of your template
function is a complex data type, which will be inserted into the result as such. Please be aware that only the
first token of a templated string is used in the result. This limitation is caused by the way Jinja2 is (ab-) used.
3. Configure the server:
```json
# ...
"template": "template.json",
"plugins_dir": "plugins",
"plugins": {
"mybackend": {
"filename", "/var/space_state"
}
}
# ...
```
4. Start the server and query it.
[master]: https://gitlab.com/s3lph/spaceapi-server/commits/master
[spaceapi]: https://spaceapi.io/
[pypi-bottle]: https://pypi.org/project/bottle/
[pypi-jinja2]: https://pypi.org/project/Jinja2/
[mit]: https://gitlab.com/s3lph/spaceapi-server/blob/master/LICENSE
[spaceapi-getting-started]: https://spaceapi.io/getting-started/
[jinja]: https://jinja.palletsprojects.com/