release 4.2.11 ramon navarro bosch & nathan van gheem · guillotina documentation, release...

263
guillotina Documentation Release 4.2.11 Ramon Navarro Bosch & Nathan Van Gheem Oct 24, 2018

Upload: ngohanh

Post on 28-Oct-2018

230 views

Category:

Documents


1 download

TRANSCRIPT

guillotina DocumentationRelease 4.2.11

Ramon Navarro Bosch & Nathan Van Gheem

Oct 24, 2018

Contents

1 Getting Started 3

2 REST API 5

3 Narrative Developer Documentation 113

4 Deploying 167

5 Programming API Reference 169

6 Training 191

7 Why Guillotina 225

8 About 227

Python Module Index 253

HTTP Routing Table 255

i

ii

guillotina Documentation, Release 4.2.11

Guillotina is the only full-featured Python AsyncIO REST Resource Application Server designed for high-performance, horizontally scaling solutions.

Contents 1

guillotina Documentation, Release 4.2.11

2 Contents

CHAPTER 1

Getting Started

Are you new to Guillotina? This is the place to start!

• Quick tour of Guillotina gives an overview of the major features in Guillotina, covering a little about a lot.

• To learn more, go to the training section.

• For help getting Guillotina set up, try Installing Guillotina.

• Need help? Join our Gitter channel.

3

guillotina Documentation, Release 4.2.11

4 Chapter 1. Getting Started

CHAPTER 2

REST API

After you’re up and running, primarily, Guillotina provides a REST API to work with and it is what you should becomethe most familiar with.

Guillotina API structure mirrors the object tree structure. Within the object tree structure, there are four major typesof objects you’ll want to be familiar with:

• Application: The root of the tree: /

• Database: A configured database: /(db)

• Container: An main object to add data to: /(db)/(container)

• Content: Item or Folder by default. This is your dynamic object tree you create

The endpoints available around these objects are detailed below:

2.1 Application

GET GETGet application data

Retrieves serialization of application

• Permission: guillotina.AccessContent

• Context: guillotina.interfaces.content.IApplication

http

GET / HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

5

guillotina Documentation, Release 4.2.11

curl -i http://nohost/ -H 'Accept: application/json' --user root:root

httpie

http http://nohost/ Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 88Content-Type: application/json

{"databases": [

"db"],"static_file": [],"static_directory": [],"@type": "Application"

}

Status Codes

• 200 OK – Application data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET GETGet API Definition

Retrieves information on API configuration

• Permission: guillotina.GetContainers

• Context: guillotina.interfaces.content.IApplication

http

GET /@apidefinition HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/@apidefinition -H 'Accept: application/json' --user→˓root:root

httpie

http http://nohost/@apidefinition Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 34909Content-Type: application/json

6 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

{"guillotina.interfaces.content.IContainer": {

"endpoints": {"@addons": {

"POST": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "POST","permission": "guillotina.ManageAddons","name": "@addons","summary": "Install addon to container","parameters": [

{"name": "body","in": "body","schema": {

"$ref": "#/definitions/Addon"}

}],"module": "guillotina.api.addons.install"

},"DELETE": {

"context": ["guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.ManageAddons","name": "@addons","summary": "Uninstall an addon from container","parameters": [

{"name": "body","in": "body","schema": {

"$ref": "#/definitions/Addon"}

}],"module": "guillotina.api.addons.uninstall"

},"GET": {

"context": ["guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource",

2.1. Application 7

guillotina Documentation, Release 4.2.11

"guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "GET","permission": "guillotina.ManageAddons","name": "@addons","summary": "List available addons","responses": {

"200": {"description": "Get list of available and installed

→˓addons","schema": {

"$ref": "#/definitions/AddonResponse"}

}},"module": "guillotina.api.addons.get_addons"

}},"@addons/{addon}": {

"DELETE": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.ManageAddons","name": "@addons/{addon}","summary": "Uninstall an addon from container","parameters": [

{"name": "addon","in": "path"

}],"module": "guillotina.api.addons.uninstall_path"

}},"@resolveuid/{uid}": {

"GET": {"method": "GET","name": "@resolveuid/{uid}","context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

8 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

],"permission": "guillotina.AccessContent","summary": "Get content by UID","responses": {

"200": {"description": "Successful"

}},"module": "guillotina.api.content.resolve_uid"

}},"@login": {

"POST": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "POST","permission": "guillotina.Public","name": "@login","summary": "Components for a resource","allow_access": true,"module": "guillotina.api.login.Login"

}},"@login-renew": {

"POST": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "POST","permission": "guillotina.AccessContent","name": "@login-renew","summary": "Refresh to a new token","module": "guillotina.api.login.Refresh"

}},"@registry/{key}": {

"GET": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

2.1. Application 9

guillotina Documentation, Release 4.2.11

],"method": "GET","permission": "guillotina.ReadConfiguration","name": "@registry/{key}","summary": "Read container registry settings","responses": {

"200": {"description": "Successfully registered interface","type": "object","schema": {

"properties": {"value": {

"type": "object"}

}}

}},"module": "guillotina.api.registry.Read"

}},"@registry": {

"GET": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "GET","permission": "guillotina.ReadConfiguration","name": "@registry","summary": "Read container registry settings","responses": {

"200": {"description": "Successfully registered interface","type": "object","schema": {

"properties": {"value": {

"type": "object"}

}}

}},"module": "guillotina.api.registry.get_registry"

},"POST": {

"context": ["guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable",

10 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "POST","permission": "guillotina.RegisterConfigurations","name": "@registry","summary": "Register a new interface to for registry settings

→˓","parameters": [

{"name": "body","in": "body","type": "object","schema": {

"properties": {"interface": {

"type": "string","required": true

},"initial_values": {

"type": "object","required": false

}}

}}

],"responses": {

"200": {"description": "Successfully registered interface"

}},"module": "guillotina.api.registry.Register"

}},"@registry/{dotted_name}": {

"PATCH": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "PATCH","permission": "guillotina.WriteConfiguration","name": "@registry/{dotted_name}","summary": "Update registry setting","parameters": {

"name": "body","in": "body","type": "object","schema": {

"properties": {"value": {

"type": "any",

2.1. Application 11

guillotina Documentation, Release 4.2.11

"required": true}

}}

},"responses": {

"200": {"description": "Successfully wrote configuration"

}},"module": "guillotina.api.registry.Write"

}},"@types": {

"GET": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","name": "@types","summary": "Read information on available types","responses": {

"200": {"description": "Result results on types","schema": {

"properties": {}}

}},"module": "guillotina.api.types.get_all_types"

}},"@types/{type_name}": {

"GET": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","name": "@types/{type_name}","summary": "Read information on available types","responses": {

"200": {"description": "Result results on types","schema": {

12 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"properties": {}}

}},"module": "guillotina.api.types.Read"

}},"@user": {

"GET": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","name": "@user","summary": "Get information on the currently logged in user","responses": {

"200": {"description": "Get information on the user","schema": {

"properties": {}}

}},"module": "guillotina.api.user.get_user_info"

}},"@wstoken": {

"GET": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","name": "@wstoken","summary": "Return a web socket token","responses": {

"200": {"description": "The new token","schema": {

"properties": {"token": {

"type": "string","required": true

}}

2.1. Application 13

guillotina Documentation, Release 4.2.11

}}

},"module": "guillotina.api.ws.WebsocketGetToken"

}},"@ws": {

"GET": {"context": [

"guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","name": "@ws","summary": "Make a web socket connection","module": "guillotina.api.ws.WebsocketsView"

}}

},"DELETE": {

"context": ["guillotina.interfaces.content.IContainer","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","guillotina.component.interfaces.ISite","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.DeleteContainers","summary": "Delete container","module": "guillotina.api.container.DefaultDELETE"

}},"guillotina.interfaces.content.IApplication": {

"GET": {"context": [

"guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","summary": "Get application data","description": "Retrieves serialization of application","responses": {

"200": {"description": "Application data","schema": {

"$ref": "#/definitions/Application"

14 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

}}

},"module": "guillotina.api.app.get"

},"endpoints": {

"@apidefinition": {"GET": {

"context": ["guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "GET","permission": "guillotina.GetContainers","name": "@apidefinition","summary": "Get API Definition","description": "Retrieves information on API configuration","module": "guillotina.api.app.get_api_definition"

}},"@component-subscribers": {

"GET": {"context": [

"guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "GET","name": "@component-subscribers","permission": "guillotina.ReadConfiguration","summary": "Get all registered subscribers","module": "guillotina.api.app.get_all_subscribers"

}},"@storages": {

"GET": {"context": [

"guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "GET","permission": "guillotina.GetDatabases","name": "@storages","module": "guillotina.api.storage.storages_get"

}},"@storages/{storage_id}": {

"GET": {"context": [

"guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

2.1. Application 15

guillotina Documentation, Release 4.2.11

],"method": "GET","permission": "guillotina.GetDatabases","name": "@storages/{storage_id}","module": "guillotina.api.storage.storage_get"

},"POST": {

"context": ["guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "POST","permission": "guillotina.MountDatabase","name": "@storages/{storage_id}","module": "guillotina.api.storage.storage_create_db"

}},"@storages/{storage_id}/{db_id}": {

"DELETE": {"context": [

"guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.UmountDatabase","name": "@storages/{storage_id}/{db_id}","module": "guillotina.api.storage.delete_db"

},"GET": {

"context": ["guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "GET","permission": "guillotina.GetDatabases","name": "@storages/{storage_id}/{db_id}","module": "guillotina.api.storage.get_db"

}}

},"PUT": {

"context": ["guillotina.interfaces.content.IApplication","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "PUT","permission": "guillotina.MountDatabase","ignore": true,"module": "guillotina.api.container.NotImplemented"

}

16 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

},"guillotina.interfaces.content.IResource": {

"endpoints": {"@behaviors": {

"PATCH": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "PATCH","permission": "guillotina.ModifyContent","name": "@behaviors","summary": "Add behavior to resource","parameters": [

{"name": "body","in": "body","schema": {

"$ref": "#/definitions/Behavior"}

}],"responses": {

"200": {"description": "Successfully added behavior"

},"412": {

"description": "Behavior already assigned here"}

},"module": "guillotina.api.behaviors.default_patch"

},"DELETE": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.ModifyContent","name": "@behaviors","summary": "Remove behavior from resource","parameters": [

{"name": "body","in": "body","schema": {

"$ref": "#/definitions/Behavior"}

}],"responses": {

"200": {"description": "Successfully removed behavior"

},"412": {

"description": "Behavior not assigned here"

2.1. Application 17

guillotina Documentation, Release 4.2.11

}},"module": "guillotina.api.behaviors.default_delete"

},"GET": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","name": "@behaviors","summary": "Get information on behaviors for this resource","responses": {

"200": {"description": "A listing of behaviors for content","schema": {

"$ref": "#/definitions/BehaviorsResponse"}

}},"module": "guillotina.api.behaviors.default_get"

}},"@behaviors/{behavior}": {

"DELETE": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.ModifyContent","name": "@behaviors/{behavior}","summary": "Remove behavior from resource","parameters": [

{"name": "behavior","in": "path","schema": {

"$ref": "#/definitions/Behavior"}

}],"responses": {

"200": {"description": "Successfully removed behavior"

},"412": {

"description": "Behavior not assigned here"}

},"module": "guillotina.api.behaviors.default_delete_withparams"

}},"@sharing": {

"GET": {

18 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "GET","permission": "guillotina.SeePermissions","name": "@sharing","summary": "Get sharing settings for this resource","responses": {

"200": {"description": "All the sharing defined on this

→˓resource","schema": {

"$ref": "#/definitions/ResourceACL"}

}},"module": "guillotina.api.content.sharing_get"

},"POST": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "POST","permission": "guillotina.ChangePermissions","name": "@sharing","summary": "Change permissions for a resource","parameters": [

{"name": "body","in": "body","type": "object","schema": {

"$ref": "#/definitions/Permissions"}

}],"responses": {

"200": {"description": "Successfully changed permission"

}},"module": "guillotina.api.content.SharingPOST"

},"PUT": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "PUT","permission": "guillotina.ChangePermissions","name": "@sharing","summary": "Replace permissions for a resource","parameters": [

2.1. Application 19

guillotina Documentation, Release 4.2.11

{"name": "body","in": "body","type": "object","schema": {

"$ref": "#/definitions/Permissions"}

}],"responses": {

"200": {"description": "Successfully replaced permissions"

}},"module": "guillotina.api.content.SharingPUT"

}},"@all_permissions": {

"GET": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "GET","permission": "guillotina.SeePermissions","name": "@all_permissions","summary": "See all permission settings for this resource","responses": {

"200": {"description": "All the permissions defined on this

→˓resource","schema": {

"$ref": "#/definitions/AllPermissions"}

}},"module": "guillotina.api.content.all_permissions"

}},"@canido": {

"GET": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","name": "@canido","summary": "Check if user has permissions on context","parameters": [

{"name": "permission","in": "query","required": true,"type": "string"

}

20 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

],"responses": {

"200": {"description": "Successfully changed permission"

}},"module": "guillotina.api.content.can_i_do"

}},"@move": {

"POST": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "POST","name": "@move","permission": "guillotina.MoveContent","summary": "Move resource","parameters": [

{"name": "body","in": "body","type": "object","schema": {

"properties": {"destination": {

"type": "string","description": "Absolute path to

→˓destination object from container","required": false

},"new_id": {

"type": "string","description": "Optional new id to assign

→˓object","required": false

}}

}}

],"responses": {

"200": {"description": "Successfully moved resource"

}},"module": "guillotina.api.content.move"

}},"@duplicate": {

"POST": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],

2.1. Application 21

guillotina Documentation, Release 4.2.11

"method": "POST","name": "@duplicate","permission": "guillotina.DuplicateContent","summary": "Duplicate resource","parameters": [

{"name": "body","in": "body","type": "object","schema": {

"properties": {"destination": {

"type": "string","description": "Absolute path to

→˓destination object from container","required": false

},"new_id": {

"type": "string","description": "Optional new id to assign

→˓object","required": false

}}

}}

],"responses": {

"200": {"description": "Successfully duplicated object"

}},"module": "guillotina.api.content.duplicate"

}},"@dynamic-fields": {

"GET": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "GET","name": "@dynamic-fields","permission": "guillotina.ModifyContent","summary": "Get a list of available fields","module": "guillotina.api.dynamic.available_dynamic_fields"

}},"@upload/{field_name}": {

"PATCH": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "PATCH","permission": "guillotina.ModifyContent",

22 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"name": "@upload/{field_name}","traversed_service_definitions": {

"{field_name}": {"summary": "Update the content of a file","parameters": [

{"name": "field_name","in": "path","description": "Name of file field","required": true

}],"responses": {

"200": {"description": "Successfully updated content"

}}

}},"module": "guillotina.api.files.UploadFile"

}},"@download/{field_name}/{filename}": {

"GET": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "GET","permission": "guillotina.ViewContent","name": "@download/{field_name}/{filename}","traversed_service_definitions": {

"{field_name}": {"summary": "Download the content of a file","parameters": [

{"name": "field_name","in": "path","description": "Name of file field","required": true

}],"responses": {

"200": {"description": "Successfully updated content"

}}

}},"module": "guillotina.api.files.DownloadFile"

}},"@download/{field_name}": {

"GET": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation",

2.1. Application 23

guillotina Documentation, Release 4.2.11

"zope.interface.Interface"],"method": "GET","permission": "guillotina.ViewContent","name": "@download/{field_name}","traversed_service_definitions": {

"{field_name}": {"summary": "Download the content of a file","parameters": [

{"name": "field_name","in": "path","description": "Name of file field","required": true

}],"responses": {

"200": {"description": "Successfully updated content"

}}

}},"module": "guillotina.api.files.DownloadFile"

}},"@tusupload/{field_name}": {

"POST": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "POST","permission": "guillotina.ModifyContent","name": "@tusupload/{field_name}","traversed_service_definitions": {

"{field_name}": {"summary": "TUS endpoint","parameters": [

{"name": "field_name","in": "path","description": "Name of file field","required": true

},{

"name": "Upload-Offset","in": "headers","type": "integer","required": true

},{

"name": "UPLOAD-LENGTH","in": "headers","type": "integer","required": true

},

24 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

{"name": "UPLOAD-MD5","in": "headers","type": "string","required": false

},{

"name": "UPLOAD-EXTENSION","in": "headers","type": "string","required": false

},{

"name": "TUS-RESUMABLE","in": "headers","type": "string","required": true

},{

"name": "UPLOAD-METADATA","in": "headers","type": "string","required": false

}],"responses": {

"204": {"description": "Successfully patched data","headers": {

"Location": {"type": "string"

},"Tus-Resumable": {

"type": "string"},"Access-Control-Expose-Headers": {

"type": "string"}

}}

}}

},"module": "guillotina.api.files.TusCreateFile"

},"HEAD": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "HEAD","permission": "guillotina.ModifyContent","name": "@tusupload/{field_name}","traversed_service_definitions": {

"{field_name}": {"summary": "TUS endpoint","parameters": [

2.1. Application 25

guillotina Documentation, Release 4.2.11

{"name": "field_name","in": "path","description": "Name of file field","required": true

}],"responses": {

"200": {"description": "Successfully patched data","headers": {

"Upload-Offset": {"type": "integer"

},"Tus-Resumable": {

"type": "string"},"Access-Control-Expose-Headers": {

"type": "string"}

}}

}}

},"module": "guillotina.api.files.TusHeadFile"

},"PATCH": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "PATCH","permission": "guillotina.ModifyContent","name": "@tusupload/{field_name}","traversed_service_definitions": {

"{field_name}": {"summary": "TUS endpoint","parameters": [

{"name": "field_name","in": "path","description": "Name of file field","required": true

},{

"name": "Upload-Offset","in": "headers","type": "integer","required": true

},{

"name": "CONTENT-LENGTH","in": "headers","type": "integer","required": true

}

26 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

],"responses": {

"204": {"description": "Successfully patched data","headers": {

"Upload-Offset": {"type": "integer"

}}

}}

}},"module": "guillotina.api.files.TusPatchFile"

},"OPTIONS": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "OPTIONS","permission": "guillotina.AccessPreflight","name": "@tusupload/{field_name}","traversed_service_definitions": {

"{field_name}": {"summary": "TUS endpoint","parameters": [

{"name": "field_name","in": "path","description": "Name of file field","required": true

}],"responses": {

"200": {"description": "Successfully returned tus info

→˓","headers": {

"Tus-Version": {"type": "string"

},"Tus-Resumable": {

"type": "string"},"Tus-Max-Size": {

"type": "integer"},"Tus-Extension": {

"type": "string"}

}}

}}

},"module": "guillotina.api.files.TusOptionsFile"

2.1. Application 27

guillotina Documentation, Release 4.2.11

}},"@search": {

"GET": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "GET","permission": "guillotina.SearchContent","name": "@search","summary": "Make search request","parameters": [

{"name": "q","in": "query","required": true,"type": "string"

}],"responses": {

"200": {"description": "Search results","type": "object","schema": {

"$ref": "#/definitions/SearchResults"}

}},"module": "guillotina.api.search.search_get"

},"POST": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "POST","permission": "guillotina.RawSearchContent","name": "@search","summary": "Make a complex search query","parameters": [

{"name": "body","in": "body","schema": {

"properties": {}}

}],"responses": {

"200": {"description": "Search results","type": "object","schema": {

"$ref": "#/definitions/SearchResults"}

28 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

}},"module": "guillotina.api.search.search_post"

}},"@catalog-reindex": {

"POST": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "POST","permission": "guillotina.ReindexContent","name": "@catalog-reindex","summary": "Reindex entire container content","responses": {

"200": {"description": "Successfully reindexed content"

}},"module": "guillotina.api.search.CatalogReindex"

}},"@async-catalog-reindex": {

"POST": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "POST","permission": "guillotina.ReindexContent","name": "@async-catalog-reindex","summary": "Asynchronously reindex entire container content","responses": {

"200": {"description": "Successfully initiated reindexing"

}},"module": "guillotina.api.search.AsyncCatalogReindex"

}},"@catalog": {

"POST": {"context": [

"guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "POST","permission": "guillotina.ManageCatalog","name": "@catalog","summary": "Initialize catalog","responses": {

"200": {"description": "Successfully initialized catalog"

}

2.1. Application 29

guillotina Documentation, Release 4.2.11

},"module": "guillotina.api.search.catalog_post"

},"DELETE": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.ManageCatalog","name": "@catalog","summary": "Delete search catalog","responses": {

"200": {"description": "Successfully deleted catalog"

}},"module": "guillotina.api.search.catalog_delete"

}}

},"HEAD": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "HEAD","permission": "guillotina.ViewContent","module": "guillotina.api.content.default_head"

},"GET": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "GET","permission": "guillotina.ViewContent","summary": "Retrieves serialization of resource","responses": "guillotina.api.content.get_content_json_schema_responses

→˓","parameters": [

{"name": "include","in": "query","type": "string","description": ""

},{

"name": "omit","in": "query","type": "string","description": ""

}],"module": "guillotina.api.content.DefaultGET"

30 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

},"POST": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "POST","permission": "guillotina.AddContent","summary": "Add new resouce inside this container resource","parameters": [

{"name": "body","in": "body","schema": {

"$ref": "#/definitions/AddableResource"}

}],"responses": {

"200": {"description": "Resource data","schema": {

"$ref": "#/definitions/ResourceFolder"}

}},"module": "guillotina.api.content.DefaultPOST"

},"PATCH": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "PATCH","permission": "guillotina.ModifyContent","summary": "Modify the content of this resource","parameters": "guillotina.api.content.patch_content_json_schema_

→˓parameters","responses": {

"200": {"description": "Resource data","schema": {

"$ref": "#/definitions/Resource"}

}},"module": "guillotina.api.content.DefaultPATCH"

},"PUT": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "PUT","permission": "guillotina.ModifyContent",

2.1. Application 31

guillotina Documentation, Release 4.2.11

"summary": "Replace the content of this resource","parameters": "guillotina.api.content.patch_content_json_schema_

→˓parameters","responses": {

"200": {"description": "Resource data","schema": {

"$ref": "#/definitions/Resource"}

}},"module": "guillotina.api.content.DefaultPUT"

},"DELETE": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.DeleteContent","summary": "Delete resource","responses": {

"200": {"description": "Successfully deleted resource"

}},"module": "guillotina.api.content.DefaultDELETE"

},"OPTIONS": {

"context": ["guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","zope.interface.Interface"

],"method": "OPTIONS","permission": "guillotina.AccessPreflight","summary": "Get CORS information for resource","module": "guillotina.api.content.DefaultOPTIONS"

}},"guillotina.interfaces.content.IFolder": {

"endpoints": {"@ids": {

"GET": {"context": [

"guillotina.interfaces.content.IFolder","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","zope.interface.Interface"

],"method": "GET","name": "@ids","permission": "guillotina.Manage","summary": "Return a list of ids in the resource","responses": {

32 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"200": {"description": "Successfully returned list of ids"

}},"module": "guillotina.api.content.ids"

}},"@items": {

"GET": {"context": [

"guillotina.interfaces.content.IFolder","guillotina.interfaces.content.IResource","guillotina.interfaces.content.ILocation","guillotina.interfaces.content.IAsyncContainer","guillotina.interfaces.content.ITraversable","zope.interface.Interface"

],"method": "GET","name": "@items","permission": "guillotina.Manage","summary": "Paginated list of sub objects","parameters": [

{"name": "include","in": "query","type": "string"

},{

"name": "omit","in": "query","type": "string"

},{

"name": "page_size","in": "query","type": "number","default": 20

},{

"name": "page","in": "query","type": "number","default": 1

}],"responses": {

"200": {"description": "Successfully returned response object"

}},"module": "guillotina.api.content.items"

}}

}},"guillotina.interfaces.content.IAsyncContainer": {

"endpoints": {"@addable-types": {

2.1. Application 33

guillotina Documentation, Release 4.2.11

"GET": {"context": [

"guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "GET","name": "@addable-types","permission": "guillotina.AddContent","summary": "Return a list of type names that can be added to

→˓container","responses": {

"200": {"description": "Successfully returned list of type

→˓names"}

},"module": "guillotina.api.content.addable_types"

}}

}},"zope.interface.Interface": {

"endpoints": {"@invalidate-cache": {

"GET": {"method": "GET","name": "@invalidate-cache","permission": "guillotina.ModifyContent","summary": "Invalidate cache of object","responses": {

"200": {"description": "Successfully invalidated"

}},"module": "guillotina.api.content.invalidate_cache"

}}

}},"guillotina.interfaces.content.IDatabase": {

"GET": {"context": [

"guillotina.interfaces.content.IDatabase","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "GET","permission": "guillotina.GetContainers","summary": "Get list of containers","responses": {

"200": {"description": "Get a list of containers","schema": {

"properties": {"containers": {

"type": "array","items": {

34 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"type": "string"}

}}

}}

},"module": "guillotina.api.container.DefaultGET"

},"POST": {

"context": ["guillotina.interfaces.content.IDatabase","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "POST","permission": "guillotina.AddContainer","summary": "Create a new Container","description": "Creates a new container on the database","parameters": [

{"name": "body","in": "body","schema": {

"$ref": "#/definitions/BaseResource"}

}],"responses": {

"200": {"description": "Container result","schema": {

"$ref": "#/definitions/BaseResource"}

}},"module": "guillotina.api.container.DefaultPOST"

},"DELETE": {

"context": ["guillotina.interfaces.content.IDatabase","guillotina.interfaces.content.ITraversable","guillotina.interfaces.content.IAsyncContainer","zope.interface.Interface"

],"method": "DELETE","permission": "guillotina.UmountDatabase","ignore": true,"module": "guillotina.api.container.NotImplemented"

}},"guillotina.interfaces.content.IStaticFile": {

"GET": {"context": [

"guillotina.interfaces.content.IStaticFile","zope.interface.Interface"

],

2.1. Application 35

guillotina Documentation, Release 4.2.11

"method": "GET","permission": "guillotina.AccessContent","module": "guillotina.api.files.FileGET"

}},"guillotina.interfaces.content.IStaticDirectory": {

"GET": {"context": [

"guillotina.interfaces.content.IStaticDirectory","guillotina.interfaces.content.ITraversable","zope.interface.Interface"

],"method": "GET","permission": "guillotina.AccessContent","module": "guillotina.api.files.DirectoryGET"

}}

}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET GETGet all registered subscribers

• Permission: guillotina.ReadConfiguration

• Context: guillotina.interfaces.content.IApplication

http

GET /@component-subscribers HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/@component-subscribers -H 'Accept: application/json' --user→˓root:root

httpie

http http://nohost/@component-subscribers Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 814Content-Type: application/json

{"guillotina.interfaces.content.IResource": {

"guillotina.interfaces.events.IObjectPermissionsModifiedEvent": ["guillotina.catalog.index.security_changed"

],"guillotina.interfaces.events.IObjectMovedEvent": [

36 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"guillotina.catalog.index.moved_object"],"guillotina.interfaces.events.IObjectRemovedEvent": [

"guillotina.catalog.index.remove_object"],"guillotina.interfaces.events.IObjectModifiedEvent": [

"guillotina.catalog.index.add_object","guillotina.subscribers.modified_object"

],"guillotina.interfaces.events.IObjectAddedEvent": [

"guillotina.catalog.index.add_object"]

},"guillotina.interfaces.content.IContainer": {

"guillotina.interfaces.events.IObjectAddedEvent": ["guillotina.catalog.index.initialize_catalog"

],"guillotina.interfaces.events.IObjectRemovedEvent": [

"guillotina.catalog.index.remove_catalog"]

}}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.2 Database

POST /(db)Create a new Container

Creates a new container on the database

• Permission: guillotina.AddContainer

• Context: guillotina.interfaces.content.IDatabase

http

POST /db HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"@type": "Container","id": "container"

}

curl

curl -i -X POST http://nohost/db -H 'Accept: application/json' -H 'Content-Type:→˓application/json' --data-raw '{"@type": "Container", "id": "container"}' --user→˓root:root

2.2. Database 37

guillotina Documentation, Release 4.2.11

httpie

echo '{"@type": "Container","id": "container"

}' | http POST http://nohost/db Accept:application/json Content-Type:application/→˓json -a root:root

response

HTTP/1.1 200 OKContent-Length: 63Content-Type: application/jsonLocation: /db/container

{"@type": "Container","id": "container","title": "container"

}

Status Codes

• 200 OK – Container result

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)Get list of containers

• Permission: guillotina.GetContainers

• Context: guillotina.interfaces.content.IDatabase

http

GET /db HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db -H 'Accept: application/json' --user root:root

httpie

http http://nohost/db Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 50Content-Type: application/json

{"containers": [

"container"],

38 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"@type": "Database"}

Status Codes

• 200 OK – Get a list of containers

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.3 Container

GET /(db)/container Retrieves serialization of resource

• Permission: guillotina.ViewContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

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

httpie

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

response

HTTP/1.1 200 OKContent-Length: 512Content-Type: application/json

{"@id": "http://127.0.0.1:51723/db/container","@type": "Container","@name": "container","@uid": "e17d899460e442e1a1aa4d769eee798b","@static_behaviors": [],"parent": {},"is_folderish": true,"creation_date": "2018-10-24T20:12:23.082300+00:00","modification_date": "2018-10-24T20:12:23.082300+00:00","UID": "e17d899460e442e1a1aa4d769eee798b","type_name": "Container","title": "container","uuid": "e17d899460e442e1a1aa4d769eee798b","__behaviors__": [],"__name__": "container","items": [],

2.3. Container 39

guillotina Documentation, Release 4.2.11

"length": 0}

Query Parameters

• include (string) –

• omit (string) –

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.3.1 Types

GET /(db)/container/@types Read information on available types

• Permission: guillotina.AccessContent

• Context: guillotina.interfaces.content.IContainer

http

GET /db/container/@types HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/@types -H 'Accept: application/json' --user→˓root:root

httpie

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

response

HTTP/1.1 200 OKContent-Length: 6047Content-Type: application/json

