Extensible & composable

API

Some context about Nuxeo Platform

  • OSGi like component architecture
  • Service oriented Java API
  • Extension point system


Everything is a plugin

Nuxeo API challenges

expose Nuxeo Platform via http without loosing our soul

Being dynamic

  • Services to expose depends on deployment option

    +workflowService +conversionService -relationService
  • Contribute custom Services

    +myCustomBusinessService
  • Data Structures (users, docs ...) depends on configuration

    
    {
    common : {...},
    dublincore : {...},
    customSchema : {...},
    ...
    }
    

Being composable

  • "Pack" several services calls inside the same transaction

    updateDoc + validateTask + generatePDF
  • Fetch all required information in one call

    • aggregate all required info
    doc attributes + associated tasks + urls

And also

  • efficiently manage streams

    byte[]    ==> evil
    MultiPart ==> pain
  • manage authentication in an easy and pluggable way

  • be HTML/JavaScript friendly

    new XMLHttpRequest()

How we built the Automation API

expose a dynamic http API

JAX-RS vs SOAP

  • Soap sucks
    • super static / not composable
    • not web friendly (JS, Blob)
    • heavy and complex
  • use JAX-RS + JSON
    • simple but efficient

Automation REST EndPoint

  • Expose Nuxeo entities as REST resources
    • Documents, Users, Workflows, Tasks ...
  • CRUD via GET, PUT, POST, DELETE

    GET /nuxeo/api/v1/path/{path} HTTP/1.1
    GET /nuxeo/api/v1/id/{uid} HTTP/1.1
    GET /nuxeo/api/v1/user/{userName} HTTP/1.1
    GET /nuxeo/api/v1/group/{groupId} HTTP/1.1

REST Endpoint : GET Document

GET /nuxeo/api/v1/path/default-domain/workspaces/WS/TestNote HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{
  "entity-type": "document",
  "repository": "default",
  "uid": "6a3998e3-6890-45f5-9c19-b708814a9c1c",
  "path": "/default-domain/workspaces/WS/TestNote",
  "type": "Note",
  "state": "project",
  "versionLabel": "0.0",
  "isCheckedOut": true,
  "title": "TestNote",
  "lastModified": "2014-01-20T13:11:29.64Z",
  ...
}

Automation RPC : Operations EndPoint

  • Coarse grained API on top of Java API
    • each services can contribute Operations
  • Expose endpoint for Operations
    • GET to retrieve definition
    • POST to execute

Operations EndPoint

GET /nuxeo/site/automation/Document.PageProvider HTTP/1.1

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

id":"Document.PageProvider", "label":"PageProvider", "description":"Perform a query ...", "signature":[ "void", "documents" ], "params":[ { "name":"page", "type":"integer", "required":false },{
"name":"query", "type":"string", "required":false, }, ... ] }

Operations EndPoint

POST /nuxeo/site/automation/Document.PageProvider HTTP/1.1
Content-Type: application/json+nxrequest
{ "params" :
    { "query" : "select * from Note",
      "page" : 0
    }
}

HTTP/1.1 200 OK
Content-Type: application/json
{
  "entity-type": "documents",
  "pageIndex": 0,
  "pageSize": 2,
  "pageCount": 2,
  "entries": [
    {
      "entity-type": "document",
      "repository": "default",
      "uid": "3f76a415-ad73-4522-9450-d12af25b7fb4",
      ...
    }, { ...}, ...
 ]
}

Dynamic data structure

Fetch all data in one call

Control Marshaling

  • use header to control what schemas should be sent

    X-NXDocumentProperties dublincore, common, file

    fetch the data needed on the client side and nothing more

GET /nuxeo/api/v1/path/default-domain/workspaces/WS/TestNote HTTP/1.1
X-NXDocumentProperties dublincore, common, file

Control Marshaling

HTTP/1.1 200 OK
Content-Type: application/json
{  "entity-type": "document",
  ...
  "properties": {
    "note:mime_type": "text/x-web-markdown",
    "note:note": "##Hello",
    "files:files": [
      {
        "file": {
          "name": "layout.png",
          "mime-type": "image/png",
          "length": "43627",
          "data": "files/6a3998e3-6890-45f5-9c19-b708814a9c1c"
      } } ] } ...
}

Fetch extra info

  • use RestContributor to fetch additional info (Mixins)

    • urls, breadcrumb info, comments, related documents ...
  • use header to tell server what info is needed

    X-NXContext-Category breadcrumb
GET /nuxeo/api/v1/path/default-domain/workspaces/WS/TestNote HTTP/1.1
X-NXContext-Category breadcrumb

Fetch REST contributions

HTTP/1.1 200 OK
Content-Type: application/json
{
  "entity-type": "document",
  ...
  "contextParameters": {
    "breadcrumb": {
      "entity-type": "documents",
      "entries": [
        {
          "entity-type": "document",
          "repository": "default",
          "path": "/default-domain",
          ...
        }, ... ] } }
}

Leverage adapter system

  • contribute custom logic

@WebAdapter(name = BOAdapter.NAME,
            type = "BOService", targetType = "Document")
@Produces({ "application/json+nxentity"})
public class BOAdapter extends DefaultAdapter {
  • expose as business object with custom marshaling
GET /nuxeo/api/v1/path/domain/workspaces/WS/note@audit HTTP/1.1
GET /nuxeo/api/v1/path/domain/workspaces/WS/note@bo/MyBO HTTP/1.1

Compose API

Expose the API that matches your needs

Automation Chains

  • Operations have Input / Output and Arguments
  • Operations can be chained on the service side
    • one call , one transaction

doc => updateDoc
           =(doc)=> validateTask
                            =(doc)=> generatePDF =(Blob)=>

Create new Operations via composition

Compose Chain

via XML / via Drag&Drop


Operations and resources

  • Bridge Operations and Resources Endpoints
  • "pipe Operation or Chain" on resource
 fetch resource => feed Operation or Chain
POST /nuxeo/api/v1/path/domain/workspaces/WS/note/@op/Blob.ToPDF

Operations and Extensions

  • Contribute new Operations via Extension Point
  • Contribute new Adapters via Extension Point
  • Compose Operations via Workflow

REST EndPoint and Automation


Dynamic and Composable API

Cool, but what is the tradeof

API is more poweful, but more complex

  • Data mapping needs to be dynamic

  • Calling dynamic endpoints is more complex than static API

endpoint.invoke("createDocument", {input: ... , params:...})
nuxeo.createDocument(path, name, type)

Solution : Provide client lib

  • provides some data mapping
  • it does some checks
  • it provides a simpler API
    (createDocument, addComment ...)


Java, Javascript, Python, Dart, Php ...

SOA and Dynamic API

  • More constraints on ESB UI Designer

    • dynamic endpoints and signatures
  • Needs dynamic data mapping

    • introspection is required
  • Nuxeo complex properties are also a challenge

Mule ESB example

  • Leverage Java Lib service wrapper
  • Use invoke like pattern for dynamic Operations
    • can not dynamicaly build UI from Automation metadata
  • Plug Mule DataSense with Nuxeo Types introspection
    • Mule fetches datatypes from Nuxeo

Dynamic data mapping


Next steps

  • nuxeo.io !
  • Leverage Automation introspection

    • generate Service wrapper for client libs
    • generate Blockly fragments for UI Chain designer


Q&A ?

Dynamic and Composable API

By Thierry Delprat

Dynamic and Composable API

  • 5,605