Skip to content

Latest commit

 

History

History
367 lines (262 loc) · 12.7 KB

README.rst

File metadata and controls

367 lines (262 loc) · 12.7 KB

plone.jsonapi.routes

Author: Ramon Bartl
Version: 0.2

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

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