[{

"title": "Item","$schema": "http://json-schema.org/draft-04/schema#","type": "object","required": [

"type_name","uuid"

],"definitions": {

"guillotina.behaviors.dublincore.IDublinCore": {"type": "object",

40 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"properties": {"title": {

"type": "string","description": "The first unqualified Dublin Core 'Title'

→˓element value.","title": "Title"

},"description": {

"type": "string","description": "The first unqualified Dublin Core

→˓'Description' element value.","title": "Description"

},"creation_date": {

"type": "datetime","description": "The date and time that an object is

→˓created. \nThis is normally set automatically.","title": "Creation Date"

},"modification_date": {

"type": "datetime","description": "The date and time that the object was

→˓last modified in a\nmeaningful way.","title": "Modification Date"

},"effective_date": {

"type": "datetime","description": "The date and time that an object should

→˓be published. ","title": "Effective Date"

},"expiration_date": {

"type": "datetime","description": "The date and time that the object should

→˓become unpublished.","title": "Expiration Date"

},"creators": {

"type": "array","description": "The unqualified Dublin Core 'Creator'

→˓element values","title": "Creators","items": {

"type": "string"}

},"tags": {

"type": "array","description": "The unqualified Dublin Core 'Tags'

→˓element values","title": "Tags","items": {

"type": "string"}

},"publisher": {

"type": "string","description": "The first unqualified Dublin Core

→˓'Publisher' element value.",

2.3. Container 41

guillotina Documentation, Release 4.2.11

"title": "Publisher"},"contributors": {

"type": "array","description": "The unqualified Dublin Core 'Contributor'

→˓element values","title": "Contributors","items": {

"type": "string"}

}},"required": [],"invariants": [],"title": "Dublin Core fields","description": ""

}},"properties": {

"__name__": {"type": "string","description": "The object can be looked up from the parent's

→˓sublocations using this name.","readonly": true,"title": "The name within the parent"

},"type_name": {

"type": "string","readonly": true

},"title": {

"type": "string","description": "Title of the Resource","title": "Title"

},"uuid": {

"type": "string","readonly": true,"title": "UUID"

},"modification_date": {

"type": "datetime","title": "Modification date"

},"creation_date": {

"type": "datetime","title": "Creation date"

},"__behaviors__": {

"type": "array","description": "Dynamic behaviors for the content type","readonly": true,"title": "Enabled behaviors"

},"guillotina.behaviors.dublincore.IDublinCore": [

{"$ref": "#/definitions/guillotina.behaviors.dublincore.

→˓IDublinCore"

42 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

}]

},"invariants": []

},{

"title": "Folder","$schema": "http://json-schema.org/draft-04/schema#","type": "object","required": [

"type_name","uuid"

],"definitions": {

"guillotina.behaviors.dublincore.IDublinCore": {"type": "object","properties": {

"title": {"type": "string","description": "The first unqualified Dublin Core 'Title'

→˓element value.","title": "Title"

},"description": {

"type": "string","description": "The first unqualified Dublin Core

→˓'Description' element value.","title": "Description"

},"creation_date": {

"type": "datetime","description": "The date and time that an object is

→˓created. \nThis is normally set automatically.","title": "Creation Date"

},"modification_date": {

"type": "datetime","description": "The date and time that the object was

→˓last modified in a\nmeaningful way.","title": "Modification Date"

},"effective_date": {

"type": "datetime","description": "The date and time that an object should

→˓be published. ","title": "Effective Date"

},"expiration_date": {

"type": "datetime","description": "The date and time that the object should

→˓become unpublished.","title": "Expiration Date"

},"creators": {

"type": "array","description": "The unqualified Dublin Core 'Creator'

→˓element values","title": "Creators",

2.3. Container 43

guillotina Documentation, Release 4.2.11

"items": {"type": "string"

}},"tags": {

"type": "array","description": "The unqualified Dublin Core 'Tags'

→˓element values","title": "Tags","items": {

"type": "string"}

},"publisher": {

"type": "string","description": "The first unqualified Dublin Core

→˓'Publisher' element value.","title": "Publisher"

},"contributors": {

"type": "array","description": "The unqualified Dublin Core 'Contributor'

→˓element values","title": "Contributors","items": {

"type": "string"}

}},"required": [],"invariants": [],"title": "Dublin Core fields","description": ""

}},"properties": {

"__name__": {"type": "string","description": "The object can be looked up from the parent's

→˓sublocations using this name.","readonly": true,"title": "The name within the parent"

},"type_name": {

"type": "string","readonly": true

},"title": {

"type": "string","description": "Title of the Resource","title": "Title"

},"uuid": {

"type": "string","readonly": true,"title": "UUID"

},"modification_date": {

44 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"type": "datetime","title": "Modification date"

},"creation_date": {

"type": "datetime","title": "Creation date"

},"__behaviors__": {

"type": "array","description": "Dynamic behaviors for the content type","readonly": true,"title": "Enabled behaviors"

},"guillotina.behaviors.dublincore.IDublinCore": [

{"$ref": "#/definitions/guillotina.behaviors.dublincore.

→˓IDublinCore"}

]},"invariants": []

},{

"title": "Container","$schema": "http://json-schema.org/draft-04/schema#","type": "object","required": [

"type_name","uuid"

],"definitions": {},"properties": {

"__name__": {"type": "string","description": "The object can be looked up from the parent's

→˓sublocations using this name.","readonly": true,"title": "The name within the parent"

},"type_name": {

"type": "string","readonly": true

},"title": {

"type": "string","description": "Title of the Resource","title": "Title"

},"uuid": {

"type": "string","readonly": true,"title": "UUID"

},"modification_date": {

"type": "datetime","title": "Modification date"

},"creation_date": {

2.3. Container 45

guillotina Documentation, Release 4.2.11

"type": "datetime","title": "Creation date"

},"__behaviors__": {

"type": "array","description": "Dynamic behaviors for the content type","readonly": true,"title": "Enabled behaviors"

}},"invariants": []

}]

Status Codes

• 200 OK – Result results on types

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/@types/type_name Read information on available types

• Permission: guillotina.AccessContent

• Context: guillotina.interfaces.content.IContainer

http

GET /db/container/@types/Item HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/@types/Item -H 'Accept: application/json' --→˓user root:root

httpie

http http://nohost/db/container/@types/Item Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 2597Content-Type: application/json

{"title": "Item","$schema": "http://json-schema.org/draft-04/schema#","type": "object","required": [

"type_name","uuid"

],"definitions": {

46 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"guillotina.behaviors.dublincore.IDublinCore": {"type": "object","properties": {

"title": {"type": "string","description": "The first unqualified Dublin Core 'Title'

→˓element value.","title": "Title"

},"description": {

"type": "string","description": "The first unqualified Dublin Core 'Description

→˓' element value.","title": "Description"

},"creation_date": {

"type": "datetime","description": "The date and time that an object is created.

→˓\nThis is normally set automatically.","title": "Creation Date"

},"modification_date": {

"type": "datetime","description": "The date and time that the object was last

→˓modified in a\nmeaningful way.","title": "Modification Date"

},"effective_date": {

"type": "datetime","description": "The date and time that an object should be

→˓published. ","title": "Effective Date"

},"expiration_date": {

"type": "datetime","description": "The date and time that the object should

→˓become unpublished.","title": "Expiration Date"

},"creators": {

"type": "array","description": "The unqualified Dublin Core 'Creator' element

→˓values","title": "Creators","items": {

"type": "string"}

},"tags": {

"type": "array","description": "The unqualified Dublin Core 'Tags' element

→˓values","title": "Tags","items": {

"type": "string"}

},"publisher": {

2.3. Container 47

guillotina Documentation, Release 4.2.11

"type": "string","description": "The first unqualified Dublin Core 'Publisher'

→˓element value.","title": "Publisher"

},"contributors": {

"type": "array","description": "The unqualified Dublin Core 'Contributor'

→˓element values","title": "Contributors","items": {

"type": "string"}

}},"required": [],"invariants": [],"title": "Dublin Core fields","description": ""

}},"properties": {

"__name__": {"type": "string","description": "The object can be looked up from the parent's

→˓sublocations using this name.","readonly": true,"title": "The name within the parent"

},"type_name": {

"type": "string","readonly": true

},"title": {

"type": "string","description": "Title of the Resource","title": "Title"

},"uuid": {

"type": "string","readonly": true,"title": "UUID"

},"modification_date": {

"type": "datetime","title": "Modification date"

},"creation_date": {

"type": "datetime","title": "Creation date"

},"__behaviors__": {

"type": "array","description": "Dynamic behaviors for the content type","readonly": true,"title": "Enabled behaviors"

},"guillotina.behaviors.dublincore.IDublinCore": [

48 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

{"$ref": "#/definitions/guillotina.behaviors.dublincore.IDublinCore

→˓"}

]},"invariants": []

}

Status Codes

• 200 OK – Result results on types

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.3.2 User

GET /(db)/container/@user Get information on the currently logged in user

• Permission: guillotina.AccessContent

• Context: guillotina.interfaces.content.IContainer

http

GET /db/container/@user HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

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

httpie

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

response

HTTP/1.1 200 OKContent-Length: 302Content-Type: application/json

{"root": {

"roles": {"guillotina.Authenticated": 1

},"groups": [

"Managers"],"permissions": {},"properties": {}

},

2.3. Container 49

guillotina Documentation, Release 4.2.11

"groups": {"Managers": {

"roles": {"guillotina.ContainerAdmin": 1,"guillotina.ContainerDeleter": 1,"guillotina.Owner": 1,"guillotina.Member": 1,"guillotina.Manager": 1

},"groups": []

}}

}

Status Codes

• 200 OK – Get information on the user

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.3.3 Registry

GET /(db)/container/@registry Read container registry settings

• Permission: guillotina.ReadConfiguration

• Context: guillotina.interfaces.content.IContainer

http

GET /db/container/@registry HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/@registry -H 'Accept: application/json' --user→˓root:root

httpie

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

response

HTTP/1.1 200 OKContent-Length: 125Content-Type: application/json

{"value": {

"guillotina.interfaces.registry.ILayers.active_layers": [],"guillotina.interfaces.registry.IAddons.enabled": []

}}

50 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

Status Codes

• 200 OK – Successfully registered interface

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

POST /(db)/container/@registry Register a new interface to for registry settings

• Permission: guillotina.RegisterConfigurations

• Context: guillotina.interfaces.content.IContainer

http

POST /db/container/@registry HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

{"interface": "guillotina.documentation.IRegistryData"

}

curl

curl -i -X POST http://nohost/db/container/@registry -H 'Accept: application/json→˓' --data-raw '{

"interface": "guillotina.documentation.IRegistryData"}' --user root:root

httpie

echo '{"interface": "guillotina.documentation.IRegistryData"

}' | http POST http://nohost/db/container/@registry Accept:application/json -a→˓root:root

response

HTTP/1.1 201 OKContent-Length: 2Content-Type: application/json

{}

Status Codes

• 200 OK – Successfully registered interface

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PATCH /(db)/container/@registry/key Update registry setting

• Permission: guillotina.WriteConfiguration

• Context: guillotina.interfaces.content.IContainer

2.3. Container 51

guillotina Documentation, Release 4.2.11

http

PATCH /db/container/@registry/guillotina.documentation.IRegistryData.foobar HTTP/→˓1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

{"value": "something"

}

curl

curl -i -X PATCH http://nohost/db/container/@registry/guillotina.documentation.→˓IRegistryData.foobar -H 'Accept: application/json' --data-raw '{

"value": "something"}' --user root:root

httpie

echo '{"value": "something"

}' | http PATCH http://nohost/db/container/@registry/guillotina.documentation.→˓IRegistryData.foobar Accept:application/json -a root:root

response

HTTP/1.1 204 OKContent-Length: 0Content-Type: application/json

Query Parameters

• name (string) –

• in (string) –

• type (string) –

• schema (string) –

Status Codes

• 200 OK – Successfully wrote configuration

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/@registry/key Read container registry settings

• Permission: guillotina.ReadConfiguration

• Context: guillotina.interfaces.content.IContainer

http

GET /db/container/@registry/guillotina.documentation.IRegistryData.foobar HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

52 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

curl

curl -i http://nohost/db/container/@registry/guillotina.documentation.→˓IRegistryData.foobar -H 'Accept: application/json' --user root:root

httpie

http http://nohost/db/container/@registry/guillotina.documentation.IRegistryData.→˓foobar Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 22Content-Type: application/json

{"value": "something"

}

Status Codes

• 200 OK – Successfully registered interface

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.3.4 Addons

GET /(db)/container/@addons List available addons

• Permission: guillotina.ManageAddons

• Context: guillotina.interfaces.content.IContainer

http

GET /db/container/@addons HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/@addons -H 'Accept: application/json' --user→˓root:root

httpie

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

response

HTTP/1.1 200 OKContent-Length: 94Content-Type: application/json

{

2.3. Container 53

guillotina Documentation, Release 4.2.11

"available": [{

"id": "docaddon","title": "Doc addon","dependencies": []

}],"installed": []

}

Status Codes

• 200 OK – Get list of available and installed addons

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

POST /(db)/container/@addons Install addon to container

• Permission: guillotina.ManageAddons

• Context: guillotina.interfaces.content.IContainer

http

POST /db/container/@addons HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

{"id": "docaddon"

}

curl

curl -i -X POST http://nohost/db/container/@addons -H 'Accept: application/json' -→˓-data-raw '{

"id": "docaddon"}' --user root:root

httpie

echo '{"id": "docaddon"

}' | http POST http://nohost/db/container/@addons Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 104Content-Type: application/json

{"available": [

{"id": "docaddon",

54 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"title": "Doc addon","dependencies": []

}],"installed": [

"docaddon"]

}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

DELETE /(db)/container/@addons Uninstall an addon from container

• Permission: guillotina.ManageAddons

• Context: guillotina.interfaces.content.IContainer

http

DELETE /db/container/@addons HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

{"id": "docaddon"

}

curl

curl -i -X DELETE http://nohost/db/container/@addons -H 'Accept: application/json→˓' --data-raw '{

"id": "docaddon"}' --user root:root

httpie

echo '{"id": "docaddon"

}' | http DELETE http://nohost/db/container/@addons Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.3. Container 55

guillotina Documentation, Release 4.2.11

2.3.5 Dynamic Fields

Dynamic fields are done with the IDynamicFields behavior so first we add the behavior.

PATCH /(db)/container/@behaviors Add behavior to resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/@behaviors HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

{"behavior": "guillotina.behaviors.dynamic.IDynamicFields"

}

curl

curl -i -X PATCH http://nohost/db/container/@behaviors -H 'Accept: application/→˓json' --data-raw '{

"behavior": "guillotina.behaviors.dynamic.IDynamicFields"}' --user root:root

httpie

echo '{"behavior": "guillotina.behaviors.dynamic.IDynamicFields"

}' | http PATCH http://nohost/db/container/@behaviors Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 2Content-Type: application/json

{}

Status Codes

• 200 OK – Successfully added behavior

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

• 412 Precondition Failed – Behavior already assigned here

Then, we can add a field.

PATCH /(db)/container Modify the content of this resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

56 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

http

PATCH /db/container HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

{"guillotina.behaviors.dynamic.IDynamicFields": {

"fields": {"foobar": {

"title": "Hello field","type": "text"

}}

}}

curl

curl -i -X PATCH http://nohost/db/container -H 'Accept: application/json' --data-→˓raw '{

"guillotina.behaviors.dynamic.IDynamicFields": {"fields": {

"foobar": {"title": "Hello field","type": "text"

}}

}}' --user root:root

httpie

echo '{"guillotina.behaviors.dynamic.IDynamicFields": {

"fields": {"foobar": {

"title": "Hello field","type": "text"

}}

}}' | http PATCH http://nohost/db/container Accept:application/json -a root:root

response

HTTP/1.1 204 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

To inspect the dynamic fields available on content

2.3. Container 57

guillotina Documentation, Release 4.2.11

GET /(db)/container/@dynamic-fields Get a list of available fields

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/@dynamic-fields HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/@dynamic-fields -H 'Accept: application/json' -→˓-user root:root

httpie

http http://nohost/db/container/@dynamic-fields Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 52Content-Type: application/json

{"foobar": {

"title": "Hello field","type": "text"

}}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

Update dynamic field values

POST /(db)/container/id Add new resouce inside this container resource

• Permission: guillotina.AddContent

• Context: guillotina.interfaces.content.IResource

http

POST /db/container HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

{"@type": "Item","id": "foobar-fields","@behaviors": [

58 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"guillotina.behaviors.dynamic.IDynamicFieldValues"]

}

curl

curl -i -X POST http://nohost/db/container -H 'Accept: application/json' --data-→˓raw '{

"@type": "Item","id": "foobar-fields","@behaviors": [

"guillotina.behaviors.dynamic.IDynamicFieldValues"]

}' --user root:root

httpie

echo '{"@type": "Item","id": "foobar-fields","@behaviors": [

"guillotina.behaviors.dynamic.IDynamicFieldValues"]

}' | http POST http://nohost/db/container Accept:application/json -a root:root

response

HTTP/1.1 201 OKContent-Length: 198Content-Type: application/jsonLocation: http://127.0.0.1:51723/db/container/foobar-fields

{"@id": "http://127.0.0.1:51723/db/container/foobar-fields","@name": "foobar-fields","@type": "Item","@uid": "e17|9f016a669dab424f850da29e54eb1298","UID": "e17|9f016a669dab424f850da29e54eb1298"

}

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PATCH /(db)/container/id Modify the content of this resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar-fields HTTP/1.1Accept: application/json

2.3. Container 59

guillotina Documentation, Release 4.2.11

Authorization: Basic cm9vdDpyb290

{"guillotina.behaviors.dynamic.IDynamicFieldValues": {

"values": {"op": "update","value": [

{"key": "foobar","value": "value"

}]

}}

}

curl

curl -i -X PATCH http://nohost/db/container/foobar-fields -H 'Accept: application/→˓json' --data-raw '{

"guillotina.behaviors.dynamic.IDynamicFieldValues": {"values": {

"op": "update","value": [

{"key": "foobar","value": "value"

}]

}}

}' --user root:root

httpie

echo '{"guillotina.behaviors.dynamic.IDynamicFieldValues": {

"values": {"op": "update","value": [

{"key": "foobar","value": "value"

}]

}}

}' | http PATCH http://nohost/db/container/foobar-fields Accept:application/json -→˓a root:root

response

HTTP/1.1 204 OKContent-Length: 0Content-Type: application/json

Status Codes

60 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id Retrieves serialization of resource

• Permission: guillotina.ViewContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar-fields?include=guillotina.behaviors.dynamic.→˓IDynamicFieldValues HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i 'http://nohost/db/container/foobar-fields?include=guillotina.behaviors.→˓dynamic.IDynamicFieldValues' -H 'Accept: application/json' --user root:root

httpie

http 'http://nohost/db/container/foobar-fields?include=guillotina.behaviors.→˓dynamic.IDynamicFieldValues' Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 675Content-Type: application/json

{"@id": "http://127.0.0.1:51723/db/container/foobar-fields","@type": "Item","@name": "foobar-fields","@uid": "e17|9f016a669dab424f850da29e54eb1298","@static_behaviors": [

"guillotina.behaviors.dublincore.IDublinCore"],"parent": {

"@id": "http://127.0.0.1:51723/db/container","@name": "container","@type": "Container","@uid": "e17d899460e442e1a1aa4d769eee798b","UID": "e17d899460e442e1a1aa4d769eee798b"

},"is_folderish": false,"creation_date": "2018-10-24T20:12:24.018849+00:00","modification_date": "2018-10-24T20:12:24.039732+00:00","UID": "e17|9f016a669dab424f850da29e54eb1298","guillotina.behaviors.dynamic.IDynamicFieldValues": {

"values": {"foobar": "value"

}

2.3. Container 61

guillotina Documentation, Release 4.2.11

}}

Query Parameters

• include (string) –

• omit (string) –

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.4 Item

POST /(db)/container Add new resouce inside this container resource

• Permission: guillotina.AddContent

• Context: guillotina.interfaces.content.IResource

http

POST /db/container HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"@type": "Item","id": "foobar"

}

curl

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

httpie

echo '{"@type": "Item","id": "foobar"

}' | http POST http://nohost/db/container Accept:application/json Content-→˓Type:application/json -a root:root

response

HTTP/1.1 201 OKContent-Length: 184Content-Type: application/jsonLocation: http://127.0.0.1:51723/db/container/foobar

62 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

{"@id": "http://127.0.0.1:51723/db/container/foobar","@name": "foobar","@type": "Item","@uid": "527|41a7dffc0f8f472a84b0e4b9119aeb58","UID": "527|41a7dffc0f8f472a84b0e4b9119aeb58"

}

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PATCH /(db)/container/id Modify the content of this resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"title": "foobar"

}

curl

curl -i -X PATCH http://nohost/db/container/foobar -H 'Accept: application/json' -→˓H 'Content-Type: application/json' --data-raw '{"title": "foobar"}' --user→˓root:root

httpie

echo '{"title": "foobar"

}' | http PATCH http://nohost/db/container/foobar Accept:application/json Content-→˓Type:application/json -a root:root

response

HTTP/1.1 204 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

2.4. Item 63

guillotina Documentation, Release 4.2.11

• 404 Not Found – The resource does not exist

GET /(db)/container/id Retrieves serialization of resource

• Permission: guillotina.ViewContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar -H 'Accept: application/json' --user→˓root:root

httpie

http http://nohost/db/container/foobar Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 1036Content-Type: application/json

{"@id": "http://127.0.0.1:51723/db/container/foobar","@type": "Item","@name": "foobar","@uid": "527|41a7dffc0f8f472a84b0e4b9119aeb58","@static_behaviors": [

"guillotina.behaviors.dublincore.IDublinCore"],"parent": {

"@id": "http://127.0.0.1:51723/db/container","@name": "container","@type": "Container","@uid": "527dad67ff6942d593b72104ba28de22","UID": "527dad67ff6942d593b72104ba28de22"

},"is_folderish": false,"creation_date": "2018-10-24T20:12:24.671764+00:00","modification_date": "2018-10-24T20:12:24.690551+00:00","UID": "527|41a7dffc0f8f472a84b0e4b9119aeb58","type_name": "Item","title": "foobar","uuid": "527|41a7dffc0f8f472a84b0e4b9119aeb58","__behaviors__": [],"__name__": "foobar","guillotina.behaviors.dublincore.IDublinCore": {

"title": "foobar","description": null,"creation_date": "2018-10-24T20:12:24.671764+00:00","modification_date": "2018-10-24T20:12:24.690551+00:00",

64 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"effective_date": null,"expiration_date": null,"creators": [

"root"],"tags": null,"publisher": null,"contributors": [

"root"]

}}

Query Parameters

• include (string) –

• omit (string) –

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

DELETE /(db)/container/id Delete resource

• Permission: guillotina.DeleteContent

• Context: guillotina.interfaces.content.IResource

http

DELETE /db/container/foobar HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i -X DELETE http://nohost/db/container/foobar -H 'Accept: application/json'→˓--user root:root

httpie

http DELETE http://nohost/db/container/foobar Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully deleted resource

• 401 Unauthorized – You are not authorized to perform the operation

2.4. Item 65

guillotina Documentation, Release 4.2.11

• 404 Not Found – The resource does not exist

2.4.1 Behaviors

GET /(db)/container/id/@behaviors Get information on behaviors for this resource

• Permission: guillotina.AccessContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@behaviors HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar/@behaviors -H 'Accept: application/json→˓' --user root:root

httpie

http http://nohost/db/container/foobar/@behaviors Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 3018Content-Type: application/json

{"static": [

"guillotina.behaviors.dublincore.IDublinCore"],"dynamic": [],"available": [

"guillotina.behaviors.attachment.IAttachment","guillotina.behaviors.dynamic.IDynamicFields","guillotina.behaviors.dynamic.IDynamicFieldValues"

],"guillotina.behaviors.attachment.IAttachment": {

"type": "object","properties": {

"file": {"type": "object","properties": {

"type": "object","properties": {

"content_type": {"type": "string","description": "The content type identifies the type

→˓of data.","title": "Content Type"

},"filename": {

66 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"type": "string","title": "Filename"

},"extension": {

"type": "string","title": "Extension of the file"

},"md5": {

"type": "string","title": "MD5"

},"size": {

"type": "integer","title": "Size"

}},"required": [

"size"],"invariants": []

}}

},"required": [

"file"],"invariants": []

},"guillotina.behaviors.dublincore.IDublinCore": {

"type": "object","properties": {

"title": {"type": "string","description": "The first unqualified Dublin Core 'Title' element

→˓value.","title": "Title"

},"description": {

"type": "string","description": "The first unqualified Dublin Core 'Description'

→˓element value.","title": "Description"

},"creation_date": {

"type": "datetime","description": "The date and time that an object is created.

→˓\nThis is normally set automatically.","title": "Creation Date"

},"modification_date": {

"type": "datetime","description": "The date and time that the object was last

→˓modified in a\nmeaningful way.","title": "Modification Date"

},"effective_date": {

"type": "datetime","description": "The date and time that an object should be

→˓published. ",

2.4. Item 67

guillotina Documentation, Release 4.2.11

"title": "Effective Date"},"expiration_date": {

"type": "datetime","description": "The date and time that the object should become

→˓unpublished.","title": "Expiration Date"

},"creators": {

"type": "array","description": "The unqualified Dublin Core 'Creator' element

→˓values","title": "Creators","items": {

"type": "string"}

},"tags": {

"type": "array","description": "The unqualified Dublin Core 'Tags' element values

→˓","title": "Tags","items": {

"type": "string"}

},"publisher": {

"type": "string","description": "The first unqualified Dublin Core 'Publisher'

→˓element value.","title": "Publisher"

},"contributors": {

"type": "array","description": "The unqualified Dublin Core 'Contributor' element

→˓values","title": "Contributors","items": {

"type": "string"}

}},"required": [],"invariants": []

},"guillotina.behaviors.dynamic.IDynamicFields": {

"type": "object","properties": {

"fields": {"type": "object","additionalProperties": {

"type": "object","properties": {

"type": "object","properties": {

"title": {"type": "string"

},

68 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"type": {"type": "string","vocabulary": [

"date","integer","text","float","keyword","boolean"

]}

},"required": [

"title","type"

],"invariants": []

}}

}},"required": [

"fields"],"invariants": []

},"guillotina.behaviors.dynamic.IDynamicFieldValues": {

"type": "object","properties": {

"values": {"type": "object","additionalProperties": true

}},"required": [

"values"],"invariants": []

}}

Status Codes

• 200 OK – A listing of behaviors for content

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PATCH /(db)/container/id/@behaviors Add behavior to resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

2.4. Item 69

guillotina Documentation, Release 4.2.11

PATCH /db/container/foobar/@behaviors HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

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

}

curl

curl -i -X PATCH http://nohost/db/container/foobar/@behaviors -H 'Accept:→˓application/json' -H 'Content-Type: 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/foobar/@behaviors Accept:application/→˓json Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 2Content-Type: application/json

{}

Status Codes

• 200 OK – Successfully added behavior

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

• 412 Precondition Failed – Behavior already assigned here

DELETE /(db)/container/id/@behaviors Remove behavior from resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

DELETE /db/container/foobar/@behaviors HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

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

}

curl

70 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

curl -i -X DELETE http://nohost/db/container/foobar/@behaviors -H 'Accept:→˓application/json' -H 'Content-Type: application/json' --data-raw '{"behavior":→˓"guillotina.behaviors.attachment.IAttachment"}' --user root:root

httpie

echo '{"behavior": "guillotina.behaviors.attachment.IAttachment"

}' | http DELETE http://nohost/db/container/foobar/@behaviors Accept:application/→˓json Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 2Content-Type: application/json

{}

Status Codes

• 200 OK – Successfully removed behavior

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

• 412 Precondition Failed – Behavior not assigned here

2.4.2 Files

First, add the IAttachment behavior.

We have simple @upload and @download endpoints

PATCH /(db)/container/id/@upload/field_name

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar/@upload/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

foobar data

curl

curl -i -X PATCH http://nohost/db/container/foobar/@upload/file -H 'Accept:→˓application/json' --data-raw 'foobar data' --user root:root

httpie

echo 'foobar data' | http PATCH http://nohost/db/container/foobar/@upload/file→˓Accept:application/json -a root:root

2.4. Item 71

guillotina Documentation, Release 4.2.11

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@download/field_name

• Permission: guillotina.ViewContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@download/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

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

httpie

http http://nohost/db/container/foobar/@download/file Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Disposition: attachment; filename="b2c4dcd393d74e81b28d5435ad9b9fb6"Content-Length: 11Content-Type: text/plain

foobar data

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

But we also support TUS.

POST /(db)/container/content/@tusupload/file

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

72 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

POST /db/container/foobar/@tusupload/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290TUS-RESUMABLE: 1UPLOAD-LENGTH: 22

curl

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

httpie

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

response

HTTP/1.1 201 OKContent-Length: 2Content-Type: application/jsonLocation: http://127.0.0.1:51723/db/container/foobar/@tusupload/fileTus-Resumable: 1.0.0

{}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PATCH /(db)/container/content/@tusupload/file

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar/@tusupload/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290TUS-RESUMABLE: 1Upload-Offset: 0

<text data>

curl

curl -i -X PATCH http://nohost/db/container/foobar/@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/foobar/@tusupload/file→˓Accept:application/json Tus-Resumable:1 Upload-Offset:0 -a root:root

2.4. Item 73

guillotina Documentation, Release 4.2.11

response

HTTP/1.1 200 OKContent-Length: 2Content-Type: application/jsonTus-Resumable: 1.0.0Upload-Offset: 11

{}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PATCH /(db)/container/content/@tusupload/file

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar/@tusupload/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290TUS-RESUMABLE: 1Upload-Offset: 11

<text data>

curl

curl -i -X PATCH http://nohost/db/container/foobar/@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/foobar/@tusupload/file→˓Accept:application/json Tus-Resumable:1 Upload-Offset:11 -a root:root

response

HTTP/1.1 200 OKContent-Length: 2Content-Type: application/jsonTus-Resumable: 1.0.0Tus-Upload-Finished: 1Upload-Offset: 22

{}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

74 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

Download again, see what we have.

GET /(db)/container/id/@download/field_name

• Permission: guillotina.ViewContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@download/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

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

httpie

http http://nohost/db/container/foobar/@download/file Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Disposition: attachment; filename="f93a010f410144cf8ca93573bc0c7352"Content-Length: 22Content-Type: application/octet-stream

<text data><text data>

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.4.3 Security

GET /(db)/container/id/@all_permissions See all permission settings for this resource

• Permission: guillotina.SeePermissions

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@all_permissions HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar/@all_permissions -H 'Accept:→˓application/json' --user root:root

2.4. Item 75

guillotina Documentation, Release 4.2.11

httpie

http http://nohost/db/container/foobar/@all_permissions Accept:application/json -→˓a root:root

response

HTTP/1.1 200 OKContent-Length: 4512Content-Type: application/json

[{

"foobar": {"prinperm": [],"prinrole": [

{"principal": "root","role": "guillotina.Owner","setting": "Allow"

}],"roleperm": [],"perminhe": []

}},{

"container": {"prinperm": [],"prinrole": [

{"principal": "root","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"principal": "root","role": "guillotina.Owner","setting": "Allow"

}],"roleperm": [],"perminhe": []

}},{

"(no name)": {"prinperm": [

{"principal": "root","permission": "guillotina.AccessContent","setting": "Allow"

},{

"principal": "root","permission": "guillotina.AddContainer","setting": "Allow"

},{

76 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"principal": "root","permission": "guillotina.DeleteContainers","setting": "Allow"

},{

"principal": "root","permission": "guillotina.GetAPIDefinition","setting": "Allow"

},{

"principal": "root","permission": "guillotina.GetContainers","setting": "Allow"

},{

"principal": "root","permission": "guillotina.GetDatabases","setting": "Allow"

},{

"principal": "root","permission": "guillotina.MountDatabase","setting": "Allow"

},{

"principal": "root","permission": "guillotina.UmountDatabase","setting": "Allow"

}],"perminhe": []

}},{

"system": {"prinperm": [],"prinrole": [],"roleperm": [

{"permission": "guillotina.AccessPreflight","role": "guillotina.Anonymous","setting": "Allow"

},{

"permission": "guillotina.AccessPreflight","role": "guillotina.Authenticated","setting": "Allow"

},{

"permission": "guillotina.Public","role": "guillotina.Anonymous","setting": "Allow"

},{

"permission": "guillotina.Public","role": "guillotina.Authenticated","setting": "Allow"

},

2.4. Item 77

guillotina Documentation, Release 4.2.11

{"permission": "guillotina.ViewContent","role": "guillotina.Reader","setting": "Allow"

},{

"permission": "guillotina.ViewContent","role": "guillotina.Reviewer","setting": "Allow"

},{

"permission": "guillotina.ViewContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.ViewContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.Reader","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.Reviewer","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.DuplicateContent","role": "guillotina.Reader","setting": "Allow"

},{

"permission": "guillotina.DuplicateContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.DuplicateContent","role": "guillotina.Editor",

78 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"setting": "Allow"},{

"permission": "guillotina.DeleteContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.AddContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.MoveContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.MoveContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.ModifyContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.ModifyContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.ChangePermissions","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.SeePermissions","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.ReindexContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.ReindexContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.ManageAddons","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

2.4. Item 79

guillotina Documentation, Release 4.2.11

"permission": "guillotina.ReadConfiguration","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.WriteConfiguration","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.RegisterConfigurations","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.ManageCatalog","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.RawSearchContent","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.Manage","role": "guillotina.Manager","setting": "Allow"

},{

"permission": "guillotina.DeleteContainers","role": "guillotina.ContainerDeleter","setting": "Allow"

},{

"permission": "guillotina.SearchContent","role": "guillotina.Member","setting": "Allow"

}]

}}

]

Status Codes

• 200 OK – All the permissions defined on this resource

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@canido Check if user has permissions on context

