Author: | Ramon Bartl |
---|---|
Version: | 0.2 |
Table of Contents
This is an add-on package for plone.jsonapi.core which provides some basic URLs for Plone standard contents (and more).
The routes package is built on top of the plone.jsonapi.core package to allow Plone developers to build modern (JavaScript) web UIs which communicate through a RESTful API with their Plone site.
The plone.jsonapi.routes is compatible with Plone 4.
The official release is on pypi, so you have to simply include plone.jsonapi.routes to your buildout config.
Example:
[buildout] ... [instance] ... eggs = ... plone.jsonapi.core plone.jsonapi.routes
The routes for the standard Plone content types get registered on startup.
After installation, the Plone API routes are available below the
plone.jsonapi.core root URL (@@API) with the base /plone/api/1.0
, for example
http://localhost:8080/Plone/@@API/plone/api/1.0/api.json
.
Note
Please see the documentation of plone.jsonapi.core for the API root URL.
There is also an overview of the registered routes which can be accessed here:
http://localhost:8080/Plone/@@API/plone/api/1.0/api.json
BASE_URL: | /plone/api/1.0 |
---|
This is an overview of the provided API Routes. The basic content routes provide all an interface for CRUD operations.
CRUD URL Scheme:
OPERATION | URL | METHOD |
---|---|---|
VIEW | <BASE_URL>/<RESOURCE>/<uid:optional> | GET |
CREATE | <BASE_URL>/<RESOURCE>/create/<uid:optional> | POST |
UPDATE | <BASE_URL>/<RESOURCE>/update/<uid:optional> | POST |
DELETE | <BASE_URL>/<RESOURCE>/delete/<uid:optional> | POST |
Important
the optional UID of the create, update and delete URLs is to specify the target container where to create the content. If this is omitted, the API expects a parameter parent_uid in the request body JSON. If this is also not found, an API Error will be returned.
All GET resources acceppt request parameters.
Parameter | Type | Description |
---|---|---|
limit | number | limit the search results |
sort_on | index | sort the results by the given catalog index |
sort_order | asc/desc | sort ascending/descending |
q | query | search the SearchableText index for the given query string |
creator | username | search for items which were created by the given user |
Search for documents and return 10 results
http://localhost:8080/Plone/@@API/plone/api/1.0/documents?limit=10
Search all content created by admin
http://localhost:8080/Plone/@@API/plone/api/1.0/documents?creator=admin
Search for documents which contain the text Open-Source
http://localhost:8080/Plone/@@API/plone/api/1.0/documents?q=Open-Source
Search for all documents created by admin which contain the text Open-Source
http://localhost:8080/Plone/@@API/plone/api/1.0/documents?q=Open-Source&creator=admin
The response format is for all resources the same.
Example:
{ url: "http://localhost:8080/Plone/@@API/plone/api/1.0/documents", count: 0, _runtime: 0.0021538734436035156, items: [ ] }
- url
- The resource root url
- count
- Count of found results
- _runtime
- The processing time in milliseconds after the request was received until the respone was prepared.
- items
- An array of result items
BASE_URL: | /plone/api/1.0 |
---|---|
SCHEME: | BASE_URL/RESOURCE |
All content informations are dynamically gathered by the contents schema definition through the IInfo adapter. It is possible to define a more specific adapter for your content type to control the data returned by the API.
Resource | Description |
---|---|
folders | Resource for all Folder contents |
documents | Resource for all Page contents |
events | Resource for all Event contents |
files | Resource for all File contents |
images | Resource for all Image contents |
links | Resource for all Link contents |
newsitems | Resource for all News Item contents |
topics | Resource for all Collection (old style) contents |
collections | Resource for all Collection contents |
BASE_URL: | /plone/api/1.0 |
---|---|
SCHEME: | BASE_URL/RESOURCE |
Beside the content URLs described above, there are some other resources available in this extension.
Resource | Description |
---|---|
users | Resourece for all Plone Users |
users/current | Get the current logged in user |
This package is designed to provide an easy way for you to write your own JSON API for your custom Dexterity content types.
The plone.jsonapi.example package shows how to do so.
Lets say you want to provide a simple CRUD JSON API for your custom Dexterity content type. You want to access the API directly from the plone.jsonapi.core root URL (http://localhost:8080/Plone/@@API/).
First of all, you need to import the CRUD functions of plone.jsonapi.routes:
from plone.jsonapi.routes.api import get_items from plone.jsonapi.routes.api import create_items from plone.jsonapi.routes.api import update_items from plone.jsonapi.routes.api import delete_items
To register your custom routes, you need to import the router module of plone.jsonapi.core. The add_route decorator of this module will register your function with the api framework:
from plone.jsonapi.core import router
The next step is to provide the a function which get called by the plone.jsonapi.core framework:
@router.add_route("/example", "example", methods=["GET"]) def get(context, request): return {}
Lets go through this step by step...
The @router.add_route(...) registers the decorated function with the framework. So the function will be invoked when someone sends a request to @@API/example.
The framework registers the decorated function with the key example. We also provide the HTTP Method GET which tells the framework that we only want to get invoked on a HTTP GET request.
When the function gets invoked, the framework provides a context and a request. The context is usually the Plone site root, because this is where the base view (@@API) is registered. The request contains all needed parameters and headers from the original request.
At the moment we return an empty dictionary. Lets provide something more useful here:
@router.add_route("/example", "example", methods=["GET"]) def get(context, request=None): items = get_items("my.custom.type", request, uid=None, endpoint="example") return { "count": len(items), "items": items, }
The get_items function of the plone.jsonapi.routes.api module does all the heavy lifting here. It searches the catalog for my.custom.type contents, parses the request for any additional parameters or returns all informations of the "waked up" object if the uid is given.
The return value is a list of dictionaries, where each dictionary represents the information of one result, be it a catalog result or the full information set of an object.
Note
without the uid given, only catalog brains are returned
Now we need a way to handle the uid with this function. Therefore we can simple add another add_route decorator around this function:
@router.add_route("/example", "example", methods=["GET"]) @router.add_route("/example/<string:uid>", "example", methods=["GET"]) def get(context, request=None, uid=None): items = get_items("my.custom.type", request, uid=uid, endpoint="example") return { "count": len(items), "items": items, }
This function handles now URLs like @@API/example/4b7a1f... as well and invokes the function directly with the provided UID as the parameter. The get_items tries to find the object with the given UID to provide all informations of the waked up object.
Note
API URLs which contain the UID are automatically generated with the provided endpoint
The CREATE, UPDATE and DELETE functionality is basically identical with the basic VIEW function above, so here in short:
# CREATE @router.add_route("/example/create", "example_create", methods=["POST"]) @router.add_route("/example/create/<string:uid>", "example_create", methods=["POST"]) def create(context, request, uid=None): items = create_items("plone.example.todo", request, uid=uid, endpoint="example") return { "count": len(items), "items": items, } # UPDATE @router.add_route("/example/update", "example_update", methods=["POST"]) @router.add_route("/example/update/<string:uid>", "example_update", methods=["POST"]) def update(context, request, uid=None): items = update_items("plone.example.todo", request, uid=uid, endpoint="example") return { "count": len(items), "items": items, } # DELETE @router.add_route("/example/delete", "example_delete", methods=["POST"]) @router.add_route("/example/delete/<string:uid>", "example_delete", methods=["POST"]) def delete(context, request, uid=None): items = delete_items("plone.example.todo", request, uid=uid, endpoint="example") return { "count": len(items), "items": items, }
A small tec demo is available on youtube:
http://www.youtube.com/watch?v=MiwgkWLMUqk
MIT - do what you want