Thierry Delprat
td@nuxeo.com
https://github.com/tiry/
What we Do and What Problems We Try to Solve
we provide a Platform that developers can use to
build highly customized Content Applications
we provide components, and the tools to assemble them
everything we do is open source
various customers - various use cases
me: developer & CTO - joined the Nuxeo project 10+ years ago
Track game builds
Electronic Flight Bags
Central repository for Models
Food industry PLM
https://github.com/nuxeo
Repository
Services
"One API"
but
Multiple combinations
of
services, plugins
and Domain Models
Expose a Platform: not an application
developers using the platform
want to expose the API of their Application
One Platform "One API"
what we want to have
Client consumes a service, it does not build the service.
We can not build the target Business API: users/devs will do it
Manage the meta-model
Choose what API is exposed
Ask for the data they need
Use custom API
(versioned software artifact)
Client driven
Server controlled
"open bar" seems too messy
"one-size-fits-all" does not work
Content-Type: multipart/mixed
Actually, just the chronology has been adjusted !
Exposing Resources
Target use cases are not defined
Target Domain Model is unknown
Expose raw technical resources !
Expose Use Cases !?
Expose the Domain Model !?
GET /repo/{repoId}/path/{docPath} HTTP 1.1
GET /repo/{repoId}/id/{docId} HTTP 1.1
GET /user/{userName} HTTP 1.1
GET /group/{groupName} HTTP 1.1
GET /directory/{directoryName}/{entryId} HTTP 1.1
GET /workflowModel/{modelName} HTTP 1.1
GET /workflow/{workflowInstanceId} HTTP 1.1
GET /task/{taskId} HTTP 1.1
Documents
Users &
Groups
Tasks &
Workflows
Expose raw resources as EndPoint with REST Bindings
GET /nuxeo/api/v1/path/movies/star-wars HTTP/1.1
{
"entity-type": "document",
"repository": "default",
"uid": "5b352650-e49e-48cf-a4e3-bf97b518e7bf",
"path": "/movies/star-wars",
"type": "MovieCollection",
"isCheckedOut": true,
"title": "Star Wars",
"facets": [
"Folderish"
]
}
Server returns a minimal payload
Client need to control what data schemas are sent
GET /nuxeo/api/v1/path/movies/star-wars HTTP/1.1
X-NXProperties dublincore, common
{
"entity-type": "document",
"repository": "default",
"uid": "5b352650-e49e-48cf-a4e3-bf97b518e7bf",
"path": "/movies/star-wars",
"type": "MovieCollection",
"isCheckedOut": true,
"title": "Star Wars",
"properties": {
...
"common:icon": "/icons/movieCollection.png",
"dc:description": "Star Wars collection",
"dc:creator": "tiry",
"dc:modified": "2015-10-22T02:12:59.07Z",
"dc:lastContributor": "tiry",
"dc:created": "2015-10-22T02:12:59.07Z",
"dc:title": "Star Wars",
...
"dc:contributors": [tiry, "system" ]
},
"facets": [
"Folderish"
]
}
Marshaling registry is pluggable
custom Enrichers can be contributed
"How the data is fetched"
is a server side matter
GET /nuxeo/api/v1/path/movies/star-wars HTTP/1.1
X-NXenrichers.document: thumbnail
{
"entity-type": "document",
"repository": "default",
"uid": "5b352650-e49e-48cf-a4e3-bf97b518e7bf",
"path": "/movies/star-wars",
"type": "MovieCollection",
"isCheckedOut": true,
"title": "Star Wars",
"contextParameters":
{
"thumbnail":
{
"url": "/nuxeo/nxthumb/default/5b352650-e49e-48cf-a4e3-bf97b518e7bf/thumb:thumbnail/Small_photo.jpg"
}
},
"facets": [
"Folderish"
]
}
GET /nuxeo/api/v1/path/movies/star-wars?enrichers.document=thumbnail HTTP/1.1
Implicit JOIN
fetch.objectType=fieldToFetch
translate.objectType=fieldToTranslate
depth=children
GET /nuxeo/api/v1/path/movies/star-wars@acl HTTP/1.1
GET /nuxeo/api/v1/path/movies/star-wars@audit HTTP/1.1
GET /nuxeo/api/v1/path/movies/star-wars@bo/MyBusinessObject HTTP/1.1
{
entity-type: "MovieCollection"
id: "5b352650-e49e-48cf-a4e3-bf97b518e7bf",
"title": "Star Wars"
"episodes": 7
}
GET /nuxeo/api/v1/path/movies/star-wars@bo/MovieCollection HTTP/1.1
{"entity-type": "document",
"properties": {
{
"file:content" : {
"upload-batch' : "0b0061d48f69b072",
"upload-fileId" : 0,
"type" : "blob"
}
}}
POST /api/v1/upload/{batchId}/{fileIdx} HTTP 1.1
X-Upload-Chunk-Index 0
X-Upload-Chunk-Count 5
PUT /nuxeo/api/v1/path/movies/star-wars HTTP/1.1
Without creating 100 endpoints!
Need an other paradigm !
Exposing service API over HTTP
Build a coarse gained API on top of service Java API
select simple
> commandA(p1,p2) | commandB(p3,p4)
Commands
Command
INPUT
(Doc, Blob, User ...)
OUTPUT
(Doc, Blob, User ...)
Parameters
Context
(User, Doc ...)
WebUI.AddErrorMessage WebUI.AddInfoMessage WebUI.AddMessage Document.AddPermission Document.AddToCollection DocumentMultivaluedProperty.addItem Task.ApplyDocumentMapping Blob.AttachOnDocument BlobHolder.AttachOnCurrentDocument AttachFiles Audit.QueryWithPageProvider Blob.ImportClipboard Blob.ImportWorklist Blob.RunConverter Document.BlockPermissionInheritance WorkflowModel.BulkRestartInstances Business.BusinessCreateOperation Business.BusinessFetchOperation Business.BusinessUpdateOperation Navigation.GoBack WorkflowInstance.Cancel Navigation.ChangeCurrentTab Document.CheckIn Document.CheckOut Update.NextStep.ConditionalFolder WebUI.ClearClipboard WebUI.ClearSelectedDocuments WebUI.ClearWorklist WorkflowTask.Complete Blob.ConcatenatePDFs Context.FetchDocument Context.FetchFile Blob.ToPDF Blob.Convert Document.Copy Document.Create FileManager.Import UserWorkspace.CreateDocumentFromBlob Seam.CreateDocumentInUI Picture.Create Document.CreateLiveProxy Document.AddRelation Collection.Create Workflow.CreateRoutingTask Task.Create Directory.CreateEntries Document.Delete Document.DeleteRelation Directory.DeleteEntries WebUI.DestroySeamContext Repository.GetDocument Document.Export WebUI.DownloadFile Blob.ExportToFS Document.FetchByProperty Blob.CreateFromURL FileManager.ImportInSeam FileManager.ImportWithMetaData FileManager.ImportWithMetaDataInSeam Document.Filter Document.FollowLifecycleTransition Comment.Moderate Document.GetBlobs Document.GetChild Document.GetChildren Document.GetBlob Document.GetBlobsByProperty User.GetUserWorkspace Document.GetLinkedDocuments Proxy.GetSourceDocument User.Get Document.GetParent Context.GetEmailsWithPermissionOnDoc Context.GetTaskNames Context.GetUsersGroupIdsWithPermissionOnDoc Document.GetVersions Directory.Projection Collection.Suggestion User.GetCollections Directory.Entries Directory.SuggestEntries Collection.GetDocumentsFromCollection Favorite.GetDocuments Document.Routing.GetGraph Picture.GetView Workflow.GetOpenTasks Tag.Suggestion Task.GetAssigned UserGroup.Suggestion Document.GetRendition Blob.PostToURL Image.Blob.Resize WebUI.InitSeamContext JsonStack.ToggleDisplay Actions.GET GetRepositories Document.Lock Log Audit.LogEvent Auth.LoginAs Auth.Logout Document.Move Document.PublishToSections NRD-AC-PR-ChooseParticipants-Output NRD-AC-PR-LockDocument NRD-AC-PR-UnlockDocument NRD-AC-PR-ValidateNode-Output NRD-AC-PR-force-validate NRD-AC-PR-storeTaskInfo WebUI.NavigateTo NuxeoDrive.SetActiveFactories NuxeoDrive.AddToLocallyEditedCollection NuxeoDrive.AttachBlob NuxeoDrive.CanMove NuxeoDrive.CreateFile NuxeoDrive.CreateFolder NuxeoDrive.CreateTestDocuments NuxeoDrive.Delete NuxeoDrive.FileSystemItemExists NuxeoDrive.GenerateConflictedItemName NuxeoDrive.GetRoots NuxeoDrive.GetChangeSummary NuxeoDrive.GetChildren NuxeoDrive.GetClientUpdateInfo NuxeoDrive.GetFileSystemItem NuxeoDrive.GetTopLevelFolder NuxeoDrive.GetTopLevelChildren NuxeoDrive.Move NuxeoDrive.SetSynchronization NuxeoDrive.Rename NuxeoDrive.SetVersioningOptions NuxeoDrive.SetupIntegrationTests NuxeoDrive.TearDownIntegrationTests NuxeoDrive.UpdateFile NuxeoDrive.WaitForElasticsearchCompletion NuxeoDrive.WaitForAsyncCompletion Repository.PageProvider Context.PopDocument Context.PopDocumentList Context.PopBlob Context.PopBlobList Document.PublishToSection Context.PullDocument Context.PullDocumentList Context.PullBlob Context.PullBlobList Context.PushDocument Context.PushDocumentList Context.PushBlob Context.PushBlobList WebUI.AddToClipboard WebUI.PushDocumentToSeamContext WebUI.AddToWorklist LocalConfiguration.PutSimpleConfigurationParameters LocalConfiguration.PutSimpleConfigurationParameter Repository.Query Audit.Query Repository.ResultSetPageProvider WebUI.RaiseSeamEvents Blob.ReadMetadata Context.SetMetadataFromBlob Directory.ReadEntries WebUI.Refresh WebUI.Refresh Document.RemoveACL Services.RemoveDocumentTags Document.RemoveEntryOfMultivaluedProperty Blob.RemoveFromDocument Document.RemovePermission Document.RemoveProperty Collection.RemoveFromCollection Render.Document Render.DocumentFeed TemplateProcessor.Render Document.ReplacePermission Document.Reload Picture.Resize Context.RestoreDocumentInput Context.RestoreDocumentsInput Context.RestoreBlobInput Context.RestoreBlobsInput Document.RestoreVersion Context.RestoreBlobInputFromScript Context.RestoreBlobsInputFromScript Context.RestoreDocumentInputFromScript Context.RestoreDocumentsInputFromScript Repository.ResultSetQuery Document.Routing.Resume.Step Workflow.ResumeNode Counters.GET RunOperation RunDocumentOperation Context.RunDocumentOperationInNewTx RunFileOperation RunOperationOnList RunOperationOnProvider RunOperationOnListInNewTx RunInputScript RunScript WebUI.RunOperationInSeam Document.Save Seam.SaveDocumentInUI Repository.SaveSession SeamActions.GET Document.Mail Event.Fire Document.AddACE Context.SetVar Context.SetInputAsVar LocalConfiguration.SetSimpleConfigurationParameterAsVar Document.Routing.SetRunningStepFromTask Document.SetBlob Document.SetBlobName WebUI.SetJSFOutcome Workflow.SetNodeVariable Document.Routing.Step.Done Document.Routing.BackToReady Document.Routing.EvaluateCondition Context.SetWorkflowVar WebUI.ShowCreateForm Document.CreateVersion Context.StartWorkflow Search.SuggestersLauncher Services.TagDocument Traces.Get Traces.ToggleRecording Document.SetMetadataFromBlob Seam.GetChangeableDocument Seam.FetchFromClipboard Seam.GetCurrentDocument Seam.GetCurrentDomain Seam.GetCurrentWorkspace Seam.FetchDocument Seam.GetSelectedDocuments Seam.GetDocumentsFromSelectionList Seam.FetchFromWorklist Document.Unlock Document.UnblockPermissionInheritance Services.UntagDocument Document.Update Document.SetProperty Document.Routing.UpdateCommentsInfoOnDocument Directory.UpdateEntries Workflow.UserTaskPageProvider VersionAndAttachFile VersionAndAttachFiles Blob.SetMetadataFromDocument Blob.SetMetadataFromContext Blob.CreateZip acceptComment addCurrentDocumentToWorklist blobToPDF cancelWorkflow conditionalTask decideNextStepAndSimpleValidate downloadFilesZip evaluateCondition followLifeCycleTransition followLifeCycleTransitionTask initInitiatorComment logInAudit nextAssignee notifyInitiatorEndOfWorkflow publishDocument publishTask reinitAssigneeComment rejectComment Workflow.RemoveRoutingTask sendTaskCreatedNotificationMail setDone setNextStep setTaskDone simpleChooseNextOption1AndDone simpleChooseNextOption2AndDone simpleRefuse simpleTask simpleUndo simpleValidate terminateWorkflow undoRunningTask updateCommentsOnDoc validateDocument voidChain xmlExportRendition zipTreeExportRendition
Favorite.GetDocuments
Blob.ToPDF
Image.Blob.Resize
Document.AddRelation
Workflow.CreateRoutingTask
lot of contributed operations
Commands as REST resources
GET to retrieve definition
POST to execute
GET /nuxeo/api/v1/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, },
... ]
}
POST /nuxeo/api/v1/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",
...
}, { ...}, ...
]
}
> cat /doc/path/somedoc | command(p3,p4)
POST /nuxeo/api/v1/path/somePath/@op/Blob.ToPDF HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/pdf
...
assemble API blocks without having to code
build business API
Expose the API that matches client needs
Server side assembly
One Context
better control of the flow
What about the trade-off ?
Introspection is a requirement
Provide a Playground to test
http://nuxeo.github.io/api-playground/
Introspect Data Structures
http://nuxeo.github.io/api-playground/
Introspect Resources
http://nuxeo.github.io/api-playground/
Introspect Command API
Using introspection from Mule ESB DataSense
Dynamic API more complex to use !?
client libraries
Thank You !
http://www.nuxeo.com/careers/