• Permission: guillotina.AccessContent

• Context: guillotina.interfaces.content.IResource

80 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

http

GET /db/container/foobar/@canido?permissions=guillotina.ModifyContent,guillotina.→˓AccessContent HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i 'http://nohost/db/container/foobar/@canido?permissions=guillotina.→˓ModifyContent,guillotina.AccessContent' -H 'Accept: application/json' --user→˓root:root

httpie

http 'http://nohost/db/container/foobar/@canido?permissions=guillotina.→˓ModifyContent,guillotina.AccessContent' Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 68Content-Type: application/json

{"guillotina.ModifyContent": true,"guillotina.AccessContent": true

}

Query Parameters

• permission (string) – (required) (required)

Status Codes

• 200 OK – Successfully changed permission

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@sharing Get sharing settings for this resource

• Permission: guillotina.SeePermissions

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

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

httpie

2.4. Item 81

guillotina Documentation, Release 4.2.11

http http://nohost/db/container/foobar/@sharing Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 280Content-Type: application/json

{"local": {

"roleperm": {},"prinperm": {},"prinrole": {

"root": {"guillotina.Owner": "Allow"

}}

},"inherit": [

{"@id": "http://127.0.0.1:51723/db/container","roleperm": {},"prinperm": {},"prinrole": {

"root": {"guillotina.ContainerAdmin": "Allow","guillotina.Owner": "Allow"

}}

}]

}

Status Codes

• 200 OK – All the sharing defined on this resource

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

POST /(db)/container/id/@sharing Change permissions for a resource

• Permission: guillotina.ChangePermissions

• Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"prinrole": [

{

82 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"principal": "foobar","role": "guillotina.Owner","setting": "Allow"

}]

}

curl

curl -i -X POST http://nohost/db/container/foobar/@sharing -H 'Accept:→˓application/json' -H 'Content-Type: application/json' --data-raw '{"prinrole": [→˓{"principal": "foobar", "role": "guillotina.Owner", "setting": "Allow"}]}' --→˓user root:root

httpie

echo '{"prinrole": [

{"principal": "foobar","role": "guillotina.Owner","setting": "Allow"

}]

}' | http POST http://nohost/db/container/foobar/@sharing Accept:application/json→˓Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully changed permission

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PUT /(db)/container/id/@sharing Replace permissions for a resource

• Permission: guillotina.ChangePermissions

• Context: guillotina.interfaces.content.IResource

http

PUT /db/container/foobar/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"prinrole": [

{"principal": "foobar",

2.4. Item 83

guillotina Documentation, Release 4.2.11

"role": "guillotina.Owner","setting": "Allow"

}]

}

curl

curl -i -X PUT http://nohost/db/container/foobar/@sharing -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"prinrole": [{"principal→˓": "foobar", "role": "guillotina.Owner", "setting": "Allow"}]}' --user root:root

httpie

echo '{"prinrole": [

{"principal": "foobar","role": "guillotina.Owner","setting": "Allow"

}]

}' | http PUT http://nohost/db/container/foobar/@sharing Accept:application/json→˓Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully replaced permissions

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.4.4 Content

POST /(db)/container/id/@move Move resource

• Permission: guillotina.MoveContent

• Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar/@move HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"destination": "",

84 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"new_id": "foobar2"}

curl

curl -i -X POST http://nohost/db/container/foobar/@move -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"destination": "", "new_→˓id": "foobar2"}' --user root:root

httpie

echo '{"destination": "","new_id": "foobar2"

}' | http POST http://nohost/db/container/foobar/@move Accept:application/json→˓Content-Type:application/json -a root:root

response

HTTP/1.1 412 OKContent-Length: 258Content-Type: application/json

{"reason": "preconditionFailed","details": "","type": "PreconditionFailed","eid": "10776e0d55f84ea9aca91979d9e1abcf","message": "Precondition Failed Destination already has object with the id

→˓foobar2 on < Item at /container/foobar2 by 140703109802824 >"}

Status Codes

• 200 OK – Successfully moved resource

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

POST /(db)/container/id/@duplicate Duplicate resource

• Permission: guillotina.DuplicateContent

• Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar2/@duplicate HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"destination": "","new_id": "foobar3"

}

2.4. Item 85

guillotina Documentation, Release 4.2.11

curl

curl -i -X POST http://nohost/db/container/foobar2/@duplicate -H 'Accept:→˓application/json' -H 'Content-Type: application/json' --data-raw '{"destination→˓": "", "new_id": "foobar3"}' --user root:root

httpie

echo '{"destination": "","new_id": "foobar3"

}' | http POST http://nohost/db/container/foobar2/@duplicate Accept:application/→˓json Content-Type:application/json -a root:root

response

HTTP/1.1 412 OKContent-Length: 260Content-Type: application/json

{"reason": "preconditionFailed","details": "","type": "PreconditionFailed","eid": "007e436b81014adda760218f4560e138","message": "Precondition Failed Destination already has object with the id

→˓foobar3 on < Folder at /container/foobar2 by 140703115680200 >"}

Status Codes

• 200 OK – Successfully duplicated object

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@invalidate-cache Invalidate cache of object

• Permission: guillotina.ModifyContent

• Context: zope.interface.Interface

http

GET /db/container/foobar2/@invalidate-cache HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar2/@invalidate-cache -H 'Accept:→˓application/json' --user root:root

httpie

http http://nohost/db/container/foobar2/@invalidate-cache Accept:application/json→˓-a root:root

86 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully invalidated

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.5 Folder

POST /(db)/container Add new resouce inside this container resource

• Permission: guillotina.AddContent

• Context: guillotina.interfaces.content.IResource

http

POST /db/container HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"@type": "Folder","id": "foobar"

}

curl

curl -i -X POST http://nohost/db/container -H 'Accept: application/json' -H→˓'Content-Type: application/json' --data-raw '{"@type": "Folder", "id": "foobar"}→˓' --user root:root

httpie

echo '{"@type": "Folder","id": "foobar"

}' | http POST http://nohost/db/container Accept:application/json Content-→˓Type:application/json -a root:root

response

HTTP/1.1 201 OKContent-Length: 186Content-Type: application/jsonLocation: http://127.0.0.1:51723/db/container/foobar

{"@id": "http://127.0.0.1:51723/db/container/foobar",

2.5. Folder 87

guillotina Documentation, Release 4.2.11

"@name": "foobar","@type": "Folder","@uid": "527|8c8da09b50f44ac699d189e8f47c6400","UID": "527|8c8da09b50f44ac699d189e8f47c6400"

}

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PATCH /(db)/container/id Modify the content of this resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"title": "foobar"

}

curl

curl -i -X PATCH http://nohost/db/container/foobar -H 'Accept: application/json' -→˓H 'Content-Type: application/json' --data-raw '{"title": "foobar"}' --user→˓root:root

httpie

echo '{"title": "foobar"

}' | http PATCH http://nohost/db/container/foobar Accept:application/json Content-→˓Type:application/json -a root:root

response

HTTP/1.1 204 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

88 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

GET /(db)/container/id Retrieves serialization of resource

• Permission: guillotina.ViewContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar -H 'Accept: application/json' --user→˓root:root

httpie

http http://nohost/db/container/foobar Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 1065Content-Type: application/json

{"@id": "http://127.0.0.1:51723/db/container/foobar","@type": "Folder","@name": "foobar","@uid": "527|8c8da09b50f44ac699d189e8f47c6400","@static_behaviors": [

"guillotina.behaviors.dublincore.IDublinCore"],"parent": {

"@id": "http://127.0.0.1:51723/db/container","@name": "container","@type": "Container","@uid": "527dad67ff6942d593b72104ba28de22","UID": "527dad67ff6942d593b72104ba28de22"

},"is_folderish": true,"creation_date": "2018-10-24T20:12:24.198337+00:00","modification_date": "2018-10-24T20:12:24.217299+00:00","UID": "527|8c8da09b50f44ac699d189e8f47c6400","type_name": "Folder","title": "foobar","uuid": "527|8c8da09b50f44ac699d189e8f47c6400","__behaviors__": [],"__name__": "foobar","guillotina.behaviors.dublincore.IDublinCore": {

"title": "foobar","description": null,"creation_date": "2018-10-24T20:12:24.198337+00:00","modification_date": "2018-10-24T20:12:24.217299+00:00","effective_date": null,"expiration_date": null,"creators": [

2.5. Folder 89

guillotina Documentation, Release 4.2.11

"root"],"tags": null,"publisher": null,"contributors": [

"root"]

},"items": [],"length": 0

}

Query Parameters

• include (string) –

• omit (string) –

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

DELETE /(db)/container/id Delete resource

• Permission: guillotina.DeleteContent

• Context: guillotina.interfaces.content.IResource

http

DELETE /db/container/foobar HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i -X DELETE http://nohost/db/container/foobar -H 'Accept: application/json'→˓--user root:root

httpie

http DELETE http://nohost/db/container/foobar Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully deleted resource

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

90 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

2.5.1 Behaviors

GET /(db)/container/id/@behaviors Get information on behaviors for this resource

• Permission: guillotina.AccessContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@behaviors HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar/@behaviors -H 'Accept: application/json→˓' --user root:root

httpie

http http://nohost/db/container/foobar/@behaviors Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 3018Content-Type: application/json

{"static": [

"guillotina.behaviors.dublincore.IDublinCore"],"dynamic": [],"available": [

"guillotina.behaviors.attachment.IAttachment","guillotina.behaviors.dynamic.IDynamicFields","guillotina.behaviors.dynamic.IDynamicFieldValues"

],"guillotina.behaviors.attachment.IAttachment": {

"type": "object","properties": {

"file": {"type": "object","properties": {

"type": "object","properties": {

"content_type": {"type": "string","description": "The content type identifies the type

→˓of data.","title": "Content Type"

},"filename": {

"type": "string","title": "Filename"

},

2.5. Folder 91

guillotina Documentation, Release 4.2.11

"extension": {"type": "string","title": "Extension of the file"

},"md5": {

"type": "string","title": "MD5"

},"size": {

"type": "integer","title": "Size"

}},"required": [

"size"],"invariants": []

}}

},"required": [

"file"],"invariants": []

},"guillotina.behaviors.dublincore.IDublinCore": {

"type": "object","properties": {

"title": {"type": "string","description": "The first unqualified Dublin Core 'Title' element

→˓value.","title": "Title"

},"description": {

"type": "string","description": "The first unqualified Dublin Core 'Description'

→˓element value.","title": "Description"

},"creation_date": {

"type": "datetime","description": "The date and time that an object is created.

→˓\nThis is normally set automatically.","title": "Creation Date"

},"modification_date": {

"type": "datetime","description": "The date and time that the object was last

→˓modified in a\nmeaningful way.","title": "Modification Date"

},"effective_date": {

"type": "datetime","description": "The date and time that an object should be

→˓published. ","title": "Effective Date"

},

92 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"expiration_date": {"type": "datetime","description": "The date and time that the object should become

→˓unpublished.","title": "Expiration Date"

},"creators": {

"type": "array","description": "The unqualified Dublin Core 'Creator' element

→˓values","title": "Creators","items": {

"type": "string"}

},"tags": {

"type": "array","description": "The unqualified Dublin Core 'Tags' element values

→˓","title": "Tags","items": {

"type": "string"}

},"publisher": {

"type": "string","description": "The first unqualified Dublin Core 'Publisher'

→˓element value.","title": "Publisher"

},"contributors": {

"type": "array","description": "The unqualified Dublin Core 'Contributor' element

→˓values","title": "Contributors","items": {

"type": "string"}

}},"required": [],"invariants": []

},"guillotina.behaviors.dynamic.IDynamicFields": {

"type": "object","properties": {

"fields": {"type": "object","additionalProperties": {

"type": "object","properties": {

"type": "object","properties": {

"title": {"type": "string"

},"type": {

"type": "string",

2.5. Folder 93

guillotina Documentation, Release 4.2.11

"vocabulary": ["date","integer","text","float","keyword","boolean"

]}

},"required": [

"title","type"

],"invariants": []

}}

}},"required": [

"fields"],"invariants": []

},"guillotina.behaviors.dynamic.IDynamicFieldValues": {

"type": "object","properties": {

"values": {"type": "object","additionalProperties": true

}},"required": [

"values"],"invariants": []

}}

Status Codes

• 200 OK – A listing of behaviors for content

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PATCH /(db)/container/id/@behaviors Add behavior to resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar/@behaviors HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

94 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

Content-Type: application/json

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

}

curl

curl -i -X PATCH http://nohost/db/container/foobar/@behaviors -H 'Accept:→˓application/json' -H 'Content-Type: 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/foobar/@behaviors Accept:application/→˓json Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 2Content-Type: application/json

{}

Status Codes

• 200 OK – Successfully added behavior

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

• 412 Precondition Failed – Behavior already assigned here

DELETE /(db)/container/id/@behaviors Remove behavior from resource

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

DELETE /db/container/foobar/@behaviors HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

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

}

curl

curl -i -X DELETE http://nohost/db/container/foobar/@behaviors -H 'Accept:→˓application/json' -H 'Content-Type: application/json' --data-raw '{"behavior":→˓"guillotina.behaviors.attachment.IAttachment"}' --user root:root

2.5. Folder 95

guillotina Documentation, Release 4.2.11

httpie

echo '{"behavior": "guillotina.behaviors.attachment.IAttachment"

}' | http DELETE http://nohost/db/container/foobar/@behaviors Accept:application/→˓json Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 2Content-Type: application/json

{}

Status Codes

• 200 OK – Successfully removed behavior

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

• 412 Precondition Failed – Behavior not assigned here

2.5.2 Files

PATCH /(db)/container/id/@upload/field_name

• Permission: guillotina.ModifyContent

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar/@upload/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

foobar data

curl

curl -i -X PATCH http://nohost/db/container/foobar/@upload/file -H 'Accept:→˓application/json' --data-raw 'foobar data' --user root:root

httpie

echo 'foobar data' | http PATCH http://nohost/db/container/foobar/@upload/file→˓Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

96 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@download/field_name

• Permission: guillotina.ViewContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@download/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

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

httpie

http http://nohost/db/container/foobar/@download/file Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Disposition: attachment; filename="7be954cb459e4f949d6c779c016df4a7"Content-Length: 11Content-Type: text/plain

foobar data

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.5.3 Security

GET /(db)/container/id/@all_permissions See all permission settings for this resource

• Permission: guillotina.SeePermissions

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@all_permissions HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

2.5. Folder 97

guillotina Documentation, Release 4.2.11

curl -i http://nohost/db/container/foobar/@all_permissions -H 'Accept:→˓application/json' --user root:root

httpie

http http://nohost/db/container/foobar/@all_permissions Accept:application/json -→˓a root:root

response

HTTP/1.1 200 OKContent-Length: 4512Content-Type: application/json

[{

"foobar": {"prinperm": [],"prinrole": [

{"principal": "root","role": "guillotina.Owner","setting": "Allow"

}],"roleperm": [],"perminhe": []

}},{

"container": {"prinperm": [],"prinrole": [

{"principal": "root","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"principal": "root","role": "guillotina.Owner","setting": "Allow"

}],"roleperm": [],"perminhe": []

}},{

"(no name)": {"prinperm": [

{"principal": "root","permission": "guillotina.AccessContent","setting": "Allow"

},{

"principal": "root",

98 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

"permission": "guillotina.AddContainer","setting": "Allow"

},{

"principal": "root","permission": "guillotina.DeleteContainers","setting": "Allow"

},{

"principal": "root","permission": "guillotina.GetAPIDefinition","setting": "Allow"

},{

"principal": "root","permission": "guillotina.GetContainers","setting": "Allow"

},{

"principal": "root","permission": "guillotina.GetDatabases","setting": "Allow"

},{

"principal": "root","permission": "guillotina.MountDatabase","setting": "Allow"

},{

"principal": "root","permission": "guillotina.UmountDatabase","setting": "Allow"

}],"perminhe": []

}},{

"system": {"prinperm": [],"prinrole": [],"roleperm": [

{"permission": "guillotina.AccessPreflight","role": "guillotina.Anonymous","setting": "Allow"

},{

"permission": "guillotina.AccessPreflight","role": "guillotina.Authenticated","setting": "Allow"

},{

"permission": "guillotina.Public","role": "guillotina.Anonymous","setting": "Allow"

},{

2.5. Folder 99

guillotina Documentation, Release 4.2.11

"permission": "guillotina.Public","role": "guillotina.Authenticated","setting": "Allow"

},{

"permission": "guillotina.ViewContent","role": "guillotina.Reader","setting": "Allow"

},{

"permission": "guillotina.ViewContent","role": "guillotina.Reviewer","setting": "Allow"

},{

"permission": "guillotina.ViewContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.ViewContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.Reader","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.Reviewer","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.AccessContent","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.DuplicateContent","role": "guillotina.Reader","setting": "Allow"

},{

"permission": "guillotina.DuplicateContent","role": "guillotina.Owner","setting": "Allow"

100 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

},{

"permission": "guillotina.DuplicateContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.DeleteContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.AddContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.MoveContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.MoveContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.ModifyContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.ModifyContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.ChangePermissions","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.SeePermissions","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.ReindexContent","role": "guillotina.Owner","setting": "Allow"

},{

"permission": "guillotina.ReindexContent","role": "guillotina.Editor","setting": "Allow"

},{

"permission": "guillotina.ManageAddons",

2.5. Folder 101

guillotina Documentation, Release 4.2.11

"role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.ReadConfiguration","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.WriteConfiguration","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.RegisterConfigurations","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.ManageCatalog","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.RawSearchContent","role": "guillotina.ContainerAdmin","setting": "Allow"

},{

"permission": "guillotina.Manage","role": "guillotina.Manager","setting": "Allow"

},{

"permission": "guillotina.DeleteContainers","role": "guillotina.ContainerDeleter","setting": "Allow"

},{

"permission": "guillotina.SearchContent","role": "guillotina.Member","setting": "Allow"

}]

}}

]

Status Codes

• 200 OK – All the permissions defined on this resource

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@canido Check if user has permissions on context

102 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

• Permission: guillotina.AccessContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@canido?permissions=guillotina.ModifyContent,guillotina.→˓AccessContent HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i 'http://nohost/db/container/foobar/@canido?permissions=guillotina.→˓ModifyContent,guillotina.AccessContent' -H 'Accept: application/json' --user→˓root:root

httpie

http 'http://nohost/db/container/foobar/@canido?permissions=guillotina.→˓ModifyContent,guillotina.AccessContent' Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 68Content-Type: application/json

{"guillotina.ModifyContent": true,"guillotina.AccessContent": true

}

Query Parameters

• permission (string) – (required)

Status Codes

• 200 OK – Successfully changed permission

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@sharing Get sharing settings for this resource

• Permission: guillotina.SeePermissions

• Context: guillotina.interfaces.content.IResource

http

GET /db/container/foobar/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

2.5. Folder 103

guillotina Documentation, Release 4.2.11

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

httpie

http http://nohost/db/container/foobar/@sharing Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 280Content-Type: application/json

{"local": {

"roleperm": {},"prinperm": {},"prinrole": {

"root": {"guillotina.Owner": "Allow"

}}

},"inherit": [

{"@id": "http://127.0.0.1:51723/db/container","roleperm": {},"prinperm": {},"prinrole": {

"root": {"guillotina.ContainerAdmin": "Allow","guillotina.Owner": "Allow"

}}

}]

}

Status Codes

• 200 OK – All the sharing defined on this resource

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

POST /(db)/container/id/@sharing Change permissions for a resource

• Permission: guillotina.ChangePermissions

• Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

104 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

Content-Type: application/json

{"prinrole": [

{"principal": "foobar","role": "guillotina.Owner","setting": "Allow"

}]

}

curl

curl -i -X POST http://nohost/db/container/foobar/@sharing -H 'Accept:→˓application/json' -H 'Content-Type: application/json' --data-raw '{"prinrole": [→˓{"principal": "foobar", "role": "guillotina.Owner", "setting": "Allow"}]}' --→˓user root:root

httpie

echo '{"prinrole": [

{"principal": "foobar","role": "guillotina.Owner","setting": "Allow"

}]

}' | http POST http://nohost/db/container/foobar/@sharing Accept:application/json→˓Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully changed permission

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

PUT /(db)/container/id/@sharing Replace permissions for a resource

• Permission: guillotina.ChangePermissions

• Context: guillotina.interfaces.content.IResource

http

PUT /db/container/foobar/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

2.5. Folder 105

guillotina Documentation, Release 4.2.11

{"prinrole": [

{"principal": "foobar","role": "guillotina.Owner","setting": "Allow"

}]

}

curl

curl -i -X PUT http://nohost/db/container/foobar/@sharing -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"prinrole": [{"principal→˓": "foobar", "role": "guillotina.Owner", "setting": "Allow"}]}' --user root:root

httpie

echo '{"prinrole": [

{"principal": "foobar","role": "guillotina.Owner","setting": "Allow"

}]

}' | http PUT http://nohost/db/container/foobar/@sharing Accept:application/json→˓Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully replaced permissions

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

2.5.4 Content

POST /(db)/container/id/@move Move resource

• Permission: guillotina.MoveContent

• Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar/@move HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

106 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

Content-Type: application/json

{"destination": "","new_id": "foobar2"

}

curl

curl -i -X POST http://nohost/db/container/foobar/@move -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"destination": "", "new_→˓id": "foobar2"}' --user root:root

httpie

echo '{"destination": "","new_id": "foobar2"

}' | http POST http://nohost/db/container/foobar/@move Accept:application/json→˓Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 55Content-Type: application/json

{"@url": "http://127.0.0.1:51723/db/container/foobar2"

}

Status Codes

• 200 OK – Successfully moved resource

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

POST /(db)/container/id/@duplicate Duplicate resource

• Permission: guillotina.DuplicateContent

• Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar2/@duplicate HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/json

{"destination": "","new_id": "foobar3"

}

curl

2.5. Folder 107

guillotina Documentation, Release 4.2.11

curl -i -X POST http://nohost/db/container/foobar2/@duplicate -H 'Accept:→˓application/json' -H 'Content-Type: application/json' --data-raw '{"destination→˓": "", "new_id": "foobar3"}' --user root:root

httpie

echo '{"destination": "","new_id": "foobar3"

}' | http POST http://nohost/db/container/foobar2/@duplicate Accept:application/→˓json Content-Type:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 1286Content-Type: application/json

{"@id": "http://127.0.0.1:51723/db/container/foobar3","@type": "Folder","@name": "foobar3","@uid": "527|4905ec4321884ef7b798b878c9ab2d02","@static_behaviors": [

"guillotina.behaviors.dublincore.IDublinCore"],"parent": {

"@id": "http://127.0.0.1:51723/db/container","@name": "container","@type": "Container","@uid": "527dad67ff6942d593b72104ba28de22","UID": "527dad67ff6942d593b72104ba28de22"

},"is_folderish": true,"creation_date": "2018-10-24T20:12:24.277414+00:00","modification_date": "2018-10-24T20:12:24.462897+00:00","UID": "527|4905ec4321884ef7b798b878c9ab2d02","type_name": "Folder","title": null,"uuid": "527|4905ec4321884ef7b798b878c9ab2d02","__behaviors__": [

"guillotina.behaviors.attachment.IAttachment"],"__name__": "foobar3","guillotina.behaviors.dublincore.IDublinCore": {

"title": null,"description": null,"creation_date": "2018-10-24T20:12:24.277414+00:00","modification_date": "2018-10-24T20:12:24.462897+00:00","effective_date": null,"expiration_date": null,"creators": [

"root"],"tags": null,"publisher": null,"contributors": [

"root"

108 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

]},"guillotina.behaviors.attachment.IAttachment": {

"file": {"filename": "7be954cb459e4f949d6c779c016df4a7","content_type": "text/plain","size": 11,"extension": null,"md5": null

}},"items": [],"length": 0

}

Status Codes

• 200 OK – Successfully duplicated object

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@addable-types Return a list of type names that can be added to container

• Permission: guillotina.AddContent

• Context: guillotina.interfaces.content.IAsyncContainer

http

GET /db/container/foobar3/@addable-types HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar3/@addable-types -H 'Accept: application/→˓json' --user root:root

httpie

http http://nohost/db/container/foobar3/@addable-types Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 31Content-Type: application/json

["Item","Folder","Container"

]

Status Codes

2.5. Folder 109

guillotina Documentation, Release 4.2.11

• 200 OK – Successfully returned list of type names

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@ids Return a list of ids in the resource

• Permission: guillotina.Manage

• Context: guillotina.interfaces.content.IFolder

http

GET /db/container/foobar3/@ids HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar3/@ids -H 'Accept: application/json' --→˓user root:root

httpie

http http://nohost/db/container/foobar3/@ids Accept:application/json -a root:root

response

HTTP/1.1 200 OKContent-Length: 2Content-Type: application/json

[]

Status Codes

• 200 OK – Successfully returned list of ids

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@items Paginated list of sub objects

• Permission: guillotina.Manage

• Context: guillotina.interfaces.content.IFolder

http

GET /db/container/foobar3/@items HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar3/@items -H 'Accept: application/json' --→˓user root:root

110 Chapter 2. REST API

guillotina Documentation, Release 4.2.11

httpie

http http://nohost/db/container/foobar3/@items Accept:application/json -a→˓root:root

response

HTTP/1.1 200 OKContent-Length: 53Content-Type: application/json

{"items": [],"total": 0,"page": 1,"page_size": 20

}

Query Parameters

• include (string) –

• omit (string) –

• page_size (number) – (default: 20)

• page (number) – (default: 1)

Status Codes

• 200 OK – Successfully returned response object

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

GET /(db)/container/id/@invalidate-cache Invalidate cache of object

• Permission: guillotina.ModifyContent

• Context: zope.interface.Interface

http

GET /db/container/foobar3/@invalidate-cache HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

curl

curl -i http://nohost/db/container/foobar3/@invalidate-cache -H 'Accept:→˓application/json' --user root:root

httpie

http http://nohost/db/container/foobar3/@invalidate-cache Accept:application/json→˓-a root:root

response

2.5. Folder 111

guillotina Documentation, Release 4.2.11

HTTP/1.1 200 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully invalidated

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

112 Chapter 2. REST API

CHAPTER 3

Narrative Developer Documentation

3.1 Getting started

In these narrative docs, we’ll go through creating a todo application.

3.1.1 Installation

pip install guillotina

3.1.2 Generating the initial application

Guillotina comes with a cookiecutter template for creating a base application.

First, install cookiecutter if it isn’t already installed.

pip install cookiecutter

Then, run the generate command:

guillotina create --template=application

Enter guillotina_todo for package_name.

Then, install your package:

cd guillotina_todopython setup.py develop

113

guillotina Documentation, Release 4.2.11

3.1.3 Configuring

The scaffold produces an initial config.yaml configuration file for you.

You can inspect and customize your configuration. Most notable is the database configuration. If you want to run adevelopment postgresql server, I recommend you use docker:

docker run --rm \-e POSTGRES_DB=guillotina \-e POSTGRES_USER=guillotina \-p 127.0.0.1:5432:5432 \--name postgres postgres:9.6

3.1.4 Creating to-do type

Types consist of an interface (schema) using the excellent zope.interface package and a class that uses thatinterface.

Create a guillotina_todo/content.py file with the following:

from guillotina import configurefrom guillotina import schemafrom guillotina import interfacesfrom guillotina import content

class IToDo(interfaces.IItem):text = schema.Text()

@configure.contenttype(type_name="ToDo",schema=IToDo)

class ToDo(content.Item):"""Our ToDo type"""

Then, we want to make sure our content type configuration is getting loaded, so add this to your __init__.pyincludeme function:

from guillotina import configureconfigure.scan('guillotina_todo.content')

3.1.5 Running

You run you application by using the guillotina command runner again:

guillotina serve -c config.yaml

3.1.6 Creating your todo list

Create container first: http

114 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

POST /db/ HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"@type": "Container","description": "My todo list","id": "todo","title": "ToDo List"

}

curl

curl -i -X POST http://localhost:8080/db/ -H 'Accept: application/json' -H 'Content-→˓Type: application/json' --data-raw '{"@type": "Container", "description": "My todo→˓list", "id": "todo", "title": "ToDo List"}' --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":→˓"My todo list", "id": "todo", "title": "ToDo List"}' --auth-no-challenge --→˓user=root --password=root

httpie

echo '{"@type": "Container","description": "My todo list","id": "todo","title": "ToDo List"

}' | 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': 'My todo list','id': 'todo','title': 'ToDo List',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/jsonLocation: /db/todo

{"@type": "Container","id": "todo",

3.1. Getting started 115

guillotina Documentation, Release 4.2.11

"title": "ToDo List"}

Install your todo list application: http

POST /db/todo/@addons HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"id": "guillotina_todo"

}

curl

curl -i -X POST http://localhost:8080/db/todo/@addons -H 'Accept: application/json' -→˓H 'Content-Type: application/json' --data-raw '{"id": "guillotina_todo"}' --user→˓root:root

wget

wget -S -O- http://localhost:8080/db/todo/@addons --header='Accept: application/json'→˓--header='Content-Type: application/json' --post-data='{"id": "guillotina_todo"}' --→˓auth-no-challenge --user=root --password=root

httpie

echo '{"id": "guillotina_todo"

}' | http POST http://localhost:8080/db/todo/@addons Accept:application/json Content-→˓Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo/@addons', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'id': 'guillotina_todo',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

{"available": [

{"id": "guillotina_todo","title": "Guillotina server application python project"

}],"installed": [

"guillotina_todo"

116 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

]}

Add todo items: http

POST /db/todo HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"@type": "ToDo","text": "Get milk"

}

curl

curl -i -X POST http://localhost:8080/db/todo -H 'Accept: application/json' -H→˓'Content-Type: application/json' --data-raw '{"@type": "ToDo", "text": "Get milk"}'→˓--user root:root

wget

wget -S -O- http://localhost:8080/db/todo --header='Accept: application/json' --→˓header='Content-Type: application/json' --post-data='{"@type": "ToDo", "text": "Get→˓milk"}' --auth-no-challenge --user=root --password=root

httpie

echo '{"@type": "ToDo","text": "Get milk"

}' | http POST http://localhost:8080/db/todo Accept:application/json Content-→˓Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'@type': 'ToDo','text': 'Get milk',

}, auth=('root', 'root'))

response

HTTP/1.1 201 CreatedContent-Type: application/jsonLocation: http://localhost:8080/db/todo/385ac34a49bc406f8494600c50b99a85

{"@id": "http://localhost:8080/db/todo/385ac34a49bc406f8494600c50b99a85","@name": "385ac34a49bc406f8494600c50b99a85","@type": "ToDo","@uid": "5c9|385ac34a49bc406f8494600c50b99a85",

3.1. Getting started 117

guillotina Documentation, Release 4.2.11

"UID": "5c9|385ac34a49bc406f8494600c50b99a85"}

http

POST /db/todo HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"@type": "ToDo","text": "Do laundry"

}

curl

curl -i -X POST http://localhost:8080/db/todo -H 'Accept: application/json' -H→˓'Content-Type: application/json' --data-raw '{"@type": "ToDo", "text": "Do laundry"}→˓' --user root:root

wget

wget -S -O- http://localhost:8080/db/todo --header='Accept: application/json' --→˓header='Content-Type: application/json' --post-data='{"@type": "ToDo", "text": "Do→˓laundry"}' --auth-no-challenge --user=root --password=root

httpie

echo '{"@type": "ToDo","text": "Do laundry"

}' | http POST http://localhost:8080/db/todo Accept:application/json Content-→˓Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'@type': 'ToDo','text': 'Do laundry',

}, auth=('root', 'root'))

response

HTTP/1.1 201 CreatedContent-Type: application/jsonLocation: http://localhost:8080/db/todo/77332e3153a54924b9b36eb263848826

{"@id": "http://localhost:8080/db/todo/77332e3153a54924b9b36eb263848826","@name": "77332e3153a54924b9b36eb263848826","@type": "ToDo","@uid": "5c9|77332e3153a54924b9b36eb263848826",

118 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

"UID": "5c9|77332e3153a54924b9b36eb263848826"}

