Quick tour of Guillotina¶
Guillotina is powerful datastore, capable of storing and indexing millions of objects.
It is a high performance web server based on many of the technologies and lessons learned from Plone, Pyramid, Django and others all while utilizing Python’s great AsyncIO library.
Using Python’s AsyncIO, it works well with micro-service oriented environments.
Features:
- REST JSON API
- Built-in authentication/authorization, built-in JWT support
- Hierarchical data/url structure. Object storage.
- Permissions/roles/groups
- Fully customizable permission/roles/groups based on hierarchical data structure
- Robust customizable component architecture and configuration syntax
- Content types, dynamic behaviors, based on python interfaces and json schemas.
- Built-in CORS support
- Serialization/Validation library integrated.
- Elastic search integration through guillotina_elasticsearch, or fallback to PostgreSQL json indexing.
- Declarative configuration using decorators.
- Integrated cloud storage file uploads.
- py.test fixtures for easy service/api/endpoint testing
- Built-in command system to run jobs.
- Rich ecosystem of additional packages for adding additional features: Integration with RabbitMQ, batching of queries, redis cache layer.
- Powerful addon architecture based on Zope Component Architecture.
What is Guillotina like?¶
Example configuration:¶
---
applications:
- myapp
databases:
- db:
storage: postgresql
dsn:
scheme: postgres
dbname: guillotina
user: postgres
host: localhost
password: ''
port: 5432
port: 8080
root_user:
password: root
Example service:¶
See instructions below to play with.
from guillotina import configure
@configure.service(name="@foobar")
async def foobar(context, request):
return {"foo": "bar"}
Example content type:¶
See instructions below to play with.
from guillotina import configure
from guillotina import content
from guillotina import Interface
from guillotina import schema
class IMyType(Interface):
foobar = schema.TextLine()
@configure.contenttype(
type_name="MyType", schema=IMyType, behaviors=["guillotina.behaviors.dublincore.IDublinCore"]
)
class Foobar(content.Item):
pass
Example usage:¶
See instructions below to play with.
-
POST
/db/container/
¶ Create MyType
Example
http
POST /db/container HTTP/1.1 Accept: application/json Content-Type: application/json Host: localhost:8080 Authorization: Basic cm9vdDpyb290 { "@type": "MyType", "id": "foobar", "foobar": "foobar" }
curl
curl -i -X POST http://localhost:8080/db/container -H 'Accept: application/json' -H 'Content-Type: application/json' --data-raw '{"@type": "MyType", "foobar": "foobar", "id": "foobar"}' --user root:root
wget
wget -S -O- http://localhost:8080/db/container --header='Accept: application/json' --header='Content-Type: application/json' --post-data='{"@type": "MyType", "foobar": "foobar", "id": "foobar"}' --auth-no-challenge --user=root --password=root
httpie
echo '{ "@type": "MyType", "foobar": "foobar", "id": "foobar" }' | http POST http://localhost:8080/db/container Accept:application/json Content-Type:application/json -a root:root
python-requests
requests.post('http://localhost:8080/db/container', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'@type': 'MyType', 'foobar': 'foobar', 'id': 'foobar'}, auth=('root', 'root'))
response
HTTP/1.1 201 OK Content-Type: application/json
Request Headers: - Authorization – Required token to authenticate
Status Codes: - 201 Created – no error
- 401 Unauthorized – Invalid Auth code
- 500 Internal Server Error – Error processing request
-
GET
/db/container/foobar/
¶ Get MyType
Example
http
GET /db/container/foobar HTTP/1.1 Accept: application/json Host: localhost:8080 Authorization: Basic cm9vdDpyb290
curl
curl -i http://localhost:8080/db/container/foobar -H 'Accept: application/json' --user root:root
wget
wget -S -O- http://localhost:8080/db/container/foobar --header='Accept: application/json' --auth-no-challenge --user=root --password=root
httpie
http http://localhost:8080/db/container/foobar Accept:application/json -a root:root
python-requests
requests.get('http://localhost:8080/db/container/foobar', headers={'Accept': 'application/json'}, auth=('root', 'root'))
response
HTTP/1.1 200 OK Content-Length: 851 Content-Type: application/json { "@id": "http://localhost:8080/db/container/foobar", "@name": "foobar", "@type": "MyType", "@uid": "e3f|81c5406638bd4a68b89275f739fc18b2", "UID": "e3f|81c5406638bd4a68b89275f739fc18b2", "creation_date": "2018-07-21T13:14:15.245181+00:00", "foobar": "foobar", "guillotina.behaviors.dublincore.IDublinCore": { "contributors": [ "root" ], "creation_date": "2018-07-21T13:14:15.245181+00:00", "creators": [ "root" ], "description": null, "effective_date": null, "expiration_date": null, "modification_date": "2018-07-21T13:14:15.245181+00:00", "publisher": null, "tags": null, "title": null }, "is_folderish": false, "modification_date": "2018-07-21T13:14:15.245181+00:00", "parent": { "@id": "http://localhost:8080/db/container", "@name": "container", "@type": "Container", "@uid": "e3f4e401d12843a4a303666da4158458", "UID": "e3f4e401d12843a4a303666da4158458" } }
Request Headers: - Authorization – Required token to authenticate
Status Codes: - 200 OK – no error
- 401 Unauthorized – Invalid Auth code
- 500 Internal Server Error – Error processing request
-
POST
/db/@foobar
¶ Use foobar service
Example
http
POST /db/@foobar HTTP/1.1 Accept: application/json Host: localhost:8080 Authorization: Basic cm9vdDpyb290
curl
curl -i -X POST http://localhost:8080/db/@foobar -H 'Accept: application/json' --user root:root
wget
wget -S -O- http://localhost:8080/db/@foobar --header='Accept: application/json' --auth-no-challenge --user=root --password=root
httpie
http POST http://localhost:8080/db/@foobar Accept:application/json -a root:root
python-requests
requests.post('http://localhost:8080/db/@foobar', headers={'Accept': 'application/json'}, auth=('root', 'root'))
response
HTTP/1.1 201 OK Content-Type: application/json { "foo": "bar"}
or
http
POST /db/container/@foobar HTTP/1.1 Accept: application/json Host: localhost:8080 Authorization: Basic cm9vdDpyb290
curl
curl -i -X POST http://localhost:8080/db/container/@foobar -H 'Accept: application/json' --user root:root
wget
wget -S -O- http://localhost:8080/db/container/@foobar --header='Accept: application/json' --auth-no-challenge --user=root --password=root
httpie
http POST http://localhost:8080/db/container/@foobar Accept:application/json -a root:root
python-requests
requests.post('http://localhost:8080/db/container/@foobar', headers={'Accept': 'application/json'}, auth=('root', 'root'))
response
HTTP/1.1 201 OK Content-Type: application/json { "foo": "bar"}
or
http
POST /db/container/foobar/@foobar HTTP/1.1 Accept: application/json Host: localhost:8080 Authorization: Basic cm9vdDpyb290
curl
curl -i -X POST http://localhost:8080/db/container/foobar/@foobar -H 'Accept: application/json' --user root:root
wget
wget -S -O- http://localhost:8080/db/container/foobar/@foobar --header='Accept: application/json' --auth-no-challenge --user=root --password=root
httpie
http POST http://localhost:8080/db/container/foobar/@foobar Accept:application/json -a root:root
python-requests
requests.post('http://localhost:8080/db/container/foobar/@foobar', headers={'Accept': 'application/json'}, auth=('root', 'root'))
response
HTTP/1.1 201 OK Content-Type: application/json { "foo": "bar"}
Request Headers: - Authorization – Required token to authenticate
Status Codes: - 200 OK – no error
- 401 Unauthorized – Invalid Auth code
- 500 Internal Server Error – Error processing request
You can see that @foobar service is available on any endpoints.
Playing with those examples¶
To play with those examples you should install Guillotina and Cookiecutter, let’s do that in a python venv:
python3.7 -m venv .
source ./bin/activate
pip install guillotina cookiecutter
Use Guillotina templates to create an application:
./bin/g create --template=application
full_name []: My App
email []: guillotina@myapp.io
package_name [guillotina_myproject]: myapp
project_short_description [Guillotina server application python project]:
Select open_source_license:
1 - MIT license
2 - BSD license
3 - ISC license
4 - Apache Software License 2.0
5 - GNU General Public License v3
6 - Not open source
Choose from 1, 2, 3, 4, 5, 6 [1]:
You should now have a structure like the following one:
.
└── myapp
├── README.rst
├── config.yaml
├── myapp
│ ├── __init__.py
│ ├── api.py
│ └── install.py
└── setup.py
Now copy Example content type section content in myapp/myapp/content.py
.
Add configure.scan('myapp.content')
to myapp/myapp/__init__.py
includeme
function.
@foobar
service is already defined in myapp/mayapp/api.py
.
Then install myapp
:
pip install -e myapp
Edit myapp/config.yaml
to fit your needs, especially in term of db configuration.
And run guillotina with:
g serve -c myapp/config.yaml
Now create a container:
http
POST /db/ HTTP/1.1
Accept: application/json
Content-Type: application/json
Host: localhost:8080
Authorization: Basic cm9vdDpyb290
{
"@type": "Container",
"title": "Container 1",
"id": "container",
"description": "Description"
}
curl
curl -i -X POST http://localhost:8080/db/ -H 'Accept: application/json' -H 'Content-Type: application/json' --data-raw '{"@type": "Container", "description": "Description", "id": "container", "title": "Container 1"}' --user root:root
wget
wget -S -O- http://localhost:8080/db/ --header='Accept: application/json' --header='Content-Type: application/json' --post-data='{"@type": "Container", "description": "Description", "id": "container", "title": "Container 1"}' --auth-no-challenge --user=root --password=root
httpie
echo '{
"@type": "Container",
"description": "Description",
"id": "container",
"title": "Container 1"
}' | http POST http://localhost:8080/db/ Accept:application/json Content-Type:application/json -a root:root
python-requests
requests.post('http://localhost:8080/db/', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'@type': 'Container', 'description': 'Description', 'id': 'container', 'title': 'Container 1'}, auth=('root', 'root'))
response
HTTP/1.1 201 OK
Content-Type: application/json
You can now use all above examples.