Using the Guillotina API

Before we start using the Guillotina API, let’s get us some test data to play with.

Using the testdata command, we’ll populate our database with some data from wikipedia.

g testdata --per-node=5 --depth=2 --container=container

Interacting with the API

You can use whatever you’d like but this training will mention use of Postman.

Open up Postman and do a GET on http://localhost:8080/db/container with the username root and password root for basic auth.

We can not necessarily go over every single API but will touch on a few and give a general understanding of how to explore and use the API.

Creating content

To create content, do a POST request on a container or folder object.

POST /(db)/(container)

Add new resouce inside this container resource

  • Permission: guillotina.AddContent
  • Context: guillotina.interfaces.content.IResource

http

POST /db/container HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290

{
    "@type": "Item",
    "id": "foobar5"
}

curl

curl -i -X POST http://nohost/db/container -H 'Accept: application/json' --data-raw '{
    "@type": "Item",
    "id": "foobar5"
}' --user root:root

httpie

echo '{
    "@type": "Item",
    "id": "foobar5"
}' | http POST http://nohost/db/container Accept:application/json -a root:root

response

HTTP/1.1 201 OK
Content-Type: application/json
Location: http://localhost/db/container/foobar5

{
    "@id": "http://localhost/db/container/foobar5",
    "@name": "foobar5",
    "@type": "Item",
    "@uid": "bf6|f13d2535731443b492102e832eee65df"
}
Status Codes:

Adding behaviors

To add a dynamic behavior, we use the @behavior endpoint.

PATCH /(db)/(container)/(content)/@behaviors

Add behavior to resource

  • Permission: guillotina.ModifyContent
  • Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar5/@behaviors HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290

{
    "behavior": "guillotina.behaviors.attachment.IAttachment"
}

curl

curl -i -X PATCH http://nohost/db/container/foobar5/@behaviors -H 'Accept: application/json' --data-raw '{
    "behavior": "guillotina.behaviors.attachment.IAttachment"
}' --user root:root

httpie

echo '{
    "behavior": "guillotina.behaviors.attachment.IAttachment"
}' | http PATCH http://nohost/db/container/foobar5/@behaviors Accept:application/json -a root:root

response

HTTP/1.1 200 OK
Content-Type: application/json

{}
Status Codes:

Uploading files

Simple file uploads can be done with the @upload endpoint.

PATCH /(db)/(container)/(content)/@upload/file
  • Permission: guillotina.ModifyContent
  • Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar5/@upload/file HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290

<text data>

curl

curl -i -X PATCH http://nohost/db/container/foobar5/@upload/file -H 'Accept: application/json' --data-raw '<text data>' --user root:root

httpie

echo '<text data>' | http PATCH http://nohost/db/container/foobar5/@upload/file Accept:application/json -a root:root

response

HTTP/1.1 200 OK
Content-Type: application/json

{}
Status Codes:

Then, to download the file, use the @download endpoint.

GET /(db)/(container)/(content)/@download/file
  • Permission: guillotina.ViewContent
  • Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar5/@download/file HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar5/@download/file -H 'Accept: application/json' --user root:root

httpie

http http://nohost/db/container/foobar5/@download/file Accept:application/json -a root:root

response

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Disposition: attachment; filename="f16fe4b800dd435aa2cb8e86d417e2f6"
Content-Length: 11
Content-Type: application/octet-stream

<text data>
Status Codes:

Uploading files with TUS

Guillotina also supports the TUS protocol using the @tusupload endpoint. The TUS protocol allows you to upload large files in chunks and allows you to have resumable uploads.

First, initialize the TUS upload with a POST

POST /(db)/(container)/(content)/@tusupload/file
  • Permission: guillotina.ModifyContent
  • Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar5/@tusupload/file HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290
TUS-RESUMABLE: 1
UPLOAD-LENGTH: 22

curl

curl -i -X POST http://nohost/db/container/foobar5/@tusupload/file -H 'Accept: application/json' -H 'Tus-Resumable: 1' -H 'Upload-Length: 22' --user root:root

httpie

http POST http://nohost/db/container/foobar5/@tusupload/file Accept:application/json Tus-Resumable:1 Upload-Length:22 -a root:root

response

HTTP/1.1 201 OK
Content-Type: application/json
Location: http://localhost/db/container/foobar5/@tusupload/file
Tus-Resumable: 1.0.0

{}
Status Codes:

Next, upload the chunks(here we’re doing chunks):

PATCH /(db)/(container)/(content)/@tusupload/file
  • Permission: guillotina.ModifyContent
  • Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar5/@tusupload/file HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290
TUS-RESUMABLE: 1
Upload-Offset: 0

<text data>

curl

curl -i -X PATCH http://nohost/db/container/foobar5/@tusupload/file -H 'Accept: application/json' -H 'Tus-Resumable: 1' -H 'Upload-Offset: 0' --data-raw '<text data>' --user root:root