Get a list of todo items: http

GET /db/todo HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Host: localhost:8080

curl

curl -i http://localhost:8080/db/todo -H 'Accept: application/json' --user root:root

wget

wget -S -O- http://localhost:8080/db/todo --header='Accept: application/json' --auth-→˓no-challenge --user=root --password=root

httpie

http http://localhost:8080/db/todo Accept:application/json -a root:root

python-requests

requests.get('http://localhost:8080/db/todo', headers={'Accept': 'application/json',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

{"@id": "http://localhost:8080/db/todo","@name": "todo","@type": "Container","@uid": "5c9932350eaf4ff189d7db934222216b","UID": "5c9932350eaf4ff189d7db934222216b","__behaviors__": [],"__name__": "todo","creation_date": "2018-07-21T15:39:11.411162+00:00","is_folderish": true,"items": [

{"@id": "http://localhost:8080/db/todo/385ac34a49bc406f8494600c50b99a85","@name": "385ac34a49bc406f8494600c50b99a85","@type": "ToDo","@uid": "5c9|385ac34a49bc406f8494600c50b99a85","UID": "5c9|385ac34a49bc406f8494600c50b99a85"

},{

"@id": "http://localhost:8080/db/todo/77332e3153a54924b9b36eb263848826","@name": "77332e3153a54924b9b36eb263848826","@type": "ToDo","@uid": "5c9|77332e3153a54924b9b36eb263848826","UID": "5c9|77332e3153a54924b9b36eb263848826"

3.1. Getting started 119

guillotina Documentation, Release 4.2.11

}],"length": 2,"modification_date": "2018-07-21T15:39:11.411162+00:00","parent": {},"title": "ToDo List","type_name": "Container","uuid": "5c9932350eaf4ff189d7db934222216b"

}

3.2 Security

Guillotina provides an imperative security system. Permissions are computed for a given node in the resource treeusing some concept we are going to describe in this document.

3.2.1 Basics

We’ll be explaining the security system by showing examples. Fist, make sure to follow the steps from Getting started.

Now you should have a resource tree that we can represent like the following:

dbtodo

<fist_todo_id><second_todo_id>

Where db is the database, todo a container with to content inside.

More than that we need some users in order to be able to compute permssion(s) for them, to do so we are going toinstall guillotina_dbusers, once installed create two users, let’s say "Bob" and "Alice". You can find more informationsabout this addon especially how to get Bearer Authorization JWT see training’s users section.

Note that at this moment the resource tree can be represented like this:

dbtodo

<fist_todo_id><second_todo_id>users

BobAlice

groups

Now login with "Bob" and try access /db/todo endpoint: http

GET /db/todo/ HTTP/1.1Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-IHost: localhost:8080

curl

curl -i http://localhost:8080/db/todo/ -H 'Authorization: Bearer→˓eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-→˓JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I'

120 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

wget

wget -S -O- http://localhost:8080/db/todo/ --header='Authorization: Bearer→˓eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-→˓JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I'

httpie

http http://localhost:8080/db/todo/ Authorization:'Bearer→˓eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-→˓JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I'

python-requests

requests.get('http://localhost:8080/db/todo/', headers={'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.

→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I→˓',})

response

HTTP/1.1 401 UnauthorizedContent-Type: application/json

{"auths": [

"Bob"],"content": "< Container at /todo by 140237937521992 >","reason": "You are not authorized to view"

}

Like you can see in the response you are not authorized to view, and that’s great because it means that the securitysystem works like a charm.

Let’s grant "Bob" view permission for this db/todo/ resource tree node: http

POST /db/todo/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"prinperm": [

{"permission": "guillotina.ViewContent","principal": "Bob","setting": "Allow"

}]

}

curl

curl -i -X POST http://localhost:8080/db/todo/@sharing -H 'Accept: application/json' -→˓H 'Content-Type: application/json' --data-raw '{"prinperm": [{"permission":→˓"guillotina.ViewContent", "principal": "Bob", "setting": "Allow"}]}' --user→˓root:root

3.2. Security 121

guillotina Documentation, Release 4.2.11

wget

wget -S -O- http://localhost:8080/db/todo/@sharing --header='Accept: application/json→˓' --header='Content-Type: application/json' --post-data='{"prinperm": [{"permission→˓": "guillotina.ViewContent", "principal": "Bob", "setting": "Allow"}]}' --auth-no-→˓challenge --user=root --password=root

httpie

echo '{"prinperm": [{

"permission": "guillotina.ViewContent","principal": "Bob","setting": "Allow"

}]

}' | http POST http://localhost:8080/db/todo/@sharing Accept:application/json Content-→˓Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo/@sharing', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'prinperm': [{

'permission': 'guillotina.ViewContent','principal': 'Bob','setting': 'Allow',

}],}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

You can now access to /db/todo endpoint using Bob user: http

GET /db/todo/ HTTP/1.1Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-IHost: localhost:8080

curl

curl -i http://localhost:8080/db/todo/ -H 'Authorization: Bearer→˓eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-→˓JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I'

wget

wget -S -O- http://localhost:8080/db/todo/ --header='Authorization: Bearer→˓eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-→˓JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I'

122 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

httpie

http http://localhost:8080/db/todo/ Authorization:'Bearer→˓eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-→˓JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I'

python-requests

requests.get('http://localhost:8080/db/todo/', headers={'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.

→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I→˓',})

response

HTTP/1.1 200 OKContent-Type: application/json

{"@id": "http://localhost:8080/db/todo","@name": "todo","@type": "Container","@uid": "6e63e13b4d1647d5a4ef5ef61ea040f1","UID": "6e63e13b4d1647d5a4ef5ef61ea040f1","__behaviors__": [],"__name__": "todo","creation_date": "2018-07-22T07:33:19.098099+00:00","is_folderish": true,"items": [

{"@id": "http://localhost:8080/db/todo/9eca9e3e84ce4e79883f19fdbbe694b1","@name": "9eca9e3e84ce4e79883f19fdbbe694b1","@type": "ToDo","@uid": "6e6|9eca9e3e84ce4e79883f19fdbbe694b1","UID": "6e6|9eca9e3e84ce4e79883f19fdbbe694b1"

},{

"@id": "http://localhost:8080/db/todo/ae45417c8115463aa2d6437de3577d02","@name": "ae45417c8115463aa2d6437de3577d02","@type": "ToDo","@uid": "6e6|ae45417c8115463aa2d6437de3577d02","UID": "6e6|ae45417c8115463aa2d6437de3577d02"

},{

"@id": "http://localhost:8080/db/todo/groups","@name": "groups","@type": "GroupManager","@uid": "6e6|ff31eda7808044488dc492d2075e4e13","UID": "6e6|ff31eda7808044488dc492d2075e4e13"

},{

"@id": "http://localhost:8080/db/todo/users","@name": "users","@type": "UserManager","@uid": "6e6|753405ce2dfe4455930c8fc850f38157","UID": "6e6|753405ce2dfe4455930c8fc850f38157"

}],

3.2. Security 123

guillotina Documentation, Release 4.2.11

"length": 4,"modification_date": "2018-07-22T09:33:13.486834+00:00","parent": {},"title": "ToDo List","type_name": "Container","uuid": "6e63e13b4d1647d5a4ef5ef61ea040f1"

}

What we’ve done so far looks like we’ve grant user "Bob" view access to this node, but that’s not totally true.

As you can see in the permission definition we grant permission to a principal. A principal is like a tag and we grantpermission to that tag. The user’s id is the "principal" here but more on that later.

Another important thing is the setting attribute, which defined the permission propagation in the resource tree,this attribute can have only three value:

• Allow: set on resource and children will inherit

• Deny: set on resource and children will inherit (good way to stop propagation)

• AllowSingle: set on resource and children will not inherit (also a good way to stop propagation)

• Unset: you remove the setting

Note that we’ve defined a permission with "Allow" propagation setting at this level of the resource tree:

dbtodo <-- permission was granted here

<fist_todo_id><second_todo_id>users

BobAlice

groups

Which means that user "Bob" can view todo container, but also all todo, users and groups, but cannot db database.Try it.

The last parameter in our permission definition we’ve talk so far is the permission parameter itself, Guillotina provideda lot of permission by default, you can find an exhaustive like by reading Guillotina permissions definitions from thesource code. Most of the permissions your application will need should be defined there, but obviously you can alsodefined your own, more on that later.

3.2.2 Groups

Groups can be assigned to users, each group names are also principals, this is the way we can add principals to users.

Let’s add a group named todo_viewer: http

POST /db/todo/groups HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"@type": "Group","name": "todo_viewer"

}

124 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

curl

curl -i -X POST http://localhost:8080/db/todo/groups -H 'Accept: application/json' -H→˓'Content-Type: application/json' --data-raw '{"@type": "Group", "name": "todo_viewer→˓"}' --user root:root

wget

wget -S -O- http://localhost:8080/db/todo/groups --header='Accept: application/json' -→˓-header='Content-Type: application/json' --post-data='{"@type": "Group", "name":→˓"todo_viewer"}' --auth-no-challenge --user=root --password=root

httpie

echo '{"@type": "Group","name": "todo_viewer"

}' | http POST http://localhost:8080/db/todo/groups Accept:application/json Content-→˓Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo/groups', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'@type': 'Group','name': 'todo_viewer',

}, auth=('root', 'root'))

response

HTTP/1.1 201 CreatedContent-Type: application/jsonLocation: http://localhost:8080/db/todo/groups/14f624ef23094362961df0e083cd77e4

{"@id": "http://localhost:8080/db/todo/groups/14f624ef23094362961df0e083cd77e4","@name": "14f624ef23094362961df0e083cd77e4","@type": "Group","@uid": "6e6|ff3|14f624ef23094362961df0e083cd77e4","UID": "6e6|ff3|14f624ef23094362961df0e083cd77e4"

}

And add "Bob" and "Alice" to that group. http

PATCH /db/todo/users/Bob HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"user_groups": [

"todo_viewer"]

}

curl

3.2. Security 125

guillotina Documentation, Release 4.2.11

curl -i -X PATCH http://localhost:8080/db/todo/users/Bob -H 'Accept: application/json→˓' -H 'Content-Type: application/json' --data-raw '{"user_groups": ["todo_viewer"]}'→˓--user root:root

wget

wget -S -O- --method=PATCH http://localhost:8080/db/todo/users/Bob --header='Accept:→˓application/json' --header='Content-Type: application/json' --body-data='{"user_→˓groups": ["todo_viewer"]}' --auth-no-challenge --user=root --password=root

httpie

echo '{"user_groups": ["todo_viewer"

]}' | http PATCH http://localhost:8080/db/todo/users/Bob Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.patch('http://localhost:8080/db/todo/users/Bob', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'user_groups': ['todo_viewer'],

}, auth=('root', 'root'))

response

HTTP/1.1 204 No ContentContent-Type: application/json

http

PATCH /db/todo/users/Alice HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"user_groups": [

"todo_viewer"]

}

curl

curl -i -X PATCH http://localhost:8080/db/todo/users/Alice -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"user_groups": ["todo_viewer→˓"]}' --user root:root

wget

wget -S -O- --method=PATCH http://localhost:8080/db/todo/users/Alice --header=→˓'Accept: application/json' --header='Content-Type: application/json' --body-data='{→˓"user_groups": ["todo_viewer"]}' --auth-no-challenge --user=root --password=root

126 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

httpie

echo '{"user_groups": ["todo_viewer"

]}' | http PATCH http://localhost:8080/db/todo/users/Alice Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.patch('http://localhost:8080/db/todo/users/Alice', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'user_groups': ['todo_viewer'],

}, auth=('root', 'root'))

response

HTTP/1.1 204 No ContentContent-Type: application/json

Let’s grant todo_viewer view permission for this db/todo/ resource tree node: http

POST /db/todo/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"prinperm": [

{"permission": "guillotina.ViewContent","principal": "todo_viewer","setting": "Allow"

}]

}

curl

curl -i -X POST http://localhost:8080/db/todo/@sharing -H 'Accept: application/json' -→˓H 'Content-Type: application/json' --data-raw '{"prinperm": [{"permission":→˓"guillotina.ViewContent", "principal": "todo_viewer", "setting": "Allow"}]}' --user→˓root:root

wget

wget -S -O- http://localhost:8080/db/todo/@sharing --header='Accept: application/json→˓' --header='Content-Type: application/json' --post-data='{"prinperm": [{"permission→˓": "guillotina.ViewContent", "principal": "todo_viewer", "setting": "Allow"}]}' --→˓auth-no-challenge --user=root --password=root

httpie

3.2. Security 127

guillotina Documentation, Release 4.2.11

echo '{"prinperm": [{

"permission": "guillotina.ViewContent","principal": "todo_viewer","setting": "Allow"

}]

}' | http POST http://localhost:8080/db/todo/@sharing Accept:application/json Content-→˓Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo/@sharing', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'prinperm': [{

'permission': 'guillotina.ViewContent','principal': 'todo_viewer','setting': 'Allow',

}],}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

Now alice should be able to view todo container and all it’s children.

At the moment alice can view users and groups which is not convenient for a todo_viewer group, let’s deny that.http

POST /db/todo/users/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"prinperm": [

{"permission": "guillotina.ViewContent","principal": "todo_viewer","setting": "Deny"

}]

}

curl

curl -i -X POST http://localhost:8080/db/todo/users/@sharing -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"prinperm": [{"permission":→˓"guillotina.ViewContent", "principal": "todo_viewer", "setting": "Deny"}]}' --user→˓root:root

wget

128 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

wget -S -O- http://localhost:8080/db/todo/users/@sharing --header='Accept:→˓application/json' --header='Content-Type: application/json' --post-data='{"prinperm→˓": [{"permission": "guillotina.ViewContent", "principal": "todo_viewer", "setting":→˓"Deny"}]}' --auth-no-challenge --user=root --password=root

httpie

echo '{"prinperm": [{

"permission": "guillotina.ViewContent","principal": "todo_viewer","setting": "Deny"

}]

}' | http POST http://localhost:8080/db/todo/users/@sharing Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo/users/@sharing', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'prinperm': [{

'permission': 'guillotina.ViewContent','principal': 'todo_viewer','setting': 'Deny',

}],}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

http

POST /db/todo/groups/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"prinperm": [

{"permission": "guillotina.ViewContent","principal": "todo_viewer","setting": "Deny"

}]

}

curl

curl -i -X POST http://localhost:8080/db/todo/groups/@sharing -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"prinperm": [{"permission":→˓"guillotina.ViewContent", "principal": "todo_viewer", "setting": "Deny"}]}' --user→˓root:root

3.2. Security 129

guillotina Documentation, Release 4.2.11

wget

wget -S -O- http://localhost:8080/db/todo/groups/@sharing --header='Accept:→˓application/json' --header='Content-Type: application/json' --post-data='{"prinperm→˓": [{"permission": "guillotina.ViewContent", "principal": "todo_viewer", "setting":→˓"Deny"}]}' --auth-no-challenge --user=root --password=root

httpie

echo '{"prinperm": [{

"permission": "guillotina.ViewContent","principal": "todo_viewer","setting": "Deny"

}]

}' | http POST http://localhost:8080/db/todo/groups/@sharing Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo/groups/@sharing', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'prinperm': [{

'permission': 'guillotina.ViewContent','principal': 'todo_viewer','setting': 'Deny',

}],}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

Now both Alice and Bob can’t access to users and groups, if you want Bob to be able to access those endpoints youshould explicitly set permission for principal "Bob" on those ones.

3.2.3 Roles

Roles are granted permissions, which means that a principal with one role will inherit all that role permissions.

Guillotina defined serval default roles, see developer roles section. But remember that you can defined your ownones(more on that later).

For example let’s give to principal "Alice" the guillotina.Editor role on /db/todo/<first_todo_id>resource tree node, which grants the following permissions:

• guillotina.AccessContent

• guillotina.ViewContent

• guillotina.ModifyContent

• guillotina.ReindexContent

130 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

To do use run (don’t forget to replace <first_todo_id_> with your first todo id): http

POST /db/todo/<first_todo_id>/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"prinrole": [

{"principal": "Alice","Role": "guillotina.Editor","setting": "Allow"

}]

}

curl

curl -i -X POST 'http://localhost:8080/db/todo/<first_todo_id>/@sharing' -H 'Accept:→˓application/json' -H 'Content-Type: application/json' --data-raw '{"prinrole": [{→˓"principal": "Alice", "Role": "guillotina.Editor", "setting": "Allow"}]}' --user→˓root:root

wget

wget -S -O- 'http://localhost:8080/db/todo/<first_todo_id>/@sharing' --header=→˓'Accept: application/json' --header='Content-Type: application/json' --post-data='{→˓"prinrole": [{"principal": "Alice", "Role": "guillotina.Editor", "setting": "Allow"}→˓]}' --auth-no-challenge --user=root --password=root

httpie

echo '{"prinrole": [{

"Role": "guillotina.Editor","principal": "Alice","setting": "Allow"

}]

}' | http POST 'http://localhost:8080/db/todo/<first_todo_id>/@sharing'→˓Accept:application/json Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo/<first_todo_id>/@sharing', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'prinrole': [{

'principal': 'Alice','Role': 'guillotina.Editor','setting': 'Allow',

}],}, auth=('root', 'root'))

response

3.2. Security 131

guillotina Documentation, Release 4.2.11

HTTP/1.1 200 OKContent-Type: application/json

Now Alice can access, view, modify and reindex the first todo but Bob still only view to it.

You can also add permission for a role at/from a given resource tree node for a given role, for example at the momentprincipal "Alice" which have "guillotina.Editor" role on /db/todo/<first_todo_id> cannot delete it.

Let’s fix that by giving "guillotina.DeleteContent" permission to "guillotina.Editor" role at this specific resource treenode: http

POST /db/todo/<first_todo_id>/@sharing HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"roleperm": [

{"permission": "guillotina.DeleteContent","Role": "guillotina.Editor","setting": "Allow"

}]

}

curl

curl -i -X POST 'http://localhost:8080/db/todo/<first_todo_id>/@sharing' -H 'Accept:→˓application/json' -H 'Content-Type: application/json' --data-raw '{"roleperm": [{→˓"permission": "guillotina.DeleteContent", "Role": "guillotina.Editor", "setting":→˓"Allow"}]}' --user root:root

wget

wget -S -O- 'http://localhost:8080/db/todo/<first_todo_id>/@sharing' --header=→˓'Accept: application/json' --header='Content-Type: application/json' --post-data='{→˓"roleperm": [{"permission": "guillotina.DeleteContent", "Role": "guillotina.Editor",→˓ "setting": "Allow"}]}' --auth-no-challenge --user=root --password=root

httpie

echo '{"roleperm": [{

"Role": "guillotina.Editor","permission": "guillotina.DeleteContent","setting": "Allow"

}]

}' | http POST 'http://localhost:8080/db/todo/<first_todo_id>/@sharing'→˓Accept:application/json Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/todo/<first_todo_id>/@sharing', headers={'Accept': 'application/json',

132 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

'Content-Type': 'application/json',}, json={

'roleperm': [{'permission': 'guillotina.DeleteContent','Role': 'guillotina.Editor','setting': 'Allow',

}],}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

Now anyone who have "guillotina.Editor" role on that node, including only Alice at the moment, will be able to deleteit.

3.2.4 Security Levels

Security for every operation is managed against three definitions (in order of priority):

• Local

• Global

• Code

"Local" means for a given resource tree node.

"Global" stand for application level.

And finally "Code" for code level, like services, containers or whatever code exposed to the API.

This means that principal or role could be mandatory to acces them.

Locals can defined:

• Permission for principal with propagation definition

• Role for principal with propagation definition

• Permission for role with propagation definition

Globals:

• Role for principal

• Permission for principal

Code:

• Role for principal

• Permission for principal

• Permission for role

Roles

There are two kind of roles: Global and Local. The ones that are defined to be local can’t be used globally andvice-versa. On indexing, the global roles are the ones that are indexed for security in addition to the flat user/groupinformation from each resource.

3.2. Security 133

guillotina Documentation, Release 4.2.11

3.2.5 Python helper functions

# Code to get the global roles that have access_content to an objectfrom guillotina.security.utils import get_roles_with_access_contentget_roles_with_access_content(obj)

# Code to get the user list that have access content to an objectfrom guillotina.security.utils import get_principals_with_access_contentget_principals_with_access_content(obj)

# Code to get all the security infofrom guillotina.security.utils import settings_for_objectsettings_for_object(obj)

# Code to get the Interaction object ( security object )from guillotina.interfaces import IInteraction

interaction = IInteraction(request)

# Get the list of global roles for a user and some groupsinteraction.global_principal_roles(principal, groups)

# Get if the authenticated user has permission on a objectinteraction.check_permission(permission, obj)

3.2.6 REST APIs

Get all the endpoints and their security

[GET] APPLICATION_URL/@apidefinition (you need guillotina.GetContainers permission)

Get the security info for a resource (with inherited info)

[GET] RESOURCE/@sharing (you need guillotina.SeePermissions permission)

Modify the local roles/permission for a resource

[POST] RESOURCE/@sharing (you need guillotina.ChangePermissions permission)

{"prinperm": [

{"principal": "foobar","permission": "guillotina.ModifyContent","setting": "Allow"

}],"prinrole": [

{"principal": "foobar","role": "guillotina.Owner","setting": "Allow"

134 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

}],"roleperm": [

{"permission": "guillotina.ModifyContent","role": "guillotina.Member","setting": "Allow"

}]}

Propagation setting

The different types are:

• Allow: set on resource and children will inherit

• Deny: set on resource and children will inherit (good way to stop propagation)

• AllowSingle: set on resource and children will not inherit (also a good way to stop propagation)

• Unset: you remove the setting

3.3 Roles

guillotina implements robust ACL security.

An overview of our security features are:

• Users are given roles and groups

• Roles are granted permissions

• Groups are granted roles

• Roles can be granted to users on specific objects

3.3.1 Requests security

By default request has participation of anonymous user plus the ones added by auth plugins

3.3.2 Databases, Application and static files objects

Databases and static files have a specific permission system. They don’t have roles by default and the permissions arespecified to root user

• guillotina.AddContainer

• guillotina.GetContainers

• guillotina.DeleteContainers

• guillotina.AccessContent

• guillotina.GetDatabases

Anonymous user has on DB/StaticFiles/StaticDirectories/Application object :

3.3. Roles 135

guillotina Documentation, Release 4.2.11

• guillotina.AccessContent

3.3.3 Roles in guillotina container objects

Defined at:

• guillotina/permissions.py

3.3.4 Content Related

guillotina.Anonymous

• guillotina.AccessPreflight

guillotina.Member

• guillotina.AccessContent

guillotina.Reader

• guillotina.AccessContent

• guillotina.ViewContent

guillotina.Editor

• guillotina.AccessContent

• guillotina.ViewContent

• guillotina.ModifyContent

• guillotina.ReindexContent

guillotina.Reviewer

guillotina.Owner

• guillotina.AccessContent

• guillotina.ViewContent

• guillotina.ModifyContent

• guillotina.DeleteContent

• guillotina.AddContent

• guillotina.ChangePermissions

• guillotina.SeePermissions

• guillotina.ReindexContent

136 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

3.3.5 Container/App Roles

guillotina.ContainerAdmin

• guillotina.AccessContent

• guillotina.ManageAddons

• guillotina.RegisterConfigurations

• guillotina.WriteConfiguration

• guillotina.ReadConfiguration

• guillotina.ManageCatalog

guillotina.ContainerDeleter

• guillotina.DeletePortal

3.3.6 Default roles on Guillotina Container

They are stored in annotations using IRolePermissionMap.

Created objects set the guillotina.Owner role to the user who created it.

3.3.7 Default groups on Guillotina Container

Managers

RootParticipation

There is a root user who has permissions to all containers:

DB/APP permissions are defined on factory/content.py

3.4 Applications

Applications are used to provide additional functionality to guillotina.

3.4.1 Community Addons

Some useful addons to use in your own development:

• guillotina_elasticsearch: Index content in elastic search

• guillotina_pgcatalog: Index content in postgresql

• guillotina_dbusers: Store and authenticate users in the database

• guillotina_swagger: Automatic swagger support

• guillotina_mailer: async send mail

3.4. Applications 137

guillotina Documentation, Release 4.2.11

3.4.2 Creating

An application is a Python package that implements an entry point to tell guillotina to load it.

If you’re not familiar with how to build Python applications, please read documentation on building packages beforeyou continue.

In this example, guillotina_myaddon is your package module.

3.4.3 Initialization

Your config.yaml file will need to provide the application name in the applications array for it to be initial-ized.

applications:- guillotina_myaddon

3.4.4 Configuration

Once you create a guillotina application, there are two primary ways for it to hook into guillotina.

Call the includeme function

Your application can provide an includeme function at the root of the module and guillotina will call it withthe instance of the root object.

def includeme(root):# do initialization here...pass

Load app_settings

If an app_settings dict is provided at the module root, it will automatically merge the global guillotinaapp_settings with the module’s. This allows you to provide custom configuration.

3.5 Add-ons

Addons are integrations that can be installed or uninstalled against a Guillotina container. guillotina applicationscan potentially provide many addons. If you have not read the section on applications, please read that before youcome here. The only way to provide addons is to first implement a guillotina application.

3.5.1 Creating an add-on

Create an addon installer class in an install.py file in your guillotina application:

138 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

from guillotina.addons import Addonfrom guillotina import configure

@configure.addon(name="myaddon",title="My addon",dependencies=['cms'])

class MyAddon(Addon):

@classmethoddef install(cls, container, request):

# install codepass

@classmethoddef uninstall(cls, container, request):

# uninstall codepass

Note: Scanning

If your service modules are not imported at run-time, you may need to provide an additional scan call to get yourservices noticed by guillotina.

In your application __init__.py file, you can simply provide a scan call like:

from guillotina import configuredef includeme(root):

configure.scan('my.package')

3.5.2 Layers

A Layer is a marker you install with your add-on, this allows your application to lookup views and adapters (overridecore functionality) only for the container you installed the add-on.

from guillotina.addons import Addonfrom guillotina import configurefrom guillotina.interfaces import ILayers

LAYER = 'guillotina_myaddon.interfaces.ILayer'

@configure.addon(name="myaddon",title="My addon")

class MyAddon(Addon):

@classmethoddef install(cls, container, request):

registry = request.container_settingsregistry.for_interface(ILayers).active_layers |= {

LAYER}

3.5. Add-ons 139

guillotina Documentation, Release 4.2.11

@classmethoddef uninstall(cls, container, request):

registry = request.container_settingsregistry.for_interface(ILayers).active_layers -= {

LAYER}

3.5.3 Installing an addon into a container

Addons can be installed into a container using @addons endpoint by providing addon name as id For example: http

POST /db/container/@addons HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"id": "myaddon"

}

curl

curl -i -X POST http://localhost:8080/db/container/@addons -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"id": "myaddon"}' --user→˓root:root

wget

wget -S -O- http://localhost:8080/db/container/@addons --header='Accept: application/→˓json' --header='Content-Type: application/json' --post-data='{"id": "myaddon"}' --→˓auth-no-challenge --user=root --password=root

httpie

echo '{"id": "myaddon"

}' | http POST http://localhost:8080/db/container/@addons Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/container/@addons', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'id': 'myaddon',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

{"available": [

140 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

{"id": "myaddon","title": "Guillotina DB Users"

},{

"id": "application_name","title": "Your application title"

}],"installed": [

"dbusers","application_name"

]}

3.6 Services

Services provide responses to API endpoint requests. A service is the same as a "view" that you might see in manyweb frameworks.

The reason we’re using the convention "service" is because we’re focusing on creating API endpoints.

3.6.1 Defining a service

A service can be as simple as a function in your application:

from guillotina import configurefrom guillotina.interfaces import IContainer

@configure.service(context=IContainer, name='@myservice', method='GET',permission='guillotina.AccessContent')

async def my_service(context, request):return {

'foo': 'bar'}

The most simple way to define a service is to use the decorator method shown here.

As long as your application imports the module where your service is defined, your service will be loaded for you.

In this example, the service will apply to a GET request against a container, /zodb/guillotina/@myservice.

Note: Scanning

If your service modules are not imported at run-time, you may need to provide an additional scan call to get yourservices noticed by guillotina.

In your application __init__.py file, you can simply provide a scan call like:

from guillotina import configuredef includeme(root):

configure.scan('my.package')

3.6. Services 141

guillotina Documentation, Release 4.2.11

3.6.2 Class-based services

For more complex services, you might want to use class-based services.

With the class-based approach, the example above will look like this:

from guillotina import configurefrom guillotina.interfaces import IContainerfrom guillotina.api.service import Service

@configure.service(context=IContainer, name='@myservice', method='GET',permission='guillotina.AccessContent')

class MyService(Service):async def __call__(self):

return {'foo': 'bar'

}

3.6.3 Special cases

I want that my service is accessible no matter the content

You can define in the service configuration with allow_acces=True

from guillotina import configurefrom guillotina.interfaces import [email protected](

context=IResource, name='@download',method='GET', permission='guillotina.Public',allow_access=True)

async def my_service(context, request):pass

3.7 Response rendering

Guillotina has a rendering framework to be able to dynamically handle multiple request Accept headers.

Out of the box, Guillotina only supports application/json, text/html and text/plain dynamic responsetypes. Streaming and web socket type responses are also supported but are not handled by the dyanamic renderingframework.

3.7.1 Customizing responses

Services can provide simple type values for responses. Ideally anything that can be serialized as json(default renderer).

Additionally, services can provide custom response objects to customize status and header.

from guillotina import configurefrom guillotina.interfaces import IResourcefrom guillotina.response import Response

@configure.service(context=IResource, name='@custom-status',

142 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

method='GET', permission='guillotina.Public',allow_access=True)

async def custom_status(context, request):return {'foo': 'bar'}, 201

@configure.service(context=IResource, name='@custom-headers',method='GET', permission='guillotina.Public',allow_access=True)

async def custom_headers(context, request):return Response(content={'foo': 'bar'}, status=200, headers={'X-Foobar', 'foobar'}

→˓)

Response types

Guillotina will automatically transform any response types in the guillotina.response library.

These response objects should have simple dict values for their content if provided.

Bypassing reponses rendering

If you return any aiohttp based response objects, they will be ignored by the rendering framework.

This is useful when streaming data for example and it should not be transformed.

Custom rendering

It’s also very easy to provide your own renderer. All you need to do is provide your own renderer class and configureit with the configuration object.

Here is a yaml example:

from guillotina import configurefrom guillotina.renderer import Rendererimport yaml

# yaml is known to have a lot of different content types, it's [email protected](name='text/vnd.yaml')@configure.renderer(name='application/yaml')@configure.renderer(name='application/x-yaml')@configure.renderer(name='text/x-yaml')@configure.renderer(name='text/yaml')class RendererYaml(Renderer):

content_type = 'application/yaml'

def get_body(self, value) -> bytes:if value is not None:

value = yaml.dump(value)return value.encode('utf-8')

3.7. Response rendering 143

guillotina Documentation, Release 4.2.11

3.8 Content types

Content types allow you to provide custom schemas and content to your services.

Out-of-the-box, guillotina ships with simple Container, Folder and Item content types. The Containercontent type is the main content type to hold your data in. It is the starting point for applications and other things tooperate within.

The Folder type allows someone to add items inside of it. Both types only have simple Dublin Core fields by default.

3.8.1 Defining content types

A content type consists of a class and optionally, a schema to define the custom fields you want your class to use.

A simple type will look like this::

from guillotina import configurefrom guillotina.content import Folderfrom guillotina.interfaces import IItemfrom guillotina import schema

class IMySchema(IItem):foo = schema.Text()

@configure.contenttype(type_name="MyType",schema=IMySchema,behaviors=["guillotina.behaviors.dublincore.IDublinCore"])

class MyType(Folder):pass

This example creates a simple schema and assigns it to the MyType content type.

Note: Scanning

If your service modules are not imported at run-time, you may need to provide an additional scan call to get yourservices noticed by guillotina.

