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
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
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
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
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
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
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
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
Python Module Index
gguillotina.component, 187guillotina.configure, 169guillotina.content, 173guillotina.fields, 181guillotina.request, 174guillotina.response, 175guillotina.schema, 179guillotina.utils, 181guillotina.utils.execute, 185
253
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
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