httpie

echo '<text data>' | http PATCH http://nohost/db/container/foobar5/@tusupload/file Accept:application/json Tus-Resumable:1 Upload-Offset:0 -a root:root

response

HTTP/1.1 200 OK
Content-Type: application/json
Tus-Resumable: 1.0.0
Upload-Offset: 11

{}
Status Codes:

And final chunk:

PATCH /(db)/(container)/(content)/@tusupload/file
  • Permission: guillotina.ModifyContent
  • Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar5/@tusupload/file HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290
TUS-RESUMABLE: 1
Upload-Offset: 11

<text data>

curl

curl -i -X PATCH http://nohost/db/container/foobar5/@tusupload/file -H 'Accept: application/json' -H 'Tus-Resumable: 1' -H 'Upload-Offset: 11' --data-raw '<text data>' --user root:root

httpie

echo '<text data>' | http PATCH http://nohost/db/container/foobar5/@tusupload/file Accept:application/json Tus-Resumable:1 Upload-Offset:11 -a root:root

response

HTTP/1.1 200 OK
Content-Type: application/json
Tus-Resumable: 1.0.0
Tus-Upload-Finished: 1
Upload-Offset: 22

{}
Status Codes:

Unknown upload size

Guillotina’s TUS implementation has support for the Upload-Defer-Length header. This means you can upload files with an unknown final upload size.

In order to implement this correctly, you will need to provide the Upload-Defer-Length: 1 header and value on the initial POST to start the TUS upload. You are then not required to provide the UPLOAD-LENGTH header.

Then, before or on your last chunk, provide a UPLOAD-LENGTH value to let TUS know the upload can not finish.

Simultaneous TUS uploads

Guillotina’s TUS implementation also attempts to prevent simultaneous uploaders.

If two users attempt to start an upload on the same object + field at the same time, a 412 error will be thrown. Guillotina tracks upload activity to detect this. If there is no activity detected for 15 seconds with an unfinished TUS upload, no error is thrown.

To override this, send the TUS-OVERRIDE-UPLOAD: 1 header.

Modifying permissions

The @sharing endpoint is available to inspect and modify permissions on an object.

GET /(db)/(container)/(content)/@sharing

Get sharing settings for this resource

  • Permission: guillotina.SeePermissions
  • Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar5/@sharing HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar5/@sharing -H 'Accept: application/json' --user root:root

httpie

http http://nohost/db/container/foobar5/@sharing Accept:application/json -a root:root

response

HTTP/1.1 200 OK
Content-Type: application/json

{
    "local": {
        "roleperm": {},
        "prinperm": {},
        "prinrole": {
            "root": {
                "guillotina.Owner": "Allow"
            }
        }
    },
    "inherit": [
        {
            "@id": "http://localhost/db/container",
            "roleperm": {},
            "prinperm": {},
            "prinrole": {
                "root": {
                    "guillotina.ContainerAdmin": "Allow",
                    "guillotina.Owner": "Allow"
                }
            }
        }
    ]
}
Status Codes:

To modify, we use the same endpoint but with a POST.

POST /(db)/(container)/(content)/@sharing

Change permissions for a resource

  • Permission: guillotina.ChangePermissions
  • Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar5/@sharing HTTP/1.1
Accept: application/json
Authorization: Basic cm9vdDpyb290

{
    "prinperm": [
        {
            "principal": "foobar",
            "permission": "guillotina.ModifyContent",
            "setting": "Allow"
        }
    ]
}

curl

curl -i -X POST http://nohost/db/container/foobar5/@sharing -H 'Accept: application/json' --data-raw '{
    "prinperm": [
        {
            "principal": "foobar",
            "permission": "guillotina.ModifyContent",
            "setting": "Allow"
        }
    ]
}' --user root:root

httpie

echo '{
    "prinperm": [
        {
            "principal": "foobar",
            "permission": "guillotina.ModifyContent",
            "setting": "Allow"
        }
    ]
}' | http POST http://nohost/db/container/foobar5/@sharing Accept:application/json -a root:root

response

HTTP/1.1 200 OK
Content-Type: application/json

{}
Status Codes:

There are three types of permission settings you can modify:

  • prinperm: principal + permission
  • prinrole: principal + role
  • roleperm: role + permission

Each change can use the following settings:

  • Allow : you set it on the resource and the children will inherit
  • Deny : you set in on the resource and the children will inherit
  • AllowSingle : you set in on the resource and the children will not inherit
  • Unset : you remove the setting

Exploring the API with OpenAPI

In the previous step, we installed guillotina.contrib.swagger. With OpenAPI, we can inspect any context and explore the API.

Swagger

Visit http://localhost:8080/@docs

Authentication

Click the Authorize button

The top URL setting is what the current context is that you’re exploring the API on. If you create content at /db/container/foobar and want to explore that content’s API, you should change the URL. Different content types will have different services available.

References