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