In your application __init__.py file, you can simply provide a scan call like:

from guillotina import configuredef includeme(root):

configure.scan('my.package')

3.9 Behaviors

Besides having static content types definitions with their schema, there is the concept of behaviors. This allows us toprovide functionality across content types, using specific marker interfaces to create adapters and subscribers based onthat behavior and not the content type.

144 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

3.9.1 Definition of a behavior

If you want to have a shared behavior based on some fields and operations that needs to be shared across differentcontent types, you can define them on a guillotina.schema interface:

from zope.interface import Interfacefrom guillotina.schema import Textline

class IMyLovedBehavior(Interface):text = Textline(

title=u'Text line field',required=False

)

text2 = Textline(title=u'Text line field',required=False

)

Once you define the schema you can define a specific marker interface that will be applied to the objects that has thisbehavior:

class IMarkerBehavior(Interface):"""Marker interface for content with attachment."""

Finally the instance class that implements the schema can be defined in case you want to enable specific operations. Oryou can use guillotina.behaviors.instance.AnnotationBehavior as the default annotation storage.

For example, in case you want to have a class that stores the field as content and not as annotations:

from guillotina.behaviors.properties import ContextPropertyfrom guillotina.behaviors.instance import AnnotationBehaviorfrom guillotina.interfaces import IResourcefrom guillotina import configure, schemafrom zope.interface import Interface

class IMarkerBehavior(Interface):"""Marker interface for content with attachment."""

class IMyBehavior(Interface):foobar = schema.TextLine()

@configure.behavior(title="Attachment",provides=IMyBehavior,marker=IMarkerBehavior,for_=IResource)

class MyBehavior(AnnotationBehavior):"""If attributes"""text = ContextProperty(u'attribute', ())

In this example text will be stored on the context object and text2 as a annotation.

3.9. Behaviors 145

guillotina Documentation, Release 4.2.11

3.9.2 Static behaviors

With behaviors you can define them as static for specific content types:

from guillotina import configurefrom guillotina.interfaces import IItemfrom guillotina.content import Item

@configure.contenttype(type_name="MyItem",schema=IItem,behaviors=["guillotina.behaviors.dublincore.IDublinCore"])

class MyItem(Item):pass

Note: Scanning

If your service modules are not imported at run-time, you may need to provide an additional scan call to get yourservices noticed by guillotina.

In your application __init__.py file, you can simply provide a scan call like:

from guillotina import configuredef includeme(root):

configure.scan('my.package')

Create and modify content with behaviors

For the deserialization of the content you will need to pass on the POST/PATCH operation the behavior as a object onthe JSON.

CREATE an ITEM with the expires : POST on parent:

{"@type": "Item","guillotina.behaviors.dublincore.IDublinCore": {

"expires": "1/10/2017"}

}

MODIFY an ITEM with the expires : PATCH on the object:

{"guillotina.behaviors.dublincore.IDublinCore": {

"expires": "1/10/2017"}

}

Get content with behaviors

On the serialization of the content you will get the behaviors as objects on the content.

GET an ITEM : GET on the object:

146 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

{"@id": "http://localhost:8080/zodb/guillotina/item1","guillotina.behaviors.dublincore.IDublinCore": {

"expires": "2017-10-01T00:00:00.000000+00:00","modified": "2016-12-02T14:14:49.859953+00:00",

}}

3.9.3 Dynamic Behaviors

guillotina offers the option to have content that has dynamic behaviors applied to them.

Which behaviors are available on a context

We can know which behaviors can be applied to a specific content.

GET CONTENT_URI/@behaviors:

{"available": ["guillotina.behaviors.attachment.IAttachment"],"static": ["guillotina.behaviors.dublincore.IDublinCore"],"dynamic": [],"guillotina.behaviors.attachment.IAttachment": { },"guillotina.behaviors.dublincore.IDublinCore": { }

}

This list of behaviors is based on the for statement on the configure of the behavior. The list of static ones are theones defined on the content type definition on the configure. The list of dynamic ones are the ones that have beenassigned.

Add a new behavior to a content

We can add a new dynamic behavior to a content using a PATCH operation on the object with the @behaviorattribute, or in a small PATCH operation to the @behavior entry point with the value to add.

MODIFY an ITEM with the expires : PATCH on the object:

{"guillotina.behaviors.dublincore.IDublinCore": {

"expires": "1/10/2017"}

}

MODIFY behaviors : PATCH on the object/@behaviors:

{"behavior": "guillotina.behaviors.dublincore.IDublinCore"

}

Delete a behavior to a content

We can add a new dynamic behavior to a content by a DELETE operation to the @behavior entry point with thevalue to remove.

3.9. Behaviors 147

guillotina Documentation, Release 4.2.11

DELETE behaviors : DELETE on the object/@behaviors:

{"behavior": "guillotina.behaviors.dublincore.IDublinCore"

}

3.9.4 Out-of-the-box Behaviors

Guillotina comes with a couple behaviors:

• guillotina.behaviors.dublincore.IDublinCore: Dublin core field

• guillotina.behaviors.attachment.IAttachment: Provide file field

3.10 Interfaces

guillotina uses interfaces to abstract and define various things including content. Interfaces are useful whendefining API contracts, using inheritance, defining schema/behaviors and being able to define which content yourservices are used for.

In the services example, you’ll notice the use of context=IContainer for the service decorator configuration. Inthat case, it is used to tell guillotina that the service is only defined for a container object.

Read the zope.interface docs for more details about the power of designing around around interfaces and to learn moreabout how to use it.

3.11 Events

Guillotina provides an event/subscriber pattern for dispatching events out to subscribers when "things" happen inGuillotina.

3.11.1 Subscribing

Subscribing to events is done with the configure module.

All subscribers are configured with:

1. the object type to match subcribe to the event against

2. the type of event you want to subscribe to

For example, to subscribe when an object is modified:

from guillotina import configurefrom guillotina.interfaces import IResourcefrom guillotina.interfaces import IObjectModifiedEvent

@configure.subscriber(for_=(IResource, IObjectModifiedEvent))async def modified_object(obj, event):

pass

148 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

3.11.2 Creating events

You are also able to create your own events to notify on:

from guillotina.interfaces import IObjectEventfrom zope.interface import implementerfrom guillotina.event import notify

class ICustomEvent(IObjectEvent):pass

@implementer(IObjectEvent)class CustomEvent:

def __init__(self, object):self.object = object

await notify(CustomEvent(ob))

3.11.3 Events

• guillotina.interfaces.IObjectEvent: every time anything happens to an object

• guillotina.interfaces.IFileStartedUpload

• guillotina.interfaces.IFileFinishUploaded

• guillotina.interfaces.IFileBeforeFinishUploaded

• guillotina.interfaces.IObjectLocationEvent: Base event for remove/rename

• guillotina.interfaces.IObjectMovedEvent

• guillotina.interfaces.IBeforeObjectMovedEvent

• guillotina.interfaces.IObjectAddedEvent: when content added to folder

• guillotina.interfaces.IObjectDuplicatedEvent

• guillotina.interfaces.IBeforeObjectAddedEvent

• guillotina.interfaces.IObjectRemovedEvent

• guillotina.interfaces.IBeforeObjectRemovedEvent

• guillotina.interfaces.IObjectModifiedEvent

• guillotina.interfaces.IObjectPermissionsModifiedEvent

• guillotina.interfaces.INewUserAdded

3.12 Commands

You can provide your own CLI commands for guillotina through a simple interface.

3.12. Commands 149

guillotina Documentation, Release 4.2.11

3.12.1 Available commands

• serve: run the HTTP REST API server (this is the default command if none given)

• shell: drop into a shell with root object to manually work with

• create: use cookiecutter to generate guillotina applications

• initialize-db: databases are automatically initialized; however, you can use this command to manuallydo it

• testdata: populate the database with test data from wikipedia

• run: run a python script. The file must have a function async def run(container):

3.12.2 Command Options

• –

– --config: path to configuration file. defaults to config.(yaml|json)

– --profile: profile Guillotina while it’s running

– --profile-output: where to save profiling output

– --monitor: run with aiomonitor requires aiomonitor

– --line-profiler: use line_profiler requires line_profiler

– --line-profiler-matcher: fnmatch of module/function to profile requiresline_profiler

– --line-profiler-output: to store output in a file requires line_profiler

– --override: Override configuration values, Example: --override="root_user.password=foobar"

• serve:

– --host: host to bind to

– --port: port to bind to

– --reload: auto reload on code changes. requires aiohttp_autoreload

• shell

• create

– --template: name of template to use

– --overwrite: overwrite existing file

– --output: where to save the file

• initialize-db

• testdata

– --per-node: How many items to import per node

– --depth: How deep to make the nodes

• run

– --script: path to script to run with run async function

150 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

3.12.3 Running commands

Guillotina provides two binaries to run commands through, bin/guillotina and a shortcut, bin/g.

To run a command, it’s just a positional argument on the running command::

bin/g shell

3.12.4 Creating commands

guillotina provides a simple API to write your own CLI commands.

Here is a minimalistic example:

from guillotina.commands import Commandclass MyCommand(Command):

def get_parser(self):parser = super(MyCommand, self).get_parser()# add command arguments here...return parser

def run(self, arguments, settings, app):pass

Then, just add your command to your application’s app_settings in the __init__.py:

app_settings = {"commands": {

"mycommand": "my.package.commands.MyCommand"}

}

3.13 Application Configuration

guillotina handles application configuration mostly with decorators.

For example, registering a new service uses our configuration decorator syntax:

from guillotina import configurefrom guillotina.interfaces import IContainer

@configure.service(context=IContainer, name='@myservice', method='GET',permission='guillotina.AccessContent')

async def my_service(context, request):return {

'foo': 'bar'}

guillotina applications can override default guillotina configuration.

If multiple guillotina applications configure conflicting configurations, guillotina chooses the configurationaccording to the order the guillotina applications that are included.

3.13. Application Configuration 151

guillotina Documentation, Release 4.2.11

A full reference of the available configure decorators can be found in the programming api reference section.

3.14 Design

This section is meant to explain and defend the design of guillotina.

3.14.1 JavaScript application development focus

One of the main driving factors behind the development of guillotina is to streamline the development of customweb applications.

Some of the technologies we support in order to be a great web application development platform are:

• Everything is an API endpoint

• JWT

• Web sockets

• Configuration is done with JSON

• URL to object-tree data model

3.14.2 Speed

A primary focus of guillotina is speed. We take shortcuts and may use some ugly or less-well conceptuallyarchitected solutions in some areas in order to gain speed improvements.

Some of the decisions we made affect how applications and addons are designed. Mainly, we try to stay light on theamount of data we’re loading from the database where possible and we try to lower the number of lookups we do incertain scenarios.

That being said, guillotina is not a barebones framework. It provides a lot of functionality so it will never be asfast as say Pyramid.

"There are no solutions. There are only trade-offs." - Thomas Sowell

3.14.3 Asynchronous

guillotina is asynchronous from the ground up, built on top of aiohttp using Python 3.6’s asyncio features.

Practically speaking, being built completely on asyncio compatible technologies, guillotina does not block fornetwork IO to the database, index catalog, redis, etc or whatever you’ve integrated.

Additionally, we have support for async utilities that run in the same async loop and async content events.

Finally, we also support web sockets OOTB.

3.14.4 Security

Guillotina uses the same great security infrastructure that Plone has been using for the last 15 years which allowsyou to define permissions, roles, groups, users and customize all of them contextually based on where the content islocated in your container.

152 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

3.14.5 Style

Stylistically, guillotina pulls ideas from the best web frameworks:

• YAML/JSON configuration

• Pyramid-like idioms and syntax where it makes sense

• Functions + decorators over classes

3.15 Persistence

There are three kinds of objects that are considered on the system:

• Tree objects: objects are resources that implement guillotina.interfaces.IResource. This objecthas a __name__ and a __parent__ property that indicate the id on the tree and the link to the parent. Bythemselves they don’t have access to their children, they need to interact with the transaction object to get them.

• Annotations: objects that are associated with tree objects. These can be any type of data. In Guillotina, the mainsource of annotation objects are behaviors.

3.15.1 Saving objects

If you’re manually modifying objects in services(or views) without using the serialization adapters, you need to registerthe object to be saved to the database. To do this, just use the _p_register() method.

from guillotina import [email protected](

method='PATCH', name='@dosomething')async def matching_service(context, request):

context.foobar = 'foobar'context._p_register()

3.15.2 Transactions

Guillotina automatically manages transactions for you in services; however, if you have long running services andneed to flush data to the database, you can manually manage transactions as well.

from guillotina.transactions import get_tm

tm = get_tm()await tm.commit() # commit current transactionawait tm.begin() # start new one

There is also an async context manager:

from guillotina.transactions import managed_transaction

async with managed_transaction() as txn:# modify objects

3.15. Persistence 153

guillotina Documentation, Release 4.2.11

3.16 Blobs

guillotina provides basic blob file persistency support. These blobs are still stored in the database.

3.16.1 Registering blobs

Blobs must be registered with and stored on a resource object. This is so we can do garbage collection on the blobsthat were created for resources.

from guillotina.blob import Blob

blob = Blob(resource)resource.blob = blobblobfi = blob.open('w')

await blobfi.async_write(b'foobar')assert await blobfi.async_read() == b'foobar'

Guillotina automatically reads and writes chunks of blob data from the database.

3.17 Router

Guillotina uses aiohttp for it’s webserver. In order to route requests against Guillotina’s traversal url structure,Guillotina provides it’s own router that does traversal: guillotina.traversal.router.

3.17.1 How URLs are routed

Guillotina’s content is structured like a file system. Objects are routed to URL paths. HTTP verbs are provided againstthose objects on those paths. Additional services(or views depending on terminology) are provided with URL pathparts that start with @, for example, the @move endpoint.

3.17.2 Route matching

With Guillotina, you can also route custom sub paths off a registered service. Guillotina is primarily for routing objectsto urls; however, this feature is used to provide additional parameters to the service.

An example of where this is used is for file services: /db/container/item/@upload/file.

Registering custom route parts

from guillotina import [email protected](

method='GET', permission='guillotina.AccessContent',name='@match/{foo}/{bar}')

async def matching_service(context, request):return request.matchdict # will return {'foo': 'foo', 'bar': 'bar'}

Some caveats need to be considered when mixing in routing:

• matches are only done when traversal misses

154 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

• there are limits to the variability of the route scheme you use. For example @foobar/{one}/{two} and@foobar/one/two will be converted into the same service registration; however, the former will matchagainst variable paths and the later will only match @foobar/one/two. So you might run into restrictionsquickly if you’re trying to do complex routing.

3.17.3 Providing your own router

Guillotina allows you to provide your own customized router using the router settings.

Here is an example router that provides /v1 and /v2 type url structure:

from guillotina import configurefrom guillotina.content import Resourcefrom guillotina.interfaces import IContainerfrom guillotina.interfaces import IDefaultLayerfrom guillotina.interfaces import IRequestfrom guillotina.interfaces import IResourcefrom guillotina.traversal import TraversalRouterfrom guillotina.traversal import traversefrom zope.interface import alsoProvides

class IV1Layer(IDefaultLayer):pass

class IV2Layer(IDefaultLayer):pass

@configure.service(method='GET', name='@foobar',permission='guillotina.AccessContent',layer=IV1Layer)

async def v1_service(context, request):return {

'version': '1'}

@configure.service(method='GET', name='@foobar',permission='guillotina.AccessContent',layer=IV2Layer)

async def v2_service(context, request):return {

'version': '2'}

@configure.contenttype(type_name="VersionRouteSegment")class VersionRouteSegment(Resource):

type_name = 'VersionRouteSegment'

def __init__(self, name, parent):super().__init__()self.__name__ = self.id = nameself.__parent__ = parent

3.17. Router 155

guillotina Documentation, Release 4.2.11

class MyRouter(TraversalRouter):async def traverse(self, request: IRequest) -> IResource:

resource, tail = await super().traverse(request)if len(tail) > 0 and tail[0] in ('v1', 'v2') and IContainer.

→˓providedBy(resource):segment = VersionRouteSegment(tail[0], resource)if tail[0] == 'v1':

alsoProvides(request, IV1Layer)elif tail[0] == 'v2':

alsoProvides(request, IV2Layer)

if len(tail) > 1:# finish traversal from herereturn await traverse(request, segment, tail[1:])

else:resource = segmenttail = tail[1:]

return resource, tail

app_settings = {# provide custom application settings here...'router': MyRouter

}

3.18 Exceptions

Exceptions during the rendering of API calls are wrapped, logged and provided generic http status codes by default.

Guillotina provides a mechanism for customizing the status codes and type of responses given depending on theexception type.

3.18.1 Custom exception response

from aiohttp.web_exceptions import HTTPPreconditionFailedfrom guillotina import configurefrom guillotina.interfaces import IErrorResponseException

import json

@configure.adapter(for_=json.decoder.JSONDecodeError,provides=IErrorResponseException)

def json_decode_error_response(exc, error='', eid=None):return HTTPPreconditionFailed(

reason=f'JSONDecodeError: {eid}')

156 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

3.19 Fields

Guillotina uses schemas to define content types and behaviors. These schemas consist of field definitions.

3.19.1 Available fields

• guillotina.schema.Bool

• guillotina.schema.Bytes

• guillotina.schema.Choice: validates against vocabulary of values

• guillotina.schema.Date

• guillotina.schema.Datetime

• guillotina.schema.Decimal

• guillotina.schema.Dict

• guillotina.schema.Float

• guillotina.schema.Int

• guillotina.schema.JSONField

• guillotina.schema.List

• guillotina.schema.Set

• guillotina.schema.Text

• guillotina.schema.TextLine

• guillotina.schema.Time

• guillotina.fields.PatchField: allow updating value without patching entire value

• guillotina.fields.BucketListField: optimized storage for very large lists of data

• guillotina.files.CloudFileField: file field for storing in db or cloud storage

3.19.2 Patch field

Guillotina provides a PatchField which allows you to patch values of List, Dict and Int fields without havingthe original value.

Patch field list

from zope.interface import Interfacefrom guillotina.fields import PatchFieldfrom guillotina import schema

class IMySchema(Interface):values = PatchField(schema.List(

value_type=schema.Text()))

Then, payload for patching to append to this list would look like:

3.19. Fields 157

guillotina Documentation, Release 4.2.11

{"values": {

"op": "append","value": "foobar"

}}

Extend:

{"values": {

"op": "extend","value": ["foo", "bar"]

}}

Delete:

{"values": {

"op": "del","value": 0

}}

Update:

{"values": {

"op": "update","value": {

"index": 0,"value": "Something new"

}}

}

Patch dict field

from zope.interface import Interfacefrom guillotina.fields import PatchFieldfrom guillotina import schema

class IMySchema(Interface):values = PatchField(schema.Dict(

key_type=schema.Text(),value_type=schema.Text()

))

Then, payload for patching to add to this dict would look like:

{"values": {

"op": "assign","value": {

"key": "foo",

158 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

"value": "bar"}

}}

Delete:

{"values": {

"op": "del","value": "foo"

}}

Patch int field

PatchField can also be used on Int fields to increment, decrement or reset their original value.

from zope.interface import Interfacefrom guillotina.fields import PatchFieldfrom guillotina import schema

class IMySchema(Interface):counter = PatchField(schema.Int(

title='My Counter',default=1,

))

The payload to increment counter by 3 units would look like:

{"counter": {

"op": "inc","value": 3

}}

Notice that, at this point, counter will be set to 4 because its default value is 1. If the default would not be set, theincrement operation assumes a 0, and thus counter would be 3.

Likewise, to decrement the field, the following payload would work:

{"counter": {

"op": "dec","value": 4

}}

To reset counter to its default value, you can send the following payload without value:

{"counter": {

"op": "reset"}

}

3.19. Fields 159

guillotina Documentation, Release 4.2.11

and counter will be set to its default value 1. Otherwise, you can also send the target reset value:

{"counter": {

"op": "reset","value": 0

}}

Notice that a reset operation on a integer without a default value is equivalent to sending a value of 0.

Bucket list field

from zope.interface import Interfacefrom guillotina.fields import BucketListFieldfrom guillotina import schema

class IMySchema(Interface):values = BucketListField(

value_type=schema.Text(),bucket_len=5000

)

Then, payload for patching to append to this list would look like:

{"values": {

"op": "append","value": "foobar"

}}

Extend:

{"values": {

"op": "extend","value": ["foo", "bar"]

}}

Delete:

{"values": {

"op": "del","value": {

"bucket_index": 0,"item_index": 0

}}

}

160 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

3.20 Serializing content

Guillotina provides ways to hook into the content serialization that is done when you retreive content from the API.

The training doc has a great introduction on customizing the serialization for content.

3.21 AsyncIO Utilities

Guillotina comes with some amazing utilities to make working with AsyncIO a bit easier.

Of those, Guillotina provides functions to run asychronous functions in a queue or a pool.

For example, sending an email:

from guillotina import configurefrom guillotina.utils import execute

async def send_email(to_, from_, subject, body):pass

@configure.service(name='@myservice', method='GET',permission='guillotina.AccessContent')

async def my_service(context, request):execute.in_pool(

send_email, 'foo@bar', 'foo@bar', 'Hello!', 'Some body!').after_request()return {

'foo': 'bar'}

This will execute the function send_email in an asynchronous pool after the request is finished.

The functions execute.in_queue, execute.in_queue_with_func, execute.after_commit andexecute.before_commit are also available.

See the full specification.

3.22 Component Architecture

Guillotina is built on a component architecture. The component architecture uses adapter and singleton softwaredesign patterns to help manage complexity. It allows users to register and lookup adapters and utility defined againstinterfaces.

3.22.1 Why

program to an interface, not an implementation

favor object composition over class inheritance

keep objects stupid

3.20. Serializing content 161

guillotina Documentation, Release 4.2.11

The component architecture is a powerful tool to help you build complex software that needs to be extensible. Insoftware engineering, adapters and singletons are used often so it is a natural pattern to build on.

Almost any component/functionality in Guillotina can be overridden in an add-on application by overriding Guil-lotina’s components.

3.22.2 Basics

To query an adapter on content:

from guillotina.component import get_adapterfrom guillotina.interfaces import IResourcefrom zope.interface import Interfacefrom guillotina import configure

class IMyAdapter(Interface):pass

@configure.adapter(for_=IResource, provides=IMyAdapter)class MyAdapter:

def __init__(self, ob):self.ob = ob

def do_something(self):pass

adapter = get_adapter(ob, IMyAdapter)adapter.do_something()

To query for a utility(which is what we call singletons), it’s similiar:

from guillotina.component import get_utilityfrom guillotina.interfaces import IPermissionpermission = get_utility(IPermission, name='guillotina.AccessContent')

3.22.3 Details

To describe power of this approach, we’ll go through using adapters without the registration and lookup and then withthe component architecture.

Adapters without CA

class Automobile:wheels = 4

def __init__(self):pass

class Motorcycle(Automobile):wheels = 2

class SemiTruck(Automobile):

162 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

wheels = 18

class Operate:

def __init__(self, automobile: Automobile):self.automobile = automobile

def drive(self):pass

class OperateMotocycle:

def drive(self):pass

class OperateSemi:

def drive(self):pass

Then, to use these adapters, you might do something like:

if isinstance(auto, SemiTruck):operate = OperateSemi(auto)

elif isinstance(auto, Motorcycle):operate = OperateMotocycle(auto)

else:operate = Operate(auto)

operate.drive()

Adapters with CA

from guillotina import configurefrom zope.interface import Attribute, Interface, implementer

class IAutomobile(Interface):wheels = Attribute('number of wheels')

class IMotocycle(IAutomobile):pass

class ISemiTruck(IAutomobile):pass

@implementer(IAutomobile)class Automobile:

wheels = 4

3.22. Component Architecture 163

guillotina Documentation, Release 4.2.11

@implementer(IMotocycle)class Motorcycle(Automobile):

wheels = 2

@implementer(ISemiTruck)class SemiTruck(Automobile):

wheels = 18

class IOperate(Interface):def drive():

pass

@configure.adapter(for_=IAutomobile, provides=IOperate)class Operate:

def __init__(self, automobile: Automobile):self.automobile = automobile

def drive(self):return 'driving automobile'

@configure.adapter(for_=IMotocycle, provides=IOperate)class OperateMotocycle(Operate):

def drive(self):return 'driving motocycle'

@configure.adapter(for_=ISemiTruck, provides=IOperate)class OperateSemi(Operate):

def drive(self):return 'driving semi'

Then, to use it:

from guillotina.component import get_adaptersemi = SemiTruck()operate = get_adapter(semi, IOperate)operate.drive()

3.23 Debugging

Debugging Guillotina will be slightly different from other web application that are not asyncio but Guillotina providessome nice integration to help you out.

3.23.1 X-Debug header

Any any request, you can provide the header X-Debug:1 and Guillotina will output debugging headers in the re-sponse about timing, number of queries and cache hit/miss stats.

164 Chapter 3. Narrative Developer Documentation

guillotina Documentation, Release 4.2.11

GET /(db)/container Retrieves serialization of resource

• Permission: guillotina.ViewContent

• Context: guillotina.interfaces.content.IResource

http

GET /db/container HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290X-Debug: 1

curl

curl -i http://nohost/db/container -H 'Accept: application/json' -H 'X-Debug: 1' -→˓-user root:root

httpie

http http://nohost/db/container Accept:application/json X-Debug:1 -a root:root

response

HTTP/1.1 200 OKContent-Length: 512Content-Type: application/jsonXG-Num-Queries: 0XG-Request-Cache-hits: 0XG-Request-Cache-misses: 3XG-Request-Cache-stored: 3XG-Timing-0-start: 0.25916XG-Timing-1-traversed: 0.44394XG-Timing-2-beforeauthentication: 0.10514XG-Timing-3-authentication: 0.13566XG-Timing-4-viewfound: 0.18954XG-Timing-5-authorization: 0.57650XG-Timing-6-viewrender: 0.02384XG-Timing-7-viewrendered: 2.10452XG-Timing-8-renderer: 0.15974XG-Timing-9-headers: 0.02766XG-Timing-Total: 4.02570XG-Total-Cache-hits: 0XG-Total-Cache-misses: 6XG-Total-Cache-stored: 6

{"@id": "http://127.0.0.1:51723/db/container","@type": "Container","@name": "container","@uid": "e17d899460e442e1a1aa4d769eee798b","@static_behaviors": [],"parent": {},"is_folderish": true,"creation_date": "2018-10-24T20:12:23.082300+00:00","modification_date": "2018-10-24T20:12:23.082300+00:00","UID": "e17d899460e442e1a1aa4d769eee798b","type_name": "Container","title": "container",

3.23. Debugging 165

guillotina Documentation, Release 4.2.11

"uuid": "e17d899460e442e1a1aa4d769eee798b","__behaviors__": [],"__name__": "container","items": [],"length": 0

}

Query Parameters

• include (string) –

• omit (string) –

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

3.23.2 GDEBUG

On startup, you can also provide the environment variable GDEBUG=true. This will provide detailed query statisticswith the X-Debug:1.

3.23.3 aiomonitor

Guillotina also provides integration with the aiomonitor python module. This module allows you to attach to arunning python with asyncio to inspect the active tasks running.

First, install aiomonitor:

pip install aiomonitor

Then, run guillotina with --monitor:

g server --monitor

Finally, connect to it:

python -m aiomonitor.cli

3.23.4 Jupyter

Guillotina also works with Jupyter notebooks. Load the example notebook in the guillotina repository to see anexample of how to get started.

166 Chapter 3. Narrative Developer Documentation

CHAPTER 4

Deploying

• Installing guillotina is simply done with pip but if you need to run with docker, we also have you covered.

• Guillotina has an quite a few configuration options you might be curious about.

• You can also setup logging configuration.

• Finally, you may also need to put Guillotina behind a proxy when you deploy it.

167

guillotina Documentation, Release 4.2.11

168 Chapter 4. Deploying

CHAPTER 5

Programming API Reference

5.1 guillotina.configure

guillotina.configure.scan(path)Load a module dotted name.

Parameters path (str) – dotted name

guillotina.configure.json_schema_definition(name, schema)Register a json schema definition

Parameters

• name (str) – Name of schema

• schema (dict) – schema definition, must be json compatible

Return type None

guillotina.configure.service(**kwargs)Configure a service

>>> from guillotina import configure>>> @configure.service(name='@foobar')

async def foobar_service(context, request): return {'foo': 'bar'}

Parameters

• context (Interface) – Content type interface this service is registered against

• method (str) – HTTP method this service works against. Defaults to GET.

• permission (str) – Permission this service requires

• layer (str) – Layer this service is registered for. Default is IDefaultLayer

• name – This is used as part of the uri. Example @foobar -> /mycontent/@foobar.

• summary – Used for documentation and swagger.

169

guillotina Documentation, Release 4.2.11

• description – Used for documentation and swagger.

• responses – Used for documentation and swagger.

• parameters – Used for documentation and swagger.

guillotina.configure.contenttype(**kwargs)Configure content type

>>> from guillotina import configure>>> from guillotina.content import Item>>> @configure.contenttype(type_name="Foobar")

class Foobar(Item): pass

Parameters

• type_name (str) – Name of the content type

• schema (str) – Schema to use for content type

• add_permission (str) – Permission required to add content. Defaults to guil-lotina.AddContent

• allowed_types (list) – List of types allowed to be added inside this content assumingit is a Folder type. Defaults to allowing all types.

• behaviors (str) – List of behaviors to enable for this type.

• factory – Dotted name to custom factory to use. See guillotina.content.ResourceFactoryfor default implementation

guillotina.configure.behavior(**kwargs)Configure behavior

>>> from guillotina import configure>>> from guillotina.behaviors.instance import ContextBehavior>>> class IMyBehavior(Interface): pass>>> @configure.behavior(

title="Dublin Core fields",provides=IMyBehavior,for_="guillotina.interfaces.IResource")

class MyBehavior(ContextBehavior): pass

Parameters

• title – Title of behavior

• provides – Schema to use for behavior

• behavior – Marker interface to apply to utilized instance’s behavior

• for_ – Content type this behavior is available for

guillotina.configure.vocabulary(**kwargs)Configure vocabulary

>>> from guillotina import configure>>> @configure.vocabulary(name="myvocab")

class MyVocab:def __init__(self, context):self.context = context

170 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

self.values = range(10)def __iter__(self):return iter([])

def __contains__(self, value):return value in self.values

def __len__(self):return len(self.values)

def getTerm(self, value):return 'value'

Parameters name – Reference of the vocabulary to get it

guillotina.configure.addon(**kwargs)Configure addon

>>> from guillotina import configure>>> @configure.addon(

name="docaddon",title="Doc addon",dependencies=["cms"])

class TestAddon(Addon): pass

Parameters

• name – Unique name of addon

• title – Title of addon

• dependencies – List of names of dependency addons

guillotina.configure.adapter(**kwargs)Configure adapter

Parameters

• for_ – Type or list of types this subscriber is for: required

• provides – Interface this adapter provides

guillotina.configure.utility(**kwargs)Configure utility

Parameters

• provides – Interface this utility provides

• name – Optional to name the utility

guillotina.configure.permission(**kwargs)Configure permission

Parameters

• id –

• title –

• description –

guillotina.configure.role(**kwargs)Configure role

5.1. guillotina.configure 171

guillotina Documentation, Release 4.2.11

Parameters

• id –

• title –

• description –

• local (bool) – defaults to True

guillotina.configure.grant(**kwargs)Configure granting permission to role

Parameters

• role –

• principal –

• permission –

• permissions –

guillotina.configure.value_serializer(type)Configure a value serializer

>>> @configure.value_serializer(bytes)>>> def bytes_converter(value): return b64encode(value)

Parameters type – type to serialize

guillotina.configure.value_deserializer(field_type)Configure a value deserializer

>>> @configure.value_deserializer(IText)>>> def field_converter(field, value, context): return value

Parameters field_type – type of field to deserialize

guillotina.configure.renderer(**kwargs)Configure a renderer

>>> @configure.renderer(name='text/plain')>>> class RendererPlain(StringRenderer):

content_type = 'text/plain'

Parameters name – content type the renderer can be used for

guillotina.configure.language(name)Configure a language

>>> @configure.language(name='en')>>> class EN(DefaultLanguage): pass

Parameters name – Name of language

172 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

5.2 guillotina.content

class guillotina.content.Resource(id=None)Base resource object class

aclAccess control list stores security information on the object

Return type dict

add_behavior(iface)We need to apply the marker interface.

value: Interface to add

Return type None

remove_behavior(iface)We need to apply the marker interface.

value: Interface to add

Return type None

uuidThe unique id of the content object

class guillotina.content.Item(id=None)Basic item content type. Inherits from Resource

class guillotina.content.Folder(id=None)Basic folder content type. Inherits from Resource but provides interface to work with contained objects asyn-chronously.

async_contains(key)Asynchronously check if key exists inside this folder

Parameters key (str) – key of child object to check

Return type bool

async_del(key)Asynchronously delete object in the folder

Parameters key (str) – key of child objec to delete

Return type None

async_get(key, default=None, suppress_events=False)Asynchronously get an object inside this folder

Parameters key (str) – key of child object to get

Return type Resource

async_items(suppress_events=False)Asynchronously iterate through contents of folder

Return type Asynciterator[Tuple[str, Resource]]

async_keys()Asynchronously get the sub object keys in this folder

Return type List[str]

5.2. guillotina.content 173

guillotina Documentation, Release 4.2.11

async_len()Asynchronously calculate the len of the folder

Return type int

async_multi_get(keys, default=None, suppress_events=False)Asynchronously get an multiple objects inside this folder

Parameters keys (List[str]) – keys of child objects to get

Return type Asynciterator[Tuple[Resource]]

async_set(key, value)Asynchronously set an object in this folder

Parameters

• key (str) – key of child object to set

• value (Resource) – object to set as child

Return type None

class guillotina.content.Container(id=None)

guillotina.content.create_content_in_container(parent, type_, id_, request=None,check_security=True, **kw)

Utility to create a content.

This method is the one to use to create content. id_ can be None

Parameters

• parent (Folder) – where to create content inside of

• type (str) – content type to create

• id (str) – id to give content in parent object

• request (Optional[<InterfaceClass guillotina.interfaces.IRequest>]) – <optional>

• check_security – be able to disable security checks

Return type Resource

guillotina.content.get_all_possible_schemas_for_type(type_name)

Return type List[<InterfaceClass zope.interface.Interface>]

guillotina.content.iter_schemata(obj)

Return type Iterator[<InterfaceClass zope.interface.Interface>]

5.3 guillotina.request

class guillotina.request.Request(*args, **kwargs)Bases: aiohttp.web_request.Request

Guillotina specific request type. We store potentially a lot of state onto the request object as it is essential ourpoor man’s thread local model

add_future(name, fut, scope=”, args=None, kwargs=None)Register a future to be executed after the request has finished.

Parameters

174 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

• name (str) – name of future

• fut (Callable[..., Coroutine[Any, Any, Any]]) – future to execute after request

• scope (str) – group the futures to execute different groupings together

• args – arguments to execute future with

• kwargs – kwargs to execute future with

execute_futures(scope=”)Execute all the registered futures in a new task

Parameters scope (str) – scoped futures to execute. Leave default for normal behavior

get_future(name, scope=”)Get a registered future

Parameters

• name (str) – scoped futures to execute. Leave default for normal behavior

• scope (str) – scope name the future was registered for

record(event_name)Record event on the request

Parameters event_name (str) – name of event

5.4 guillotina.response

class guillotina.response.Response(*, content=None, headers=None, status=None)Bases: Exception

__init__(*, content=None, headers=None, status=None)

Parameters

• content (Optional[dict]) – content to serialize

• headers (Optional[dict]) – headers to set on response

• status (Optional[int]) – customize the response status

Return type None

class guillotina.response.ErrorResponse(type, message, *, reason=None, content=None,headers=None, status=500)

Bases: guillotina.response.Response

__init__(type, message, *, reason=None, content=None, headers=None, status=500)

Parameters

• type (str) – type of error

• message (str) – error message

• content (Optional[dict]) – provide additional content

• headers (Optional[dict]) – headers to set on response

• status (int) – customize the response status

Return type None

5.4. guillotina.response 175

guillotina Documentation, Release 4.2.11

class guillotina.response.HTTPError(*, content=None, headers=None, status=None)Bases: guillotina.response.Response

Base class for exceptions with status codes in the 400s and 500s.

class guillotina.response.HTTPRedirection(*, content=None, headers=None, status=None)Bases: guillotina.response.Response

Base class for exceptions with status codes in the 300s.

class guillotina.response.HTTPSuccessful(*, content=None, headers=None, status=None)Bases: guillotina.response.Response

Base class for exceptions with status codes in the 200s.

class guillotina.response.HTTPOk(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPSuccessful

class guillotina.response.HTTPCreated(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPSuccessful

class guillotina.response.HTTPAccepted(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPSuccessful

class guillotina.response.HTTPNonAuthoritativeInformation(*, content=None,headers=None, sta-tus=None)

Bases: guillotina.response.HTTPSuccessful

class guillotina.response.HTTPNoContent(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPSuccessful

class guillotina.response.HTTPResetContent(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPSuccessful

class guillotina.response.HTTPPartialContent(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPSuccessful

class guillotina.response.HTTPMultipleChoices(location, *, content=None, head-ers=None)

Bases: guillotina.response._HTTPMove

Parameters

• location (str) – where to redirect

• headers (Optional[dict]) – additional headers to set

class guillotina.response.HTTPMovedPermanently(location, *, content=None, head-ers=None)

Bases: guillotina.response._HTTPMove

Parameters

• location (str) – where to redirect

• headers (Optional[dict]) – additional headers to set

class guillotina.response.HTTPFound(location, *, content=None, headers=None)Bases: guillotina.response._HTTPMove

Parameters

• location (str) – where to redirect

176 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

• headers (Optional[dict]) – additional headers to set

class guillotina.response.HTTPSeeOther(location, *, content=None, headers=None)Bases: guillotina.response._HTTPMove

Parameters

• location (str) – where to redirect

• headers (Optional[dict]) – additional headers to set

class guillotina.response.HTTPNotModified(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPRedirection

class guillotina.response.HTTPUseProxy(location, *, content=None, headers=None)Bases: guillotina.response._HTTPMove

class guillotina.response.HTTPTemporaryRedirect(location, *, content=None, head-ers=None)

Bases: guillotina.response._HTTPMove

class guillotina.response.HTTPPermanentRedirect(location, *, content=None, head-ers=None)

Bases: guillotina.response._HTTPMove

class guillotina.response.HTTPClientError(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPError

class guillotina.response.HTTPBadRequest(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPUnauthorized(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPPaymentRequired(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPForbidden(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPNotFound(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPClientError

class guillotina.response.InvalidRoute(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPNotFound

The defined route is invalid

class guillotina.response.HTTPMethodNotAllowed(method, allowed_methods, *, con-tent=None, headers=None)

Bases: guillotina.response.HTTPClientError

__init__(method, allowed_methods, *, content=None, headers=None)

Parameters

• method (str) – method not allowed

• allowed_methods (list) – list of allowed methods

• content (Optional[dict]) – content to serialize

• headers (Optional[dict]) – headers to set on response

Return type None

5.4. guillotina.response 177

guillotina Documentation, Release 4.2.11

class guillotina.response.HTTPNotAcceptable(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPProxyAuthenticationRequired(*, content=None,headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPRequestTimeout(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPConflict(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPGone(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPLengthRequired(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPPreconditionFailed(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPRequestEntityTooLarge(*, content=None, head-ers=None, status=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPRequestURITooLong(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPUnsupportedMediaType(*, content=None, headers=None,status=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPRequestRangeNotSatisfiable(*, content=None, head-ers=None, status=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPExpectationFailed(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPMisdirectedRequest(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPUnprocessableEntity(*, content=None, headers=None,status=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPFailedDependency(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPUpgradeRequired(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPPreconditionRequired(*, content=None, headers=None,status=None)

Bases: guillotina.response.HTTPClientError

178 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

class guillotina.response.HTTPTooManyRequests(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPRequestHeaderFieldsTooLarge(*, content=None,headers=None, sta-tus=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPUnavailableForLegalReasons(link, *, content=None,headers=None)

Bases: guillotina.response.HTTPClientError

class guillotina.response.HTTPServerError(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPError

class guillotina.response.HTTPInternalServerError(*, content=None, headers=None,status=None)

Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPNotImplemented(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPBadGateway(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPServiceUnavailable(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPGatewayTimeout(*, content=None, headers=None, sta-tus=None)

Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPVersionNotSupported(*, content=None, headers=None,status=None)

Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPVariantAlsoNegotiates(*, content=None, head-ers=None, status=None)

Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPInsufficientStorage(*, content=None, headers=None,status=None)

Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPNotExtended(*, content=None, headers=None, status=None)Bases: guillotina.response.HTTPServerError

class guillotina.response.HTTPNetworkAuthenticationRequired(*, content=None,headers=None,status=None)

Bases: guillotina.response.HTTPServerError

5.5 guillotina.schema

class guillotina.schema.Bool(title=”, description=”, __name__=”, required=True, read-only=False, constraint=None, default=None, defaultFac-tory=None, missing_value=<object object>, **kw)

A field representing a Bool.

5.5. guillotina.schema 179

guillotina Documentation, Release 4.2.11

from_unicode(str)

>>> b = Bool()>>> IFromUnicode.providedBy(b)True>>> b.from_unicode('True')True>>> b.from_unicode('')False>>> b.from_unicode('true')True>>> b.from_unicode('false') or b.from_unicode('False')False

class guillotina.schema.Bytes(min_length=0, max_length=None, **kw)Field containing a byte string (like the python str).

The value might be constrained to be with length limits.

from_unicode(uc)See IFromUnicode.

class guillotina.schema.Choice(values=None, vocabulary=None, source=None, **kw)Choice fields can have a value found in a constant or dynamic set of values given by the field definition.

bind(object)See guillotina.schema._bootstrapinterfaces.IField.

from_unicode(str)See IFromUnicode.

class guillotina.schema.Date(min=None, max=None, default=None, **kw)Field containing a date.

class guillotina.schema.Datetime(*args, **kw)Field containing a DateTime.

class guillotina.schema.Decimal(*args, **kw)Field containing a Decimal.

from_unicode(uc)See IFromUnicode.

class guillotina.schema.Dict(key_type=None, value_type=None, **kw)A field representing a Dict.

bind(object)See guillotina.schema._bootstrapinterfaces.IField.

class guillotina.schema.Float(*args, **kw)Field containing a Float.

from_unicode(uc)See IFromUnicode.

class guillotina.schema.Int(*args, **kw)A field representing an Integer.

from_unicode(str)

180 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

>>> f = Int()>>> f.from_unicode("125")125>>> f.from_unicode("125.6")Traceback (most recent call last):...ValueError: invalid literal for int(): 125.6

class guillotina.schema.JSONField(schema=’{"type": "object", "properties": {}}’, **kw)

class guillotina.schema.List(value_type=None, unique=False, **kw)A field representing a List.

class guillotina.schema.Set(**kw)A field representing a set.

class guillotina.schema.Text(*args, **kw)A field containing text used for human discourse.

from_unicode(str)

>>> t = Text(constraint=lambda v: 'x' in v)>>> t.from_unicode(b"foo x spam")Traceback (most recent call last):...WrongType: ('foo x spam', <type 'unicode'>, '')>>> t.from_unicode("foo x spam")u'foo x spam'>>> t.from_unicode("foo spam")Traceback (most recent call last):...ConstraintNotSatisfied: ('foo spam', '')

class guillotina.schema.TextLine(*args, **kw)A text field with no newlines.

class guillotina.schema.Time(min=None, max=None, default=None, **kw)Field containing a time.

class guillotina.fields.PatchField(field, *args, **kwargs)

class guillotina.fields.BucketListField(*args, value_type=None, bucket_len=5000,**kwargs)

class guillotina.fields.CloudFileField(**kw)A cloud file hosted file.

Its configured on config.json with :

"cloud_storage": "guillotina.interfaces.IS3FileField"

or

"cloud_storage": "guillotina_gcloudstorage.interfaces.IGCloudFileField"

5.6 guillotina.utils

guillotina.utils.get_current_request()Return the current request by heuristically looking it up from stack

5.6. guillotina.utils 181

guillotina Documentation, Release 4.2.11

Return type <InterfaceClass guillotina.interfaces.IRequest>

guillotina.utils.get_content_path(content)Generate full path of resource object

Parameters content (<InterfaceClass guillotina.interfaces.content.IResource>) – object to get path from

Return type str

guillotina.utils.iter_parents(content)Iterate through all the parents of a content object

Parameters content (<InterfaceClass guillotina.interfaces.content.IResource>) – object to get path from

Return type Iterator[<InterfaceClass guillotina.interfaces.content.IResource>]

guillotina.utils.navigate_to(obj, path)Get a sub-object.

Parameters

• obj (<InterfaceClass guillotina.interfaces.content.IResource>)– object to get path from

• path (str) – relative path to object you want to retrieve

guillotina.utils.get_owners(obj)Return owners of an object

Parameters obj (<InterfaceClass guillotina.interfaces.content.IResource>) – object to get path from

Return type list

guillotina.utils.get_object_url(ob, request=None, **kwargs)Generate full url of object.

Parameters

• ob (<InterfaceClass guillotina.interfaces.content.IResource>) –object to get url for

• request (Optional[<InterfaceClass guillotina.interfaces.IRequest>]) – relative path toobject you want to retrieve

Return type Optional[str]

guillotina.utils.get_object_by_oid(oid, txn=None)Get an object from an oid

Parameters

• oid (str) – Object id of object you need to retreive

• txn – Database transaction object. Will get current transaction is not provided

Return type Optional[<InterfaceClass guillotina.interfaces.content.IResource>]

guillotina.utils.get_behavior(ob, iface, create=False)Generate behavior of object.

Parameters

• ob – object to get behavior for

182 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

• interface – interface registered for behavior

• create – if behavior data empty, should we create it?

guillotina.utils.get_authenticated_user(request)Get the currently authenticated user

Parameters request (<InterfaceClass guillotina.interfaces.IRequest>) –request the user is authenticated against

Return type Optional[<InterfaceClass guillotina.interfaces.security.IPrincipal>]

guillotina.utils.get_authenticated_user_id(request)Get the currently authenticated user id

Parameters request (<InterfaceClass guillotina.interfaces.IRequest>) –request the user is authenticated against

Return type Optional[str]

guillotina.utils.strings_differ(string1, string2)Check whether two strings differ while avoiding timing attacks.

This function returns True if the given strings differ and False if they are equal. It’s careful not to leak infor-mation about where they differ as a result of its running time, which can be very important to avoid certaintiming-related crypto attacks:

http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf

>>> strings_differ('one', 'one')False>>> strings_differ('one', 'two')True

Parameters

• string1 (str) –

• string2 (str) –

Return type bool

guillotina.utils.get_random_string(length=30, allowed_chars=’abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789’)Heavily inspired by Plone/Django Returns a securely generated random string.

>>> get_random_string(length=10)

Parameters

• length (int) –

• allowed_chars (str) –

Return type str

guillotina.utils.resolve_dotted_name(name)import the provided dotted name

>>> resolve_dotted_name('guillotina.interfaces.IRequest')<InterfaceClass guillotina.interfaces.IRequest>

Parameters name (str) – dotted name

5.6. guillotina.utils 183

guillotina Documentation, Release 4.2.11

Return type ~ResolvableType

guillotina.utils.get_caller_module(level=2, sys=<module ’sys’ (built-in)>)Pulled out of pyramid

Return type module

guillotina.utils.resolve_module_path(path)

Return type str

guillotina.utils.get_module_dotted_name(ob)

Return type str

guillotina.utils.get_dotted_name(ob)Convert a module/class/function to dotted path string

Parameters ob (~ResolvableType) – the object you’d like to convert

Return type str

guillotina.utils.import_class(import_string)Import class from string

Parameters import_string (str) – dotted class name

Return type module

guillotina.utils.resolve_path(file_path)Resolve path to file inside python module

>>> resolve_path('guillotina:__init__.py')PosixPath('/Users/vangheem/onna/onna-canonical/libsrc/guillotina/guillotina/__→˓init__.py')

Parameters file_path (str) – module:path string

Return type Path

guillotina.utils.merge_dicts(d1, d2)Update two dicts of dicts recursively, if either mapping has leaves that are non-dicts, the second’s leaf overwritesthe first’s.

Return type dict

guillotina.utils.apply_coroutine(func, *args, **kwargs)Call a function with the supplied arguments. If the result is a coroutine, await it.

>>> async def foobar(): return 'hi'>>> async def async_foobar(): return 'hi'>>> await apply_coroutine(foobar)'hi'>>> await apply_coroutine(async_foobar)'hi'

Parameters

• func (function) – function to run as coroutiune if one

• *args – args to call function with

• **kwargs – kwargs to call function with

184 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

Return type object

guillotina.utils.lazy_apply(func, *call_args, **call_kwargs)apply arguments in the order that they come in the function signature and do not apply if argument not provided

call_args will be applied in order if func signature has args. otherwise, call_kwargs is the magic here...

guillotina.utils.safe_unidecode(val)Convert bytes to a string in a safe way

>>> safe_unidecode(b'foobar')'foobar'

Parameters val (bytes) – bytes to convert

Return type str

class guillotina.utils.Navigator(txn, container)Provides a consistent view of the loaded objects, ensuring that only a single instance of each resource path isloaded.

It is particularly useful when objects that may have been modified or added have to be found by path (somethingthat the content api cannot do).

To keep the Navigator index consistent, all object loadings must be done through its API, meaning the contentapi should not be used anymore. In case it cannot be avoided, make sure to use the sync() function beforere-using the Navigator.

Parameters

• txn (Transaction) – A Transaction instance

• container (Container) – A Container

delete(obj)Delete an object from the tree

When using Navigator, this method should be used so that the Navigator can update its index and not returnit anymore.

get(path)Returns an object from its path.

If this path was already modified or loaded by Navigator, the same object instance will be returned.

sync()Resync the index with the transaction

If some operations may have added, modified or deleted objects that the Navigator does not know, sync()should be called so that the index is up-to-date with the transaction.

5.6.1 guillotina.utils.execute

guillotina.utils.execute.after_request(func, *args, _name=None, _request=None,_scope=”, **kwargs)

Execute after the request has successfully finished.

Parameters

• func (Callable[..., Coroutine[Any, Any, Any]]) – function to be queued

• _name – unique identifier to give in case you want to prevent duplicates

5.6. guillotina.utils 185

guillotina Documentation, Release 4.2.11

• _scope – customize scope of after commit to run for instead of default(successful request)

• _request – provide request object to prevent request lookup

• *args – arguments to call the func with

• **kwargs – keyword arguments to call the func with

guillotina.utils.execute.after_request_failed(func, *args, _name=None, _re-quest=None, **kwargs)

Execute after the request has failed or errored.

Parameters

• func (Callable[..., Coroutine[Any, Any, Any]]) – function to be queued

• _request – provide request object to prevent request lookup

• args – arguments to call the func with

• kwargs – keyword arguments to call the func with

guillotina.utils.execute.after_commit(func, *args, _request=None, **kwargs)Execute a commit to the database.

Parameters

• func (Callable) – function to be queued

• _request – provide request object to prevent request lookup

• args – arguments to call the func with

• kwargs – keyword arguments to call the func with

guillotina.utils.execute.before_commit(func, *args, _request=None, **kwargs)Execute before a commit to the database.

Parameters

• func (Callable[..., Coroutine[Any, Any, Any]]) – function to be queued

• _request – provide request object to prevent request lookup

• args – arguments to call the func with

• kwargs – keyword arguments to call the func with

guillotina.utils.execute.in_pool(func, *args, request=None, **kwargs)Execute function in the async pool.

Parameters

• func (Callable[..., Coroutine[Any, Any, Any]]) – function to be queued

• _request – provide request object to prevent request lookup. Provide if function bewrapped in database transaction.

• args – arguments to call the func with

• kwargs – keyword arguments to call the func with

Return type ExecuteContext

guillotina.utils.execute.in_queue(view)Execute view-type object(context, request) in the async queue.

Parameters view (Union[<InterfaceClass guillotina.interfaces.views.IView>,GenerateQueueView]) – view to be queued

186 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

Return type ExecuteContext

guillotina.utils.execute.in_queue_with_func(func, *args, _request=None, **kwargs)Execute function in the async queue.

Parameters

• func (Callable[..., Coroutine[Any, Any, Any]]) – function to be queued

• _request – provide request object to prevent request lookup

• args – arguments to call the func with

• kwargs – keyword arguments to call the func with

Return type ExecuteContext

class guillotina.utils.execute.ExecuteContext(func, *args, **kwargs)Execution context object to allow you to run the function in different contexts.

after_commit(_request=None)Execute after we commit to the database.

Parameters _request – provide request object to prevent request lookup

after_request(_name=None, _request=None)Execute after the request has successfully finished.

Parameters

• _name – unique identifier to give in case you want to prevent duplicates

• _request – provide request object to prevent request lookup

after_request_failed(_name=None, _request=None)Execute after the request has failed or errored.

Parameters

• _name – unique identifier to give in case you want to prevent duplicates

• _request – provide request object to prevent request lookup

before_commit(_request=None)Execute just before we commit to the database.

Parameters _request – provide request object to prevent request lookup

5.7 guillotina.component

guillotina.component.get_adapter(object, interface=<InterfaceClass zope.interface.Interface>,name=”, context=None, args=[], kwargs={})

Get a registered adapter

Parameters

• object – Object to get adapter for

• interface – What interface should the adapter provide

• name – if it is a named adapter

• args – args to provide the adapter constructor

• kwargs – kwargs to provide the adapter constructor

5.7. guillotina.component 187

guillotina Documentation, Release 4.2.11

Raises ComponentLookupError –

guillotina.component.get_adapters(objects, provided, context=None)Get a registered adapter

Parameters

• objects – Tuple of objects

• provided – What interface should the adapter provide

guillotina.component.get_multi_adapter(objects, interface=<InterfaceClasszope.interface.Interface>, name=”, context=None,args=[], kwargs={})

Get a registered multi adapter

Parameters

• objects – Objects to get adapter for

• interface – What interface should the adapter provide

• name – if it is a named adapter

• args – args to provide the adapter constructor

• kwargs – kwargs to provide the adapter constructor

Raises ComponentLookupError –

guillotina.component.query_adapter(object, interface=<InterfaceClasszope.interface.Interface>, name=”, default=None,context=None, args=[], kwargs={})

Get a registered adapter

Parameters

• object – Object to get adapter for

• interface – What interface should the adapter provide

• name – if it is a named adapter

• args – args to provide the adapter constructor

• kwargs – kwargs to provide the adapter constructor

guillotina.component.query_multi_adapter(objects, interface=<InterfaceClasszope.interface.Interface>, name=”, de-fault=None, context=None, args=[], kwargs={})

Get a registered multi adapter

Parameters

• objects – Objects to get adapter for

• interface – What interface should the adapter provide

• name – if it is a named adapter

• args – args to provide the adapter constructor

• kwargs – kwargs to provide the adapter constructor

guillotina.component.get_utility(interface, name=”, context=None)Get a registered utility

Parameters

188 Chapter 5. Programming API Reference

guillotina Documentation, Release 4.2.11

• interface – What interface should the utility provide

• name – if it is a named adapter

Raises ComponentLookupError –

guillotina.component.query_utility(interface, name=”, default=None, context=None)Get a registered utility

Parameters

• interface – What interface should the utility provide

• name – if it is a named adapter

guillotina.component.get_all_utilities_registered_for(interface, context=None)Get all utilities registered for interface

Parameters interface – What interface should the utility provide

guillotina.component.get_utilities_for(interface, context=None)Get utilities registered for interface

Parameters interface – What interface should the utility provide

5.7. guillotina.component 189

guillotina Documentation, Release 4.2.11

190 Chapter 5. Programming API Reference

CHAPTER 6

Training

6.1 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

6.1.1 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 andpassword 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 toexplore and use the API.

6.1.2 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

191

guillotina Documentation, Release 4.2.11

POST /db/container HTTP/1.1Accept: application/jsonAuthorization: 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 OKContent-Length: 186Content-Type: application/jsonLocation: http://127.0.0.1:51723/db/container/foobar5

{"@id": "http://127.0.0.1:51723/db/container/foobar5","@name": "foobar5","@type": "Item","@uid": "527|7a9efd1b6bad4f36a46f64014a18752d","UID": "527|7a9efd1b6bad4f36a46f64014a18752d"

}

Status Codes

• 200 OK – Resource data

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

6.1.3 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

192 Chapter 6. Training

guillotina Documentation, Release 4.2.11

http

PATCH /db/container/foobar5/@behaviors HTTP/1.1Accept: application/jsonAuthorization: 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 OKContent-Length: 2Content-Type: application/json

{}

Status Codes

• 200 OK – Successfully added behavior

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

• 412 Precondition Failed – Behavior already assigned here

6.1.4 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.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

<text data>

6.1. Using the Guillotina API 193

guillotina Documentation, Release 4.2.11

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 OKContent-Length: 0Content-Type: application/json

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

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.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

<text data>

curl

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

httpie

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

response

HTTP/1.1 200 OKContent-Disposition: attachment; filename="88e1bf94f8904242a85ebd6df50f5d8c"Content-Length: 11Content-Type: text/plain

<text data>

194 Chapter 6. Training

guillotina Documentation, Release 4.2.11

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

6.1.5 Uploading files with TUS

Guillotina also supports the TUS protocol using the @tusupload endpoint. The TUS protocol allows you to uploadlarge 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.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290TUS-RESUMABLE: 1UPLOAD-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 OKContent-Length: 2Content-Type: application/jsonLocation: http://127.0.0.1:51723/db/container/foobar5/@tusupload/fileTus-Resumable: 1.0.0

{}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

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

PATCH /(db)/container/content/@tusupload/file

• Permission: guillotina.ModifyContent

6.1. Using the Guillotina API 195

guillotina Documentation, Release 4.2.11

• Context: guillotina.interfaces.content.IResource

http

PATCH /db/container/foobar5/@tusupload/file HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290TUS-RESUMABLE: 1Upload-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 OKContent-Length: 2Content-Type: application/jsonTus-Resumable: 1.0.0Upload-Offset: 11

{}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

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.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290TUS-RESUMABLE: 1Upload-Offset: 11

<text data>

curl

196 Chapter 6. Training

guillotina Documentation, Release 4.2.11

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 OKContent-Length: 2Content-Type: application/jsonTus-Resumable: 1.0.0Tus-Upload-Finished: 1Upload-Offset: 22

{}

Status Codes

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

Unknown upload size

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

In order to implement this correctly, you will need to provide the Upload-Defer-Length: 1 header and valueon 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. Guillotinatracks upload activity to detect this. If there is no activity detected for 15 seconds with an unfinished TUS upload, noerror is thrown.

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

6.1.6 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

6.1. Using the Guillotina API 197

guillotina Documentation, Release 4.2.11

http

GET /db/container/foobar5/@sharing HTTP/1.1Accept: application/jsonAuthorization: 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 OKContent-Length: 280Content-Type: application/json

{"local": {

"roleperm": {},"prinperm": {},"prinrole": {

"root": {"guillotina.Owner": "Allow"

}}

},"inherit": [

{"@id": "http://127.0.0.1:51723/db/container","roleperm": {},"prinperm": {},"prinrole": {

"root": {"guillotina.ContainerAdmin": "Allow","guillotina.Owner": "Allow"

}}

}]

}

Status Codes

• 200 OK – All the sharing defined on this resource

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

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

POST /(db)/container/content/@sharing Change permissions for a resource

198 Chapter 6. Training

guillotina Documentation, Release 4.2.11

• Permission: guillotina.ChangePermissions

• Context: guillotina.interfaces.content.IResource

http

POST /db/container/foobar5/@sharing HTTP/1.1Accept: application/jsonAuthorization: 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 OKContent-Length: 0Content-Type: application/json

Status Codes

• 200 OK – Successfully changed permission

• 401 Unauthorized – You are not authorized to perform the operation

• 404 Not Found – The resource does not exist

6.1. Using the Guillotina API 199

guillotina Documentation, Release 4.2.11

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

6.1.7 Exploring the API with Swagger

In the previous step, we installed guillotina_swagger. With Swagger, we can inspect any context and explorethe API.

Visit http://localhost:8080/@docs

training/../../_static/img/swagger.png

click the Authorize button

training/../../_static/img/auth-swagger.png

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

References

• REST API

• Behaviors

• Security

6.2 AsyncIO

Python’s asyncio library allows you to run single threaded "concurrent" code using coroutines inside an event loop.

The event loop is designed for I/O over sockets and other resources, it is especially good for working with client/servernetwork connections.

Python >= 3.4(best features and performance in 3.6)

200 Chapter 6. Training

guillotina Documentation, Release 4.2.11

6.2.1 Explanation

Benefits

The event loop allows you to handle a larger number of network connections at once.

No network blocks, so you can have long running connections with very little performance impact (HTML5 socketsfor example).

How web servers are typically designed

• (Pyramid, Flash, Plone, etc)

• Processes X Threads = Total number of concurrent connections that can be handled at once.

• Client makes a request to web server, request is assigned thread, thread handle request and sends response

• If no threads available, request is blocked, waiting for an open thread

• Threads are expensive (CPU), Processes are expensive on RAM

How it works with AsyncIO

• All requests are thrown on thread loop

• Since we don’t block on network traffic, we can juggle many requests at the same time

• Modern web application servers connect with many different services that can potentially block on networktraffic — BAD

• Limiting factor is maxed out CPU, not costly thread switching between requests — GOOD

Where is network traffic used?

• Web Client/App Server

• App Server/Database

• App Server/Caching(redis)

• App Server/OAUTH

• App Server/Cloud storage

• App Server/APIs(gdrive, m$, slack, etc)

Implementation details

In order to benefit, the whole stack needs to be asyncio-aware.

Anywhere in your application server that is not and does network traffic WILL BLOCK all other connections while itis doing its network traffic (example: using the requests library instead of aiohttp)

6.2. AsyncIO 201

guillotina Documentation, Release 4.2.11

6.2.2 Basics

Get active event loop or create new one

Run coroutine inside event loop with asyncio.run_until_complete

import asyncio

async def hello():print('hi')

event_loop = asyncio.get_event_loop()event_loop.run_until_complete(hello())

6.2.3 Basics(2)

asyncio.run_until_complete automatically wraps your coroutine into a Future object and waits for it tofinish.

asyncio.ensure_future will wrap a coroutine in a future and return it to you

So you can schedule multiple coroutines that can run at the same time

import asyncio

async def hello1():await asyncio.sleep(0.5)print('hi 1')

async def hello2():print('hi 2')

event_loop = asyncio.get_event_loop()future1 = asyncio.ensure_future(hello1(), loop=event_loop)future2 = asyncio.ensure_future(hello2(), loop=event_loop)event_loop.run_until_complete(future2)event_loop.run_until_complete(future1)

6.2.4 Long running tasks

You can also schedule long running tasks on the event loop.

The tasks can run forever. . .

“Task” objects are the same as “Future” objects(well, close)

import asyncioimport random

async def hello_many():while True:

202 Chapter 6. Training

guillotina Documentation, Release 4.2.11

number = random.randint(0, 3)await asyncio.sleep(number)print('Hello {}'.format(number))

event_loop = asyncio.get_event_loop()task = asyncio.Task(hello_many())print('task running now...')event_loop.run_until_complete(asyncio.sleep(10))print('we waited 10 seconds')task.cancel()print('task cancelled')

6.2.5 ALL YOUR ASYNC BELONGS TO US

gotcha

If you want part of your code to be async(say a function), the complete stack of the caller must be async and runningon the event loop.

import asyncio

async def print_foobar1():print('foobar1')

async def print_foobar2():print('foobar2')

async def foobar():await print_foobar1()print_foobar2() # won't work, never awaited

event_loop = asyncio.get_event_loop()event_loop.run_until_complete(foobar())print_foobar1() # won't work, never awaited# await print_foobar1() # error, not running in event loop

6.2.6 "multi" processing

AsyncIO isn’t really multiprocessing but it gives you the illusion of it.

A simple example can be shown with the asyncio.gather function.

import asyncioimport aiohttp

async def download_url(url):async with aiohttp.ClientSession() as session:

resp = await session.get(url)text = await resp.text()

6.2. AsyncIO 203

guillotina Documentation, Release 4.2.11

print(f'Downloaded {url}, size {len(text)}')

event_loop = asyncio.get_event_loop()event_loop.run_until_complete(asyncio.gather(

download_url('https://www.google.com'),download_url('https://www.facebook.com'),download_url('https://www.twitter.com'),download_url('https://www.stackoverflow.com')

))

6.2.7 asyncio loops

Using yield with loops allows you to "give up" execution on every iteration of the loop.

import asyncio

async def yielding():for idx in range(5):

print(f'Before yield {idx}')yield

async def foobar2():async for idx in yielding():

print(f"Yay, I've been yield'd {idx}")

event_loop = asyncio.get_event_loop()event_loop.run_until_complete(foobar2())

6.2.8 Scheduling

loop.call_later: arrange to call on a delay loop.call_at: arrange function to be called at specified time

6.2.9 Executors

An executor is available to use when you have non-async code that needs to be made async.

A typical executor is a thread executor. This means, anything you run in an executor is being thrown in a thread to run.

It’s worse to have non-async code than to use thread executors.

Executors are also good for CPU bound code.

import asyncioimport requestsimport concurrent.futures

def download_url(url):resp = requests.get(url)text = resp.content

204 Chapter 6. Training

guillotina Documentation, Release 4.2.11

print(f'Downloaded {url}, size {len(text)}')

async def foobar():print('foobar')

executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)

event_loop = asyncio.get_event_loop()event_loop.run_until_complete(asyncio.gather(

event_loop.run_in_executor(executor, download_url, 'https://www.google.com'),event_loop.run_in_executor(executor, download_url, 'https://www.facebook.com'),event_loop.run_in_executor(executor, download_url, 'https://www.twitter.com'),event_loop.run_in_executor(executor, download_url, 'https://www.stackoverflow.com

→˓'),foobar()

))

6.2.10 Subprocess

Python also provides a very neat asyncio subprocess module.

import asyncio

async def run_cmd(cmd):print(f'Executing: {" ".join(cmd)}')process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.

→˓PIPE)out, error = await process.communicate()print(out.decode('utf8'))

event_loop = asyncio.get_event_loop()event_loop.run_until_complete(asyncio.gather(

run_cmd(['sleep', '1']),run_cmd(['echo', 'hello'])

))

6.3 Commands

Guillotina comes with a great set of commands you can use to help debug and inspect your install.

We’ve already gone through the serve, create and testdata commands so we’ll now cover shell and run.

Make sure to also read the commands reference in the docs to learn how to create your own commands.

6.3.1 Shell

The shell command allows you to get an interactive prompt into guillotina.

From here, you can connect to the database, accees objects and commit new data.

6.3. Commands 205

guillotina Documentation, Release 4.2.11

g -c config.yml shell

Then, to connect to the database and get your container object.

txn = await use_db('db')container = await use_container('container')

From here, you can access objects:

conversations = await container.async_get('conversations')await conversations.async_keys()

6.3.2 Run

The run command allows you to run a python script directly.

g -c config.yaml run --script=path/to/script.py

In order for you to utilize this, the script must have an async function named run inside it.

async def run(container):pass

6.4 Configuration

You may have wondered how running g command without any configuration and options knew to connect and con-figure the database. Well, it’s only because we provide default settings in our application and documentation to makethat step easy.

In this section, we’ll talk about working with the Guillotina configuration system.

6.4.1 Getting started

Guillotina provides a command to bootstrap a configuration file for you.

g create --template=configuration

This will produce a config.yaml file in your current path. Inspect the file to see what some of the default configu-ration options are.

6.4.2 Modifying configuration

A detailed list of configuration options and explanations can be found in the configuration section of the docs.

Note: Guillotina also supports JSON configuration files

206 Chapter 6. Training

guillotina Documentation, Release 4.2.11

6.4.3 Configuration file

To specify a configuration file other than the name config.yaml, you can use the -c or --config command lineoption.

g -c config-foobar.yaml

6.4.4 Installing applications

Guillotina applications are python packages that you install and then configure in your application settings.

For an example, we’ll go through installing swagger support.

pip install guillotina_swagger

Then, add this to your config.yaml file.

applications:- guillotina_swagger

Finally, start Guillotina again and visit http://localhost:8080/@docs.

References

• Configuration Options

6.5 Training

Prerequisites:

• Python >= 3.6

• Docker

• Postman

Contents:

6.5.1 Introduction

The Guillotina training is designed to give a complete experience of using and extending Guillotina.

The training can be useful for consumers of the Guillotina API as well as developers extending the framework withcustomizations/addons.

Please read the about chapter for details about what Guillotina is and why you should use it.

Using the training materials

The training materials make use of Python 3.6, Docker and Postman so please have up-to-date versions of all theseready.

References

• About Guillotina

6.5. Training 207

guillotina Documentation, Release 4.2.11

6.5.2 Installing Guillotina

Guillotina is a simple Python package so it can be installed with any of the number of installation methods availableto Python.

In the traing here, we will focus on using pip and docker. You can use, for example, buildout as well.

with pip

Note: It is recommended you install along with a virtualenv:

virtualenv-3.6 genvcd genvsource ./bin/activate

It’s as simple as...

pip install guillotina

For the purpose of this training, you’ll also need to install cookiecutter.

pip install cookiecutter

Guillotina also provides docker images.

References

• Quickstart

• Installation

• About Guillotina

6.5.3 Starting Guillotina

Once you have guillotina installed, you can easily run it with the g executable that it installs.

However, before we begin, we’ll need to run a postgresql server for Guillotina to use.

docker run -e POSTGRES_DB=guillotina -e POSTGRES_USER=guillotina -p 127.0.0.→˓1:5432:5432 postgres:9.6

Note: This particular docker run command produces a volatile database. Stopping and starting it again will cause youto lose any data you pushed into it.

Command

Then, simply run the default Guillotina command g.

g

Which should give you output like:

208 Chapter 6. Training

guillotina Documentation, Release 4.2.11

$ gCould not find the configuration file config.yaml. Using default settings.======== Running on http://0.0.0.0:8080 ========(Press CTRL+C to quit)

The g executable allows you to potentially run a number of commands with Guillotina. The default command isserve if none provided; however, you can explicitly run it with the serve command name as well.

g serve

The serve command also takes --host and --port options to quickly change without touching configuration.

In future sections, we’ll explore other commands available.

Check installation

Open up Postman and do a basic GET against http://localhost:8080 with basic auth credentials for rootuser and root password.

Also, do a GET on http://localhost:8080/db.

Congratulations! You have Guillotina running!

Useful run options

• --reload: auto reload on code changes. requires aiohttp_autoreload

• --profile: profile Guillotina while it’s running

• --profile-output: where to save profiling output

• --monitor: run with aiomonitor. requires aiomonitor

References

• Quickstart

• Installation

• Configuraion

• Command Options

6.5.4 Extending

In our training, we’ll be working on creating a simple chat application.

To extend Guillotina, we need to write a Python package.

Let’s start by using the cookiecutter to bootstrap an application for us.

g create --template=application

Follow the prompts and name your application guillotina_chat.

Then,

cd guillotina_chatpython setup.py develop

6.5. Training 209

guillotina Documentation, Release 4.2.11

Configuration

All application extension configuration is defined with Guillotina’s configure module and the app_settingsobject.

Defining content types, behaviors, services, etc all require the use of the configure module. Guillotina reads all theregistered configuration in code for each install application and loads it.

app_settings

Guillotina also provides a global app_settings object::

from guillotina import app_settings

This object contains all the settings from your config.yaml file as well as any additional configuration settingsdefined in addons.

app_settings has an order of precedence it will use pick settings from:

• guillotina’s default settings

• each application in order it is defined can override default guillotina settings

• config.yaml takes final precedence over all configuration

app_settings has an extra key ’file’ that contains the path of the configuration file, allowing relative paths to beused in an application settings.

Content types

For chatting, we’ll need a content type for conversations and messages.

Create a content.py file in your application and create the content types.

from guillotina import configure, content, Interface, schema

class IConversation(Interface):

users = schema.List(value_type=schema.TextLine()

)

@configure.contenttype(type_name="Conversation",schema=IConversation,behaviors=["guillotina.behaviors.dublincore.IDublinCore"],allowed_types=['Message'])

class Conversation(content.Folder):pass

class IMessage(Interface):text = schema.Text(required=True)

@configure.contenttype(

210 Chapter 6. Training

guillotina Documentation, Release 4.2.11

type_name="Message",schema=IMessage,behaviors=[

"guillotina.behaviors.dublincore.IDublinCore","guillotina.behaviors.attachment.IAttachment"

])class Message(content.Item):

pass

In order for Guillotina to detect your configuration, you’ll need to add a scan call inside your includeme functionin the __init__.py file.

from guillotina import configureconfigure.scan('guillotina_chat.content')

Test it out

Open up Postman and test creating a conversation and message instead of it.

Install an addons

Guillotina differentiates applications from addons.

An application is a python package you install into your environment and add to your list of applications in theconfiguration file.

Addons on the otherhand are when you want to perform installation logic into a container.

Define addon

To define an addon for Guillotina, we use the @configure.addon decorator in the install.py file.

For our case, we want to create a Folder with all our conversations with some default permissions.

from guillotina import configurefrom guillotina.addons import Addonfrom guillotina.content import create_content_in_containerfrom guillotina.interfaces import IRolePermissionManager

@configure.addon(name="guillotina_chat",title="Guillotina server application python project")

class ManageAddon(Addon):

@classmethodasync def install(cls, container, request):

if not await container.async_contains('conversations'):conversations = await create_content_in_container(

container, 'Folder', 'conversations',id='conversations', creators=('root',),contributors=('root',))

roleperm = IRolePermissionManager(conversations)roleperm.grant_permission_to_role(

6.5. Training 211

guillotina Documentation, Release 4.2.11

'guillotina.AddContent', 'guillotina.Member')roleperm.grant_permission_to_role(

'guillotina.AccessContent', 'guillotina.Member')

@classmethodasync def uninstall(cls, container, request):

registry = request.container_settings # noqa# uninstall logic here...

Testing

Then, using Postman, do a POST request to the @addons endpoint:

{"id": "guillotina_chat"}

Permissions/Role

Permissions are defined in your application code.

For our app, we’ll create roles that users are granted inside a conversation.

Add the following inside your __init__.py file.

configure.role("guillotina_chat.ConversationParticipant","Conversation Participant","Users that are part of a conversation", False)

configure.grant(permission="guillotina.ViewContent",role="guillotina_chat.ConversationParticipant")

configure.grant(permission="guillotina.AccessContent",role="guillotina_chat.ConversationParticipant")

configure.grant(permission="guillotina.AddContent",role="guillotina_chat.ConversationParticipant")

Event subscribers

Events in Guillotina are heavily influenced from zope events with the caveat in that we support async event handlers.

For our chat application, we want to make sure every user that is part of a conversation has permission to add newmessages and view other messages.

A simple way to do this is with an event handler that modifies permissions.

A an subscribers.py file inside your application.

from guillotina import configurefrom guillotina.interfaces import IObjectAddedEvent, IPrincipalRoleManagerfrom guillotina.utils import get_authenticated_user_id, get_current_requestfrom guillotina_chat.content import IConversation

@configure.subscriber(for_=(IConversation, IObjectAddedEvent))async def container_added(conversation, event):

212 Chapter 6. Training

guillotina Documentation, Release 4.2.11

user_id = get_authenticated_user_id(get_current_request())if user_id not in conversation.users:

conversation.users.append(user_id)

manager = IPrincipalRoleManager(conversation)for user in conversation.users or []:

manager.assign_role_to_principal('guillotina_chat.ConversationParticipant', user)

In order for Guillotina to detect your configuration, you’ll need to add a scan call inside your includeme functionin the __init__.py file.

from guillotina import configureconfigure.scan('guillotina_chat.subscribers')

Test it out

Using Postman, add a Conversation and then a Message to that conversation and then use the @sharing endpoint toinspect the assigned permissions.

Users

Guillotina does not come with any user provider OOTB and is designed to be plugged in with other services.

However, there is a simple provider that stores user data in the database called guillotina_dbusers that we willuse for the purpose of our training.

Install guillotina_dbusers

Just use pip

pip install guillotina_dbusers

And add the guillotina_dbusers to the list of applications in your config.yaml. Also makesure you are not overriding the auth_user_identifiers configuration value in your config.yaml asguillotina_dbusers uses that to work.

After you restart guillotina, you can also install dbusers into your container using the @addons endpoint: http

POST /db/container/@addons HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"id": "dbusers"

}

curl

curl -i -X POST http://localhost:8080/db/container/@addons -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"id": "dbusers"}' --user→˓root:root

6.5. Training 213

guillotina Documentation, Release 4.2.11

wget

wget -S -O- http://localhost:8080/db/container/@addons --header='Accept: application/→˓json' --header='Content-Type: application/json' --post-data='{"id": "dbusers"}' --→˓auth-no-challenge --user=root --password=root

httpie

echo '{"id": "dbusers"

}' | http POST http://localhost:8080/db/container/@addons Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/container/@addons', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'id': 'dbusers',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

{"available": [

{"id": "dbusers","title": "Guillotina DB Users"

},{

"id": "application_name","title": "Your application title"

}],"installed": [

"dbusers","application_name"

]}

Add users

Creating users is just creating a user object. http

POST /db/container/users HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290Content-Type: application/jsonHost: localhost:8080

{"@type": "User","email": "[email protected]",

214 Chapter 6. Training

guillotina Documentation, Release 4.2.11

"password": "secret","username": "Bob"

}

curl

curl -i -X POST http://localhost:8080/db/container/users -H 'Accept: application/json→˓' -H 'Content-Type: application/json' --data-raw '{"@type": "User", "email":→˓"[email protected]", "password": "secret", "username": "Bob"}' --user root:root

wget

wget -S -O- http://localhost:8080/db/container/users --header='Accept: application/→˓json' --header='Content-Type: application/json' --post-data='{"@type": "User",→˓"email": "[email protected]", "password": "secret", "username": "Bob"}' --auth-no-→˓challenge --user=root --password=root

httpie

echo '{"@type": "User","email": "[email protected]","password": "secret","username": "Bob"

}' | http POST http://localhost:8080/db/container/users Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/container/users', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'@type': 'User','email': '[email protected]','password': 'secret','username': 'Bob',

}, auth=('root', 'root'))

response

HTTP/1.1 201 CreatedContent-Type: application/jsonLocation: http://localhost:8080/db/container/users/Bob

{"@id": "http://localhost:8080/db/container/users/Bob","@name": "Bob","@type": "User","@uid": "6e6|753|05893a69ee6e4f56b540248b5728c4a4","UID": "6e6|753|05893a69ee6e4f56b540248b5728c4a4"

}

Logging in can be done with the @login endpoint which returns a jwt token. http

POST /db/container/@login HTTP/1.1Accept: application/jsonAuthorization: Basic cm9vdDpyb290

6.5. Training 215

guillotina Documentation, Release 4.2.11

Content-Type: application/jsonHost: localhost:8080

{"password": "secret","username": "Bob"

}

curl

curl -i -X POST http://localhost:8080/db/container/@login -H 'Accept: application/json→˓' -H 'Content-Type: application/json' --data-raw '{"password": "secret", "username→˓": "Bob"}' --user root:root

wget

wget -S -O- http://localhost:8080/db/container/@login --header='Accept: application/→˓json' --header='Content-Type: application/json' --post-data='{"password": "secret",→˓"username": "Bob"}' --auth-no-challenge --user=root --password=root

httpie

echo '{"password": "secret","username": "Bob"

}' | http POST http://localhost:8080/db/container/@login Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/container/@login', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'password': 'secret','username': 'Bob',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

{"exp": 1532253747,"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.

→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I"}

Then, future requests are done with a Bearer token with the jwt token. For example, to create a conversation withyour user: http

POST /db/container/conversations/ HTTP/1.1Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-IHost: localhost:8080

{

216 Chapter 6. Training

guillotina Documentation, Release 4.2.11

"@type": "Conversation","title": "New convo with foobar2","users": ["foobar", "foobar2"]

}

curl

curl -i -X POST http://localhost:8080/db/container/conversations/ -H 'Authorization:→˓Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.→˓1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I' --data-raw '{"@type": "Conversation","title": "New convo with foobar2","users": ["foobar", "foobar2"]

}'

wget

wget -S -O- http://localhost:8080/db/container/conversations/ --header=→˓'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I→˓' --post-data='{"@type": "Conversation","title": "New convo with foobar2","users": ["foobar", "foobar2"]

}'

httpie

echo '{"@type": "Conversation","title": "New convo with foobar2","users": ["foobar", "foobar2"]

}' | http POST http://localhost:8080/db/container/conversations/ Authorization:→˓'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I'

python-requests

requests.post('http://localhost:8080/db/container/conversations/', headers={'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.

→˓eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I→˓',}, data='{\r\n "@type": "Conversation",\r\n "title": "New convo with foobar2",\r\n→˓"users": ["foobar", "foobar2"]\r\n}')

Serialize content

Guillotina provides default serializations for content. It provides mechanisms for giving full content serialization ofinterfaces and behaviors as well as summary serializations that show in listings.

To customize a serialization on a type, you need to provide a multi adapter for theIResourceSerializeToJsonSummary or IResourceSerializeToJson interfaces.

For our use-case, we want to make sure to include the creation_date and some other data in the summaryserialization of conversations and messages so we can get all the info we need for our application without doing fullobjet serialization.

6.5. Training 217

guillotina Documentation, Release 4.2.11

Defining a custom serialization

Let’s define these serializers in a in a file named serialize.py.

from guillotina import configurefrom guillotina.interfaces import IResourceSerializeToJsonSummaryfrom guillotina.json.serialize_content import DefaultJSONSummarySerializerfrom guillotina.utils import get_ownersfrom guillotina_chat.content import IConversation, IMessagefrom zope.interface import Interface

@configure.adapter(for_=(IConversation, Interface),provides=IResourceSerializeToJsonSummary)

class ConversationJSONSummarySerializer(DefaultJSONSummarySerializer):async def __call__(self):

data = await super().__call__()data.update({

'creation_date': self.context.creation_date,'title': self.context.title,'users': self.context.users

})return data

@configure.adapter(for_=(IMessage, Interface),provides=IResourceSerializeToJsonSummary)

class MessageJSONSummarySerializer(DefaultJSONSummarySerializer):async def __call__(self):

data = await super().__call__()data.update({

'creation_date': self.context.creation_date,'text': self.context.text,'author': get_owners(self.context)[0]

})return data

And make sure to add the scan.

configure.scan('guillotina_chat.serialize')

Services

Services are synonymous with what other frameworks might call endpoints or views.

For the sake of our application, let’s use services for getting a user’s most recent conversations and messages for aconversation.

Creating the services

We’ll name our endpoints @get-conversations and @get-messages and put them in a file namedservices.py.

218 Chapter 6. Training

guillotina Documentation, Release 4.2.11

from guillotina import configurefrom guillotina.component import get_multi_adapterfrom guillotina.interfaces import IContainer, IResourceSerializeToJsonSummaryfrom guillotina.utils import get_authenticated_user_idfrom guillotina_chat.content import IConversation

@configure.service(for_=IContainer, name='@get-conversations',permission='guillotina.Authenticated')

async def get_conversations(context, request):results = []conversations = await context.async_get('conversations')user_id = get_authenticated_user_id(request)async for conversation in conversations.async_values():

if user_id in getattr(conversation, 'users', []):summary = await get_multi_adapter(

(conversation, request),IResourceSerializeToJsonSummary)()

results.append(summary)results = sorted(results, key=lambda conv: conv['creation_date'])return results

@configure.service(for_=IConversation, name='@get-messages',permission='guillotina.AccessContent')

async def get_messages(context, request):results = []async for message in context.async_values():

summary = await get_multi_adapter((message, request),IResourceSerializeToJsonSummary)()

results.append(summary)results = sorted(results, key=lambda mes: mes['creation_date'])return results

And make sure to add the scan.

configure.scan('guillotina_chat.services')

Async Utilities

An async utility is a utility that run persistently on the asyncio event loop. It is useful for long running tasks.

For our training, we’re going to use an async utility with a queue to send messages to logged in users.

Create a utility.py file and put the following code in it.

from guillotina import configurefrom guillotina.async_util import IAsyncUtilityfrom guillotina.component import get_multi_adapterfrom guillotina.interfaces import IResourceSerializeToJsonSummaryfrom guillotina.renderers import GuillotinaJSONEncoderfrom guillotina.utils import get_authenticated_user_id, get_current_request

import asyncioimport jsonimport logging

6.5. Training 219

guillotina Documentation, Release 4.2.11

logger = logging.getLogger('guillotina_chat')

class IMessageSender(IAsyncUtility):pass

@configure.utility(provides=IMessageSender)class MessageSenderUtility:

def __init__(self, settings=None, loop=None):self._loop = loopself._settings = {}self._webservices = []

def register_ws(self, ws, request):ws.user_id = get_authenticated_user_id(request)self._webservices.append(ws)

def unregister_ws(self, ws):self._webservices.remove(ws)

async def send_message(self, message):summary = await get_multi_adapter(

(message, get_current_request()),IResourceSerializeToJsonSummary)()

await self._queue.put((message, summary))

async def initialize(self, app=None):self._queue = asyncio.Queue()

while True:try:

message, summary = await self._queue.get()for user_id in message.__parent__.users:

for ws in self._webservices:if ws.user_id == user_id:

await ws.send_str(json.dumps(summary, cls=GuillotinaJSONEncoder))

except Exception:logger.warn(

'Error sending message',exc_info=True)

await asyncio.sleep(1)

Async utilities must implement a initialize method and performs the async task. In our case, it is creating aqueue and waiting to process messages in the queue.

For us, we will send messages to registered websockets.

Make sure, like all other configured moduels, to ensure this file is scanned by the packages __init__.py file.

Sending messages

We’ll need to add another event subscriber to the subscribers.py file in order for the utility to know to send outnew messages to registered web serveices. So your utility.py file will now look like:

220 Chapter 6. Training

guillotina Documentation, Release 4.2.11

from guillotina import configurefrom guillotina.component import get_utilityfrom guillotina.interfaces import IObjectAddedEvent, IPrincipalRoleManagerfrom guillotina.utils import get_authenticated_user_id, get_current_requestfrom guillotina_chat.content import IConversation, IMessagefrom guillotina_chat.utility import IMessageSender

@configure.subscriber(for_=(IConversation, IObjectAddedEvent))async def container_added(conversation, event):

user_id = get_authenticated_user_id(get_current_request())if user_id not in conversation.users:

conversation.users.append(user_id)

manager = IPrincipalRoleManager(conversation)for user in conversation.users or []:

manager.assign_role_to_principal('guillotina_chat.ConversationParticipant', user)

@configure.subscriber(for_=(IMessage, IObjectAddedEvent))async def message_added(message, event):

utility = get_utility(IMessageSender)await utility.send_message(message)

Websockets

Websocket support is built-in to Guillotina.

It’s as simple as using an aiohttp websocket in a service.

Create a ws.py file and put the following code in:

from aiohttp import webfrom guillotina import configurefrom guillotina.component import get_utilityfrom guillotina.interfaces import IContainerfrom guillotina.transactions import get_tmfrom guillotina_chat.utility import IMessageSender

import aiohttpimport logging

logger = logging.getLogger('guillotina_chat')

@configure.service(context=IContainer, method='GET',permission='guillotina.AccessContent', name='@conversate')

async def ws_conversate(context, request):ws = web.WebSocketResponse()utility = get_utility(IMessageSender)utility.register_ws(ws, request)

tm = get_tm(request)await tm.abort(request)await ws.prepare(request)

6.5. Training 221

guillotina Documentation, Release 4.2.11

async for msg in ws:if msg.tp == aiohttp.WSMsgType.text:

# ws does not receive any messages, just sendspass

elif msg.tp == aiohttp.WSMsgType.error:logger.debug('ws connection closed with exception {0:s}'

.format(ws.exception()))

logger.debug('websocket connection closed')utility.unregister_ws(ws)

return {}

Here, we use the utility = get_utility(IMessageSender) to get our async utility we defined previously.Then we register our webservice with utility.register_ws(ws, request).

Our web service is simple because we do not need to receive any messages and the async utility sends out the messages.

Using websockets

In order to use websockets, you need to request a websocket token first.

GET /db/container/@wstokenAuthentication Bearer <jwt token>

Then, use this token to generate a webservice URL(JavaScript example here):

var url = 'ws://localhost:8080/db/container/@conversate?ws_token=' + ws_token;SOCKET = new WebSocket(url);SOCKET.onopen = function(e){};SOCKET.onmessage = function(msg){

var data = JSON.parse(msg.data);};SOCKET.onclose = function(e){};

SOCKET.onerror = function(e){};

Static files

To pull this all together, we’ll create our web application that uses the api to provide a very simple chat experience.

Copy the following files into a new folder static in your application:

• chat.js.

• index.html.

• main.css.

Configure

Then, we’ll setup Guillotina to serve the folder.

222 Chapter 6. Training

guillotina Documentation, Release 4.2.11

Modify your config.yaml file to add:

static:status: ./static

JS Applications

You can also serve the static files in a way where it works with JavaScript applications that need to be able to translateURLs from something other than root.

jsapps:static: ./static

With this configuration any request to a url like http://localhost:8080/static/foo/bar will serve filesfrom http://localhost:8080/static.

6.5.5 Kitchen Sink

This part of the training material is going to talk about the guillotina_kitchensink repository.

This repository gives you a working configuration and install of:

• guillotina_dbusers: Store and manage users on the database

• guillotina_elasticsearch: Index on content in elasticsearch

• guillotina_swagger: Access site swagger definition at http://localhost:8080/@docs

• guillotina_rediscache: Cache db objects in redis

The components it runs as part of the docker compose file are:

• postgresql

• elasticsearch

• redis

First off, start by cloning the repository and starting it.

git clone https://github.com/guillotinaweb/guillotina_kitchensink.gitcd guillotina_kitchensinkdocker-compose -f docker-compose.yaml run --rm --service-ports guillotina

Add some content using Postman and then let’s do an elasticsearch query:

POST /db/container/@search{

"query": {"bool": {

"must": [{"match": {"title": "foobar"

}}

]}

6.5. Training 223

guillotina Documentation, Release 4.2.11

}}

224 Chapter 6. Training

CHAPTER 7

Why Guillotina

• Performance: Traditional Python web servers limit the number of simultaneous requests to the number ofthreads running the server. With AsyncIO, you are able to serve many more simultaneous requests.

• Front-end friendly: Guillotina is designed to make your JavaScript engineers happy. With things like automaticSwagger documentation for endpoints, out of the box CORS and websockets, your front-end team will be happyto work with Guillotina. We speak JSON but can adapt to any content type payload request/response bodies.

• AsyncIO: With AsyncIO, websockets are simple. More interestingly, AsyncIO is an ideal match with microser-vice architectures.

• Object model: Guillotina uses a hierarchial object model. This hierarchy of objects then maps to URLs and isperfect for managing a large number of objects.

• Security: Guillotina has a granular, hierarchical, multidimensional security system that allows you to managethe security of your content at a level not available to other frameworks.

• Scale: With integrations like Redis, ElasticSearch and Cockroach, you have the tools to scale.

225

guillotina Documentation, Release 4.2.11

226 Chapter 7. Why Guillotina

CHAPTER 8

About

• Read about the rich history of the project

Note: Scanning

If your service modules are not imported at run-time, you may need to provide an additional scan call toget your services noticed by guillotina.

In your application __init__.py file, you can simply provide a scan call like:

from guillotina import configuredef includeme(root):

configure.scan('my.package')

8.1 Developer documentation

Contents:

8.2 Configuration

guillotina and its addons define a global configuration that is used. All of these settings are config-urable by providing a JSON configuration file to the start script.

By default, the startup script looks for a config.yaml file. You can use a different file by using the -coption for the script like this: ./bin/guillotina -c myconfig.yaml.

8.2.1 Databases

Guillotina uses PostgreSQL out-of-the-box.

227

guillotina Documentation, Release 4.2.11

To configure available databases, use the databases option. Configuration options map 1-to-1 todatabase setup:

---databases:- db:

storage: postgresqldsn:scheme: postgresdbname: guillotinauser: postgreshost: localhostpassword: ''port: 5432

read_only: false

Currently supported database drivers are:

• postgresql

• cockroach

8.2.2 Static files

static:favicon.ico: static/favicon.icostatic_files: module_name:static

These files will then be available on urls /favicon.ico and /static_files.

8.2.3 JavaScript Applications

We can also serve JS apps from guillotina. These will allow routing on your JS application without anyextra configuration by returning the base directory index.html for every sub directory in the url.

Once there is SSR support in Python, guillotina will integrate with it through this as well.

jsapps:app: path/to/app

8.2.4 Root user password

root_user:password: root

8.2.5 CORS

cors:allow_origin:

- "*"allow_methods:

- GET

228 Chapter 8. About

guillotina Documentation, Release 4.2.11

- POST- DELETE- HEAD- PATCH- PUT

allow_headers:- "*"

expose_headers:- "*"

allow_credentials: truemax_age: 3660

8.2.6 Applications

To extend/override Guillotina, the applications configuration allows you to specify which to enable.

applications:- guillotina_elasticsearch

8.2.7 Async utilities

utilities:-

provides: guillotina.interfaces.ICatalogUtilityfactory: guillotina_elasticsearch.utility.ElasticSearchUtilitysettings: {}

8.2.8 Middleware

guillotina is built on aiohttp which provides support for middleware. You can provide an arrayof dotted names to use for your application.

middlewares:- guillotina_myaddon.Middleware

8.2.9 aiohttp settings

You can pass aiohttp_settings to configure the aiohttp server.

aiohttp_settings:client_max_size: 20971520

8.2.10 JWT Settings

If you want to enable JWT authentication, you’ll need to configure the JWT secret in Guillotina.

jwt:secret: foobaralgorithm: HS256

8.2. Configuration 229

guillotina Documentation, Release 4.2.11

8.2.11 Miscellaneous settings

• port (number): Port to bind to. defaults to 8080

• access_log_format (string): Customize access log format for aiohttp. defaults to None

• store_json (boolean): Serialize object into json field in database. defaults to true

• host (string): Where to host the server. defaults to "0.0.0.0"

• port (number): Port to bind to. defaults to 8080

• conflict_retry_attempts (number): Number of times to retry database conflict errors. de-faults to 3

• cloud_storage (string): Dotted path to cloud storage field type. defaults to "guillotina.interfaces.IDBFileField"

8.2.12 Transaction strategy

Guillotina provides a few different modes to operate in to customize the level of performance versusconsistency. The setting used for this is transaction_strategy which defaults to resolve.

Even though we have different transaction strategies that provide different voting algorithms to decide ifit’s a safe write, all write operations STILL make sure that the object committed matches the transactionit was retrieved with. If not, a conflict error is detected and the request is retried. So even if you choosethe transaction strategy with no database transactions, there is still a level of consistency so that you knowyou will only modify an object that is consistent with the one retrieved from the database.

Example configuration:

databases:- db:

storage: postgresqltransaction_strategy: resolvedsn:scheme: postgresdbname: guillotinauser: postgreshost: localhostpassword: ''port: 5432

Available options:

• none: No db transaction, no conflict resolution. Fastest but most dangerous mode. Use for import-ing data or if you need high performance and do not have multiple writers.

• tidonly: The same as none with no database transaction; however, we still use the database toissue us transaction ids for the data committed. Since no transaction is used, this is potentially justas safe as any of the other strategies just as long as you are not writing to multiple objects at thesame time — in those cases, you might be in an inconsistent state on tid conflicts.

• dbresolve: Use db transaction but do not perform any voting when writing(no conflict resolu-tion).

• dbresolve_readcommitted: Same as no vote; however, db transaction only started at commitphase. This should provide better performance; however, you’ll need to consider the side affects ofthis for reading data.

230 Chapter 8. About

guillotina Documentation, Release 4.2.11

• simple: Detect concurrent transactions and error if another transaction id is committed to the dbahead of the current transaction id. This is the safest mode to operate in but you might see conflicterrors.

• resolve: Same as simple; however, it allows commits when conflicting transactions are writingto different objects.

• resolve_readcommitted: Same as resolve however, db transaction only started at commitphase. This should provide better performance; however, you’ll need to consider that side affects ofthis for reading data.

Warning: not all storages are compatible with all transaction strategies.

8.2.13 Connection class

The default asyncpg connection class has some overhead. Guillotina provides a way to override it with acustom class or a provided lighter one:

pg_connection_class: guillotina.db.storages.pg.LightweightConnection

8.3 Installation/Configuration/Deployment

production

Contents:

8.3.1 Installation

Guillotina is an installable python package which can be installed with pip, easy_install orbuildout.

Additionally, Guillotina provides docker images.

Install with pip

pip install guillotina

Running

Installing Guillotina provide the g executable. To run the server, simply:

g serve

Read command options for details on commands.

8.3. Installation/Configuration/Deployment 231

guillotina Documentation, Release 4.2.11

8.3.2 Production

Nginx front

It’s very common to run the API using nginx with a proxy_pass in front, so there is an option todefine the URL for the generated URLs inside the api:

Adding the header:

X-VirtualHost-Monster https://example.com/api/

will do a rewrite of the URLs.

Sample configuration on nginx:

location /api/ {proxy_set_header X-VirtualHost-Monster $scheme://$http_host/api/proxy_pass http://api.guillotina.svc.cluster.local:80/;

}

8.3.3 Logging

Logging configuration is built into guillotina’s configuration syntax.

If the logging setting is provided, it is simply passed to Python’s dict config method:https://docs.python.org/3.6/library/logging.config.html#logging-config-dictschema

Example guillotina configuration

To log errors for guillotina for example:

{"logging": {"version": 1,"formatters": {"brief": {"format": "%(message)s"

},"default": {"format": "%(asctime)s %(levelname)-8s %(name)-15s %(message)s","datefmt": "%Y-%m-%d %H:%M:%S"

}},"handlers": {

"file": {"class": "logging.handlers.RotatingFileHandler","formatter": "default","filename": "logconfig.log","maxBytes": 1024,"backupCount": 3

}},"loggers": {"guillotina": {"level": "DEBUG","handlers": ["file"],

232 Chapter 8. About

guillotina Documentation, Release 4.2.11

"propagate": 0}

}}

}

Request logging example

{"logging": {"version": 1,"formatters": {"default": {"format": "%(message)s"

}},"handlers": {

"file": {"class": "logging.handlers.RotatingFileHandler","formatter": "default","filename": "access.log","maxBytes": 1024,"backupCount": 3

}},"loggers": {"aiohttp.access": {"level": "INFO","handlers": ["file"],"propagate": 0

}}

}}

Available Loggers

• guillotina

• aiohttp.access

• aiohttp.client

• aiohttp.internal

• aiohttp.server

• aiohttp.web

• aiohttp.websocket

8.4 About

As the Web evolves, so do the frameworks that we use to work with the Web. Guillotina is part of thatevolution, providing an asynchronous web server with a rich, REST-ful API to build your web applications

8.4. About 233

guillotina Documentation, Release 4.2.11

around.

It is designed for building JavaScript applications. It is an API framework, not a typical template-basedrendering framework like most web frameworks (Django/Pyramid/Plone). What we mean by this is thatGuillotina will not generate HTML out-of-the-box for you. It is designed to be consumed by JavaScriptapplications that do the HTML rendering, or to act as a middleware layer on a microservice architecture.

Features:

• REST JSON API

• Built-in authentication/authorization, built-in JWT support

• Hierarchical data/url structure

• Permissions/roles/groups

• Fully customizable permission/roles/groups based on hierarchical data structure

• Robust customizable component architecture and configuration syntax

• Content types, dynamic behaviors

• Built-in CORS support

• JSON schema support

• PostgreSQL and CockroachDB drivers

• Blobs

Guillotina is built on the lessons learned from great technologies of the open source projects Plone, Zope,Pyramid and Django.

Inspirations:

• Plone/Zope’s hierarchical data model

• Pyramid’s decorator-based auto-discovery application configuration syntax

• Django’s global application settings style syntax

• Zope’s component architecture for building robustly customizable applications

• Plone/Zope’s security model

• JSON Schema

Lessons Learned (from said inspired frameworks):

• Trade-offs for the sake of performance is okay

• Too many complex dependencies causes difficulties in management and upgrades

• It’s okay to fork dependency packages

8.4.1 History lesson

In the beginning, there was bobo.

bobo was what Jim Fulton called his initial idea of mapping objects to web urls. It’s an old idea. Abeautiful idea. The developers of Guillotina think it’s the best possible way to conceptualize most content-centric APIs and organization of how your web applications think about data or their APIs.

Think about this simple example. Assuming you have the following dictionary:

234 Chapter 8. About

guillotina Documentation, Release 4.2.11

{"foo": {

"bar": {}}

}

The corresponding urls for a site based off this dictionary would be:

• http://localhost:8080/

• http://localhost:8080/foo

• http://localhost:8080/foo/bar

And so on... It’s a simple way to build APIs from data around a hierarchy (or tree).

Extrapolating this concept, Jim Fulton also built the ZODB package. This was a complete databasebuilt on serializing Python objects using the pickle library. Then, frameworks like Zope (and eventuallyPlone), used this database and the bobo style of publishing objects to URLs to build a framework andCMS around.

8.4.2 An Object Graph: The guillotina datastore.

At the beginging there is the notiion of Content-Type. A content type, it’s just a python interface (A class)that describes an object. Every object could be stored on the db. And every objet, could have child objectsrelated to them. Something like:

/user@account/ /user@account/preferences /user@account/todos /user@account/todos/todos_list1/user@account/todos/todos_list1/todo_item1 /user@account/todos/todos_list1/todo_item2/user@account/todos/todos_list1/todo_item3

Allows us to better express content relations, and this is where guillotina shines, because it offers anautomatic REST API over them.

For exeample you can do a PATCH request over /user@account/preferences, to update, them or you canPOST an item over the /user@account/todos with the necessry payload to create new todo posts lists, oryou can just do a DELETE request to /user@account/todos/todos_list1/todo_item3 to remove a todo listitem.

That’s the main foundation of guillotina, and also one of the most powerful concepts, the permissionsystem, is based on this. As an example, at /user@account path, only the user is allowed to access it.. Allchild objects inherit this permission, anyone else than the owner could access them, but if at some point,we add new readers to an item (a todo list) will give access to other users.

Security is accessed throught /object_path/@sharing

Forked dependency packages

Guillotina has eaten a few packages that would have otherwise been dependencies.

The reasons for forking are:

• Required to support asyncio

• Provide tighter fit for framework

• Make installations less painful and error-prone

• Groking framework is easier when there is one package to import from

8.4. About 235

guillotina Documentation, Release 4.2.11

Forks:

• parts of the ZODB data model: we’re on a relational storage model now

• plone.behavior

• zope.security

• zope.schema

• zope.component/zope.configuration

• zope.dublincore

• zope.i18n

• zope.lifecycleevent

• zope.location

• zope.event

8.4.3 What it isn’t

• Guillotina is not a replacement for Plone

• Guillotina is not a re-implementation of Plone

• Guillotina does not implement all the features and APIs of Plone

It could some day with the guillotina_cms package but replacement of Plone is not the goal of Guillotina.

8.5 Awesome Guillotina

Some useful applications to get you started:

• guillotina_swagger: Automatic swagger generation

• guillotina_elasticsearch: Elasticsearch integration

• guillotina_dbusers: Users/Groups stored as content

• guillotina_mailer: Mailer utilities

• guillotina_rediscache: Cache database with redis

• guillotina_s3storage: S3 file storage

• guillotina_gcloudstorage: GCloud file storage

• guillotina_statsd: statsd metrics

• guillotina_prometheus: prometheus stats

Where to find more packages:

• Guillotina Web

• Onna

236 Chapter 8. About

guillotina Documentation, Release 4.2.11

8.6 Quick tour of Guillotina

Guillotina is powerful datastore, capable of storing and indexing milions 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

• Serialitzation/Validiation library integrated.

• Elastic search integration throught guillotina_elasticsearch, or fallback to postgres json indexing.

• Declarative configuration using decorators.

• Integrated cloud storage file uploads.

• py.test fixtues 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.

8.7 What is Guillotina like?

8.7.1 Example configuration:

---applications:- myappdatabases:- db:

storage: postgresqldsn:scheme: postgresdbname: guillotinauser: postgreshost: localhostpassword: ''port: 5432

8.6. Quick tour of Guillotina 237

guillotina Documentation, Release 4.2.11

port: 8080root_user:password: root

8.7.2 Example service:

See instructions below to play with.

from guillotina import configure

@configure.service(name='@foobar')async def foobar(context, request):

return {"foo": "bar"}

8.7.3 Example content type:

See instructions below to play with.

from guillotina import configurefrom guillotina import contentfrom guillotina import Interfacefrom 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

8.7.4 Example usage:

See instructions below to play with.

POST /db/container/Create MyType

Example http

POST /db/container HTTP/1.1Accept: application/jsonContent-Type: application/jsonHost: localhost:8080Authorization: Basic cm9vdDpyb290

{"@type": "MyType","id": "foobar","foobar": "foobar"

}

238 Chapter 8. About

guillotina Documentation, Release 4.2.11

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 OKContent-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.1Accept: application/jsonHost: localhost:8080Authorization: Basic cm9vdDpyb290

8.7. What is Guillotina like? 239

guillotina Documentation, Release 4.2.11

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 OKContent-Length: 851Content-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"

240 Chapter 8. About

guillotina Documentation, Release 4.2.11

}}

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/@foobarUse foobar service

Example http

POST /db/@foobar HTTP/1.1Accept: application/jsonHost: localhost:8080Authorization: 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 OKContent-Type: application/json

{ "foo": "bar"}

or http

POST /db/container/@foobar HTTP/1.1Accept: application/jsonHost: localhost:8080Authorization: Basic cm9vdDpyb290

8.7. What is Guillotina like? 241

guillotina Documentation, Release 4.2.11

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 OKContent-Type: application/json

{ "foo": "bar"}

or http

POST /db/container/foobar/@foobar HTTP/1.1Accept: application/jsonHost: localhost:8080Authorization: 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'))

242 Chapter 8. About

guillotina Documentation, Release 4.2.11

response

HTTP/1.1 201 OKContent-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.

8.7.5 Playing with those examples

In order to play with those examples you should install guillotina and cookiecutter, let’s do that in a pythonvirtualenv:

$ virtualenv .$ source ./bin/activate$ pip install guillotina cookiecutter

Then use guillotina templates to create an application:

$ ./bin/g create --template=applicationCould not find the configuration file config.json. Using default settings.full_name []: My Appemail []: [email protected]_name [guillotina_myproject]: myappproject_short_description [Guillotina server application python project]:Select open_source_license:1 - MIT license2 - BSD license3 - ISC license4 - Apache Software License 2.05 - GNU General Public License v36 - Not open sourceChoose from 1, 2, 3, 4, 5, 6 [1]:

You should now have a structure like the following one:

.myapp

README.rstconfig.yamlmyapp

__init__.pyapi.pyinstall.py

setup.py

8.7. What is Guillotina like? 243

guillotina Documentation, Release 4.2.11

Now copy Example content type section content in myapp/myapp/content.py.

Add configure.scan('myapp.content') to myapp/myapp/__init__.py includemefunction.

@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.1Accept: application/jsonContent-Type: application/jsonHost: localhost:8080Authorization: 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',

244 Chapter 8. About

guillotina Documentation, Release 4.2.11

'Content-Type': 'application/json',}, json={

'@type': 'Container','description': 'Description','id': 'container','title': 'Container 1',

}, auth=('root', 'root'))

response

HTTP/1.1 201 OKContent-Type: application/json

You can now use all above examples.

8.8 Quickstart

How to quickly get started using guillotina.

This tutorial will assume usage of virtualenv. You can use your own preferred tool for managing yourpython environment.

This tutorial assumes you have postgresql running

Setup the environment:

virtualenv .

Install guillotina:

./bin/pip install guillotina

Generate configuration file (requires cookiecutter):

./bin/pip install cookiecutter

./bin/g create --template=configuration

Finally, run the server:

./bin/g

The server should now be running on http://0.0.0.0:8080

Then, use Postman, curl or whatever tool you prefer to interact with the REST API.

You can also navigate in your Guillotina server with its built-in web admin interface by visitinghttp://localhost:8080/+admin/.

Modify the configuration in config.yaml to customize server setttings.

8.8.1 Postgresql installation instructions

If you do not have a postgresql database server installed, you can use docker to get one running quickly.

Example docker run command:

8.8. Quickstart 245

guillotina Documentation, Release 4.2.11

docker run -e POSTGRES_DB=guillotina -e POSTGRES_USER=guillotina -p 127.0.0.→˓1:5432:5432 postgres:9.6

8.8.2 Creating a container

Guillotina containers are the building block of all other content. A container is where you place all othercontent for your application. Only containers can be created inside databases.

Let’s create one: http

POST /db/ HTTP/1.1Accept: application/jsonContent-Type: application/jsonHost: localhost:8080Authorization: Basic cm9vdDpyb290

{"@type": "Container","title": "Guillotina 1","id": "guillotina","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": "guillotina", "title": "Guillotina 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": "guillotina", "title": "Guillotina 1"}→˓' --auth-no-challenge --user=root --password=root

httpie

echo '{"@type": "Container","description": "Description","id": "guillotina","title": "Guillotina 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': 'guillotina',

246 Chapter 8. About

guillotina Documentation, Release 4.2.11

'title': 'Guillotina 1',}, auth=('root', 'root'))

response

HTTP/1.1 201 OKContent-Type: application/json

and create content inside the container: http

POST /db/guillotina/ HTTP/1.1Accept: application/jsonContent-Type: application/jsonHost: localhost:8080Authorization: Basic cm9vdDpyb290

{"@type": "Item","title": "News","id": "news"

}

curl

curl -i -X POST http://localhost:8080/db/guillotina/ -H 'Accept: application/→˓json' -H 'Content-Type: application/json' --data-raw '{"@type": "Item", "id→˓": "news", "title": "News"}' --user root:root

wget

wget -S -O- http://localhost:8080/db/guillotina/ --header='Accept:→˓application/json' --header='Content-Type: application/json' --post-data='{→˓"@type": "Item", "id": "news", "title": "News"}' --auth-no-challenge --→˓user=root --password=root

httpie

echo '{"@type": "Item","id": "news","title": "News"

}' | http POST http://localhost:8080/db/guillotina/ Accept:application/json→˓Content-Type:application/json -a root:root

python-requests

requests.post('http://localhost:8080/db/guillotina/', headers={'Accept': 'application/json','Content-Type': 'application/json',

}, json={'@type': 'Item','id': 'news','title': 'News',

}, auth=('root', 'root'))

response

8.8. Quickstart 247

guillotina Documentation, Release 4.2.11

HTTP/1.1 201 OKContent-Type: application/json

8.8.3 Retrieving your data

Let’s navigating throught your newly created data.

First you can see all your containers using the following, notice that at the moment there’s only one namedguillotina: http

GET /db/ HTTP/1.1Accept: application/jsonHost: localhost:8080Authorization: Basic cm9vdDpyb290

curl

curl -i http://localhost:8080/db/ -H 'Accept: application/json' --user→˓root:root

wget

wget -S -O- http://localhost:8080/db/ --header='Accept: application/json' --→˓auth-no-challenge --user=root --password=root

httpie

http http://localhost:8080/db/ Accept:application/json -a root:root

python-requests

requests.get('http://localhost:8080/db/', headers={'Accept': 'application/json',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

{"@type": "Database","containers": [

"guillotina"]

}

Then you could explore container data using: http

GET /db/guillotina HTTP/1.1Accept: application/jsonHost: localhost:8080Authorization: Basic cm9vdDpyb290

curl

248 Chapter 8. About

guillotina Documentation, Release 4.2.11

curl -i http://localhost:8080/db/guillotina -H 'Accept: application/json' --→˓user root:root

wget

wget -S -O- http://localhost:8080/db/guillotina --header='Accept:→˓application/json' --auth-no-challenge --user=root --password=root

httpie

http http://localhost:8080/db/guillotina Accept:application/json -a root:root

python-requests

requests.get('http://localhost:8080/db/guillotina', headers={'Accept': 'application/json',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

{"@id": "http://localhost:8080/db/guillotina","@name": "guillotina","@type": "Container","@uid": "7d9ebe1b2e1044688c83985e9e0a7ef3","UID": "7d9ebe1b2e1044688c83985e9e0a7ef3","__behaviors__": [],"__name__": "guillotina","creation_date": "2018-07-21T09:37:28.125034+00:00","is_folderish": true,"items": [

{"@id": "http://localhost:8080/db/guillotina/news","@name": "news","@type": "Item","@uid": "7d9|11729830722c4e43924df18d21d14bdf","UID": "7d9|11729830722c4e43924df18d21d14bdf"

}],"length": 1,"modification_date": "2018-07-21T09:37:28.125034+00:00","parent": {},"title": "Guillotina 1","type_name": "Container","uuid": "7d9ebe1b2e1044688c83985e9e0a7ef3"

}

And finally query a specific content inside the container using: http

GET /db/guillotina/news HTTP/1.1Accept: application/jsonHost: localhost:8080Authorization: Basic cm9vdDpyb290

curl

8.8. Quickstart 249

guillotina Documentation, Release 4.2.11

curl -i http://localhost:8080/db/guillotina/news -H 'Accept: application/json→˓' --user root:root

wget

wget -S -O- http://localhost:8080/db/guillotina/news --header='Accept:→˓application/json' --auth-no-challenge --user=root --password=root

httpie

http http://localhost:8080/db/guillotina/news Accept:application/json -a→˓root:root

python-requests

requests.get('http://localhost:8080/db/guillotina/news', headers={'Accept': 'application/json',

}, auth=('root', 'root'))

response

HTTP/1.1 200 OKContent-Type: application/json

{"@id": "http://localhost:8080/db/guillotina/news","@name": "news","@type": "Item","@uid": "7d9|11729830722c4e43924df18d21d14bdf","UID": "7d9|11729830722c4e43924df18d21d14bdf","__behaviors__": [],"__name__": "news","creation_date": "2018-07-21T09:37:41.863014+00:00","guillotina.behaviors.dublincore.IDublinCore": {

"contributors": ["root"

],"creation_date": "2018-07-21T09:37:41.863014+00:00","creators": [

"root"],"description": null,"effective_date": null,"expiration_date": null,"modification_date": "2018-07-21T09:37:41.863014+00:00","publisher": null,"tags": null,"title": "News"

},"is_folderish": false,"modification_date": "2018-07-21T09:37:41.863014+00:00","parent": {

"@id": "http://localhost:8080/db/guillotina","@name": "guillotina","@type": "Container","@uid": "7d9ebe1b2e1044688c83985e9e0a7ef3","UID": "7d9ebe1b2e1044688c83985e9e0a7ef3"

},

250 Chapter 8. About

guillotina Documentation, Release 4.2.11

"title": "News","type_name": "Item","uuid": "7d9|11729830722c4e43924df18d21d14bdf"

}

8.8. Quickstart 251

guillotina Documentation, Release 4.2.11

252 Chapter 8. About

Python Module Index

gguillotina.component, 187guillotina.configure, 169guillotina.content, 173guillotina.fields, 181guillotina.request, 174guillotina.response, 175guillotina.schema, 179guillotina.utils, 181guillotina.utils.execute, 185

253

guillotina Documentation, Release 4.2.11

254 Python Module Index

HTTP Routing Table

/(db)GET /(db), 38GET /(db)/(container), 39GET /(db)/(container)/(content)/@download/file,

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

197GET /(db)/(container)/(id), 64GET /(db)/(container)/(id)/@addable-types,

109GET /(db)/(container)/(id)/@all_permissions,

75GET /(db)/(container)/(id)/@behaviors,

66GET /(db)/(container)/(id)/@canido, 80GET /(db)/(container)/(id)/@download/(field_name),

75GET /(db)/(container)/(id)/@ids, 110GET /(db)/(container)/(id)/@invalidate-cache,

86GET /(db)/(container)/(id)/@items, 110GET /(db)/(container)/(id)/@sharing, 81GET /(db)/(container)/@addons, 53GET /(db)/(container)/@dynamic-fields,

57GET /(db)/(container)/@registry, 50GET /(db)/(container)/@registry/(key),

52GET /(db)/(container)/@types, 40GET /(db)/(container)/@types/(type_name),

46GET /(db)/(container)/@user, 49POST /(db), 37POST /(db)/(container), 191POST /(db)/(container)/(content)/@sharing,

198POST /(db)/(container)/(content)/@tusupload/file,

195POST /(db)/(container)/(id), 58

POST /(db)/(container)/(id)/@duplicate,85

POST /(db)/(container)/(id)/@move, 84POST /(db)/(container)/(id)/@sharing,

82POST /(db)/(container)/@addons, 54POST /(db)/(container)/@registry, 51PUT /(db)/(container)/(id)/@sharing, 83DELETE /(db)/(container)/(id), 65DELETE /(db)/(container)/(id)/@behaviors,

70DELETE /(db)/(container)/@addons, 55PATCH /(db)/(container), 56PATCH /(db)/(container)/(content)/@behaviors,

192PATCH /(db)/(container)/(content)/@tusupload/file,

196PATCH /(db)/(container)/(content)/@upload/file,

193PATCH /(db)/(container)/(id), 63PATCH /(db)/(container)/(id)/@behaviors,

69PATCH /(db)/(container)/(id)/@upload/(field_name),

71PATCH /(db)/(container)/@behaviors, 56PATCH /(db)/(container)/@registry/(key),

51

/GETGET GET, 36

/dbGET /db/container/foobar/, 239POST /db/@foobar, 241POST /db/container/, 238

255

guillotina Documentation, Release 4.2.11

256 HTTP Routing Table

Index

Symbols__init__() (guillotina.response.ErrorResponse method),

175__init__() (guillotina.response.HTTPMethodNotAllowed

method), 177__init__() (guillotina.response.Response method), 175

Aacl (guillotina.content.Resource attribute), 173adapter() (in module guillotina.configure), 171add_behavior() (guillotina.content.Resource method),

173add_future() (guillotina.request.Request method), 174addon() (in module guillotina.configure), 171after_commit() (guillotina.utils.execute.ExecuteContext

method), 187after_commit() (in module guillotina.utils.execute), 186after_request() (guillotina.utils.execute.ExecuteContext

method), 187after_request() (in module guillotina.utils.execute), 185after_request_failed() (guil-

lotina.utils.execute.ExecuteContext method),187

after_request_failed() (in module guil-lotina.utils.execute), 186

apply_coroutine() (in module guillotina.utils), 184async_contains() (guillotina.content.Folder method), 173async_del() (guillotina.content.Folder method), 173async_get() (guillotina.content.Folder method), 173async_items() (guillotina.content.Folder method), 173async_keys() (guillotina.content.Folder method), 173async_len() (guillotina.content.Folder method), 173async_multi_get() (guillotina.content.Folder method),

174async_set() (guillotina.content.Folder method), 174

Bbefore_commit() (guillotina.utils.execute.ExecuteContext

method), 187

before_commit() (in module guillotina.utils.execute), 186behavior() (in module guillotina.configure), 170bind() (guillotina.schema.Choice method), 180bind() (guillotina.schema.Dict method), 180Bool (class in guillotina.schema), 179BucketListField (class in guillotina.fields), 181Bytes (class in guillotina.schema), 180

CChoice (class in guillotina.schema), 180CloudFileField (class in guillotina.fields), 181Container (class in guillotina.content), 174contenttype() (in module guillotina.configure), 170create_content_in_container() (in module guil-

lotina.content), 174

DDate (class in guillotina.schema), 180Datetime (class in guillotina.schema), 180Decimal (class in guillotina.schema), 180delete() (guillotina.utils.Navigator method), 185Dict (class in guillotina.schema), 180

EErrorResponse (class in guillotina.response), 175execute_futures() (guillotina.request.Request method),

175ExecuteContext (class in guillotina.utils.execute), 187

FFloat (class in guillotina.schema), 180Folder (class in guillotina.content), 173from_unicode() (guillotina.schema.Bool method), 179from_unicode() (guillotina.schema.Bytes method), 180from_unicode() (guillotina.schema.Choice method), 180from_unicode() (guillotina.schema.Decimal method),

180from_unicode() (guillotina.schema.Float method), 180from_unicode() (guillotina.schema.Int method), 180

257

guillotina Documentation, Release 4.2.11

from_unicode() (guillotina.schema.Text method), 181

Gget() (guillotina.utils.Navigator method), 185get_adapter() (in module guillotina.component), 187get_adapters() (in module guillotina.component), 188get_all_possible_schemas_for_type() (in module guil-

lotina.content), 174get_all_utilities_registered_for() (in module guil-

lotina.component), 189get_authenticated_user() (in module guillotina.utils), 183get_authenticated_user_id() (in module guillotina.utils),

183get_behavior() (in module guillotina.utils), 182get_caller_module() (in module guillotina.utils), 184get_content_path() (in module guillotina.utils), 182get_current_request() (in module guillotina.utils), 181get_dotted_name() (in module guillotina.utils), 184get_future() (guillotina.request.Request method), 175get_module_dotted_name() (in module guillotina.utils),

184get_multi_adapter() (in module guillotina.component),

188get_object_by_oid() (in module guillotina.utils), 182get_object_url() (in module guillotina.utils), 182get_owners() (in module guillotina.utils), 182get_random_string() (in module guillotina.utils), 183get_utilities_for() (in module guillotina.component), 189get_utility() (in module guillotina.component), 188grant() (in module guillotina.configure), 172guillotina.component (module), 187guillotina.configure (module), 169guillotina.content (module), 173guillotina.fields (module), 181guillotina.request (module), 174guillotina.response (module), 175guillotina.schema (module), 179guillotina.utils (module), 181guillotina.utils.execute (module), 185

HHTTPAccepted (class in guillotina.response), 176HTTPBadGateway (class in guillotina.response), 179HTTPBadRequest (class in guillotina.response), 177HTTPClientError (class in guillotina.response), 177HTTPConflict (class in guillotina.response), 178HTTPCreated (class in guillotina.response), 176HTTPError (class in guillotina.response), 175HTTPExpectationFailed (class in guillotina.response),

178HTTPFailedDependency (class in guillotina.response),

178HTTPForbidden (class in guillotina.response), 177HTTPFound (class in guillotina.response), 176

HTTPGatewayTimeout (class in guillotina.response), 179HTTPGone (class in guillotina.response), 178HTTPInsufficientStorage (class in guillotina.response),

179HTTPInternalServerError (class in guillotina.response),

179HTTPLengthRequired (class in guillotina.response), 178HTTPMethodNotAllowed (class in guillotina.response),

177HTTPMisdirectedRequest (class in guillotina.response),

178HTTPMovedPermanently (class in guillotina.response),

176HTTPMultipleChoices (class in guillotina.response), 176HTTPNetworkAuthenticationRequired (class in guil-

lotina.response), 179HTTPNoContent (class in guillotina.response), 176HTTPNonAuthoritativeInformation (class in guil-

lotina.response), 176HTTPNotAcceptable (class in guillotina.response), 177HTTPNotExtended (class in guillotina.response), 179HTTPNotFound (class in guillotina.response), 177HTTPNotImplemented (class in guillotina.response), 179HTTPNotModified (class in guillotina.response), 177HTTPOk (class in guillotina.response), 176HTTPPartialContent (class in guillotina.response), 176HTTPPaymentRequired (class in guillotina.response),

177HTTPPermanentRedirect (class in guillotina.response),

177HTTPPreconditionFailed (class in guillotina.response),

178HTTPPreconditionRequired (class in guil-

lotina.response), 178HTTPProxyAuthenticationRequired (class in guil-

lotina.response), 178HTTPRedirection (class in guillotina.response), 176HTTPRequestEntityTooLarge (class in guil-

lotina.response), 178HTTPRequestHeaderFieldsTooLarge (class in guil-

lotina.response), 179HTTPRequestRangeNotSatisfiable (class in guil-

lotina.response), 178HTTPRequestTimeout (class in guillotina.response), 178HTTPRequestURITooLong (class in guillotina.response),

178HTTPResetContent (class in guillotina.response), 176HTTPSeeOther (class in guillotina.response), 177HTTPServerError (class in guillotina.response), 179HTTPServiceUnavailable (class in guillotina.response),

179HTTPSuccessful (class in guillotina.response), 176HTTPTemporaryRedirect (class in guillotina.response),

177

258 Index

guillotina Documentation, Release 4.2.11

HTTPTooManyRequests (class in guillotina.response),178

HTTPUnauthorized (class in guillotina.response), 177HTTPUnavailableForLegalReasons (class in guil-

lotina.response), 179HTTPUnprocessableEntity (class in guillotina.response),

178HTTPUnsupportedMediaType (class in guil-

lotina.response), 178HTTPUpgradeRequired (class in guillotina.response),

178HTTPUseProxy (class in guillotina.response), 177HTTPVariantAlsoNegotiates (class in guil-

lotina.response), 179HTTPVersionNotSupported (class in guillotina.response),

179

Iimport_class() (in module guillotina.utils), 184in_pool() (in module guillotina.utils.execute), 186in_queue() (in module guillotina.utils.execute), 186in_queue_with_func() (in module guil-

lotina.utils.execute), 187Int (class in guillotina.schema), 180InvalidRoute (class in guillotina.response), 177Item (class in guillotina.content), 173iter_parents() (in module guillotina.utils), 182iter_schemata() (in module guillotina.content), 174

Jjson_schema_definition() (in module guil-

lotina.configure), 169JSONField (class in guillotina.schema), 181

Llanguage() (in module guillotina.configure), 172lazy_apply() (in module guillotina.utils), 185List (class in guillotina.schema), 181

Mmerge_dicts() (in module guillotina.utils), 184

Nnavigate_to() (in module guillotina.utils), 182Navigator (class in guillotina.utils), 185

PPatchField (class in guillotina.fields), 181permission() (in module guillotina.configure), 171

Qquery_adapter() (in module guillotina.component), 188

query_multi_adapter() (in module guillotina.component),188

query_utility() (in module guillotina.component), 189

Rrecord() (guillotina.request.Request method), 175remove_behavior() (guillotina.content.Resource method),

173renderer() (in module guillotina.configure), 172Request (class in guillotina.request), 174resolve_dotted_name() (in module guillotina.utils), 183resolve_module_path() (in module guillotina.utils), 184resolve_path() (in module guillotina.utils), 184Resource (class in guillotina.content), 173Response (class in guillotina.response), 175role() (in module guillotina.configure), 171

Ssafe_unidecode() (in module guillotina.utils), 185scan() (in module guillotina.configure), 169service() (in module guillotina.configure), 169Set (class in guillotina.schema), 181strings_differ() (in module guillotina.utils), 183sync() (guillotina.utils.Navigator method), 185

TText (class in guillotina.schema), 181TextLine (class in guillotina.schema), 181Time (class in guillotina.schema), 181

Uutility() (in module guillotina.configure), 171uuid (guillotina.content.Resource attribute), 173

Vvalue_deserializer() (in module guillotina.configure), 172value_serializer() (in module guillotina.configure), 172vocabulary() (in module guillotina.configure), 170

